mirror of
https://github.com/miniflux/v2.git
synced 2025-09-15 18:57:04 +00:00
Merge 65b86b1bcc
into 87e65f800e
This commit is contained in:
commit
ef564b39c9
3 changed files with 51 additions and 11 deletions
|
@ -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 `<meta http-equiv="Content-Security-Policy" content="` + policy.String() + `">`
|
||||
}
|
||||
|
||||
func dict(values ...any) (map[string]any, error) {
|
||||
if len(values)%2 != 0 {
|
||||
return nil, fmt.Errorf("dict expects an even number of arguments")
|
||||
|
|
|
@ -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 := `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src * data:; media-src *; frame-src *; style-src 'nonce-1234'; script-src 'nonce-1234' 'strict-dynamic'; font-src test.com; require-trusted-types-for 'script'; trusted-types html url; ">`
|
||||
got := csp(&model.User{ExternalFontHosts: "test.com"}, "1234")
|
||||
if got != want {
|
||||
t.Errorf(`Unexpected result, got %q instead of %q`, got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,24 +25,18 @@
|
|||
<link rel="apple-touch-icon" sizes="167x167" href="{{ route "appIcon" "filename" "icon-167.png" }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ route "appIcon" "filename" "icon-180.png" }}">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{{ route "stylesheet" "name" .theme "checksum" .theme_checksum }}">
|
||||
|
||||
{{ if .user }}
|
||||
{{ $cspNonce := nonce }}
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; {{ if .user.ExternalFontHosts }}font-src {{ .user.ExternalFontHosts }}; {{ end }}style-src 'self'{{ if .user.Stylesheet }}{{ if .user.ExternalFontHosts }} {{ .user.ExternalFontHosts }}{{ end }} 'nonce-{{ $cspNonce }}'{{ end }}{{ if .user.CustomJS }}; script-src 'self' 'nonce-{{ $cspNonce }}'{{ end }}; require-trusted-types-for 'script'; trusted-types html url;">
|
||||
|
||||
{{ csp .user $cspNonce | safeHTML }}
|
||||
<link rel="stylesheet" nonce="{{ $cspNonce }}" type="text/css" href="{{ route "stylesheet" "name" .theme "checksum" .theme_checksum }}">
|
||||
<script nonce="{{ $cspNonce }}" src="{{ route "javascript" "name" "app" "checksum" .app_js_checksum }}" type="module"></script>
|
||||
{{ if .user -}}
|
||||
{{ if .user.Stylesheet -}}
|
||||
<style nonce="{{ $cspNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
|
||||
{{ end -}}
|
||||
|
||||
{{ if .user.CustomJS -}}
|
||||
<script type="module" nonce="{{ $cspNonce }}">{{ .user.CustomJS | safeJS }}</script>
|
||||
{{ end -}}
|
||||
{{ else -}}
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; require-trusted-types-for 'script'; trusted-types html url;">
|
||||
{{ end -}}
|
||||
|
||||
<script src="{{ route "javascript" "name" "app" "checksum" .app_js_checksum }}" type="module"></script>
|
||||
</head>
|
||||
<body
|
||||
data-service-worker-url="{{ route "javascript" "name" "service-worker" "checksum" .sw_js_checksum }}"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue