diff --git a/internal/template/functions.go b/internal/template/functions.go
index 2a478cc5..68c5b9a8 100644
--- a/internal/template/functions.go
+++ b/internal/template/functions.go
@@ -33,6 +33,7 @@ type funcMap struct {
func (f *funcMap) Map() template.FuncMap {
return template.FuncMap{
"contains": strings.Contains,
+ "csp": csp,
"startsWith": strings.HasPrefix,
"formatFileSize": formatFileSize,
"dict": dict,
@@ -116,6 +117,42 @@ func (f *funcMap) Map() template.FuncMap {
}
}
+func csp(user *model.User, nonce string) string {
+ order := [...]string{"default-src", "img-src", "media-src", "frame-src", "style-src", "script-src", "font-src", "require-trusted-types-for", "trusted-types"}
+ policies := map[string]string{
+ "default-src": "'none'",
+ "img-src": "* data:",
+ "media-src": "*",
+ "frame-src": "*",
+ "style-src": "'nonce-" + nonce + "'",
+ "script-src": "'nonce-" + nonce + "' 'strict-dynamic'",
+ "require-trusted-types-for": "'script'",
+ "trusted-types": "html url",
+ }
+
+ if user != nil {
+ if user.ExternalFontHosts != "" {
+ policies["font-src"] = user.ExternalFontHosts
+ if user.Stylesheet != "" {
+ policies["style-src"] += " " + user.ExternalFontHosts
+ }
+ }
+ }
+
+ var policy strings.Builder
+ // This is needed to always have the same order.
+ for _, key := range order {
+ if value, ok := policies[key]; ok {
+ policy.WriteString(key)
+ policy.WriteString(" ")
+ policy.WriteString(value)
+ policy.WriteString("; ")
+ }
+ }
+
+ return ``
+}
+
func dict(values ...any) (map[string]any, error) {
if len(values)%2 != 0 {
return nil, fmt.Errorf("dict expects an even number of arguments")
diff --git a/internal/template/functions_test.go b/internal/template/functions_test.go
index dd71f28f..9c8c8888 100644
--- a/internal/template/functions_test.go
+++ b/internal/template/functions_test.go
@@ -8,6 +8,7 @@ import (
"time"
"miniflux.app/v2/internal/locale"
+ "miniflux.app/v2/internal/model"
)
func TestDict(t *testing.T) {
@@ -159,3 +160,11 @@ func TestFormatFileSize(t *testing.T) {
}
}
}
+
+func TestCSP(t *testing.T) {
+ want := ``
+ got := csp(&model.User{ExternalFontHosts: "test.com"}, "1234")
+ if got != want {
+ t.Errorf(`Unexpected result, got %q instead of %q`, got, want)
+ }
+}
diff --git a/internal/template/templates/common/layout.html b/internal/template/templates/common/layout.html
index 8c4f069d..ca3529c0 100644
--- a/internal/template/templates/common/layout.html
+++ b/internal/template/templates/common/layout.html
@@ -25,24 +25,18 @@
-
-
- {{ if .user }}
- {{ $cspNonce := nonce }}
-
-
+ {{ $cspNonce := nonce }}
+ {{ csp .user $cspNonce | safeHTML }}
+
+
+ {{ if .user -}}
{{ if .user.Stylesheet -}}
{{ end -}}
-
{{ if .user.CustomJS -}}
{{ end -}}
- {{ else -}}
-
{{ end -}}
-
-