Skip to content

Commit

Permalink
Add CONTENT_SECURITY_POLICY
Browse files Browse the repository at this point in the history
  • Loading branch information
kl177 committed Dec 15, 2023
1 parent 7990edd commit 467ec08
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 1 deletion.
18 changes: 18 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
9 changes: 9 additions & 0 deletions internal/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -165,6 +166,7 @@ type Options struct {
invidiousInstance string
proxyPrivateKey []byte
webAuthn bool
contentSecurityPolicy string
}

// NewOptions returns Options with default values.
Expand Down Expand Up @@ -241,6 +243,7 @@ func NewOptions() *Options {
invidiousInstance: defaultInvidiousInstance,
proxyPrivateKey: randomKey,
webAuthn: defaultWebAuthn,
contentSecurityPolicy: defaultContentSecurityPolicy,
}
}

Expand Down Expand Up @@ -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{}{
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions internal/config/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/template/templates/common/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

{{ if and .user .user.Stylesheet }}
{{ $stylesheetNonce := nonce }}
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; style-src 'self' 'nonce-{{ $stylesheetNonce }}'">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' {{ .contentSecurityPolicy }}; img-src * data:; media-src *; frame-src *; style-src 'self' {{ .contentSecurityPolicy }} 'nonce-{{ $stylesheetNonce }}'">
<style nonce="{{ $stylesheetNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
{{ else }}
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *">
Expand Down
1 change: 1 addition & 0 deletions internal/ui/view/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
5 changes: 5 additions & 0 deletions miniflux.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 467ec08

Please sign in to comment.