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 -}} - -