diff --git a/internal/config/config_test.go b/internal/config/config_test.go index a149d7f0400..fa7e6dcd73c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1931,3 +1931,21 @@ func TestParseConfigDumpOutput(t *testing.T) { t.Fatal(err) } } + +func TestContentSecurityPolicy(t *testing.T) { + os.Clearenv() + os.Setenv("CONTENT_SECURITY_POLICY", "fonts.googleapis.com fonts.gstatic.com") + + parser := NewParser() + opts, err := parser.ParseEnvironmentVariables() + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + expected := "fonts.googleapis.com fonts.gstatic.com" + result := opts.ContentSecurityPolicy() + + if result != expected { + t.Fatalf(`Unexpected CONTENT_SECURITY_POLICY value, got %v instead of %v`, result, expected) + } +} diff --git a/internal/config/options.go b/internal/config/options.go index abbaf84934e..30da1e98b4f 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -83,6 +83,7 @@ const ( defaultWatchdog = true defaultInvidiousInstance = "yewtu.be" defaultWebAuthn = false + defaultContentSecurityPolicy = "" ) var defaultHTTPClientUserAgent = "Mozilla/5.0 (compatible; Miniflux/" + version.Version + "; +https://miniflux.app)" @@ -165,6 +166,7 @@ type Options struct { invidiousInstance string proxyPrivateKey []byte webAuthn bool + contentSecurityPolicy string } // NewOptions returns Options with default values. @@ -241,6 +243,7 @@ func NewOptions() *Options { invidiousInstance: defaultInvidiousInstance, proxyPrivateKey: randomKey, webAuthn: defaultWebAuthn, + contentSecurityPolicy: defaultContentSecurityPolicy, } } @@ -607,6 +610,11 @@ func (o *Options) WebAuthn() bool { return o.webAuthn } +// ContentSecurityPolicy returns value for Content-Security-Policy meta tag. +func (o *Options) ContentSecurityPolicy() string { + return o.contentSecurityPolicy +} + // SortedOptions returns options as a list of key value pairs, sorted by keys. func (o *Options) SortedOptions(redactSecret bool) []*Option { var keyValues = map[string]interface{}{ @@ -682,6 +690,7 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option { "WORKER_POOL_SIZE": o.workerPoolSize, "YOUTUBE_EMBED_URL_OVERRIDE": o.youTubeEmbedUrlOverride, "WEBAUTHN": o.webAuthn, + "CONTENT_SECURITY_POLICY": o.contentSecurityPolicy, } keys := make([]string, 0, len(keyValues)) diff --git a/internal/config/parser.go b/internal/config/parser.go index d64d67c7fb4..b515303950e 100644 --- a/internal/config/parser.go +++ b/internal/config/parser.go @@ -248,6 +248,8 @@ func (p *Parser) parseLines(lines []string) (err error) { p.opts.proxyPrivateKey = parseBytes(value, randomKey) case "WEBAUTHN": p.opts.webAuthn = parseBool(value, defaultWebAuthn) + case "CONTENT_SECURITY_POLICY": + p.opts.contentSecurityPolicy = parseString(value, defaultContentSecurityPolicy) } } diff --git a/internal/template/templates/common/layout.html b/internal/template/templates/common/layout.html index 3076ec18dd4..e490950719b 100644 --- a/internal/template/templates/common/layout.html +++ b/internal/template/templates/common/layout.html @@ -36,7 +36,7 @@ {{ if and .user .user.Stylesheet }} {{ $stylesheetNonce := nonce }} - + {{ else }} diff --git a/internal/ui/view/view.go b/internal/ui/view/view.go index 077340b5ae3..3818b0ee24b 100644 --- a/internal/ui/view/view.go +++ b/internal/ui/view/view.go @@ -46,5 +46,6 @@ func New(tpl *template.Engine, r *http.Request, sess *session.Session) *View { b.params["sw_js_checksum"] = static.JavascriptBundleChecksums["service-worker"] b.params["webauthn_js_checksum"] = static.JavascriptBundleChecksums["webauthn"] b.params["webAuthnEnabled"] = config.Opts.WebAuthn() + b.params["contentSecurityPolicy"] = config.Opts.ContentSecurityPolicy() return b } diff --git a/miniflux.1 b/miniflux.1 index 7bf9ebd154f..c5f36178d06 100644 --- a/miniflux.1 +++ b/miniflux.1 @@ -520,6 +520,11 @@ Default is randomly generated at startup\&. Enable or disable WebAuthn/Passkey authentication\&. .br Default is disabled\&. +.B CONTENT_SECURITY_POLICY +Set custom domain list for Content-Security-Policy meta tag\&. +.br +Default is empty\&. +.TP .SH AUTHORS .P