release / govulncheck (push) Successful in 15s
release / web (push) Failing after 17s
release / go (push) Successful in 1m13s
release / integration (push) Successful in 4m56s
release / android (push) Successful in 5m9s
release / Build signed APK (releases and dev) (push) Successful in 5m19s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
Migration 0091 drops lidarr_config and renames the quarantine tables, types, indexes and the history column off the Lidarr name. The Integrations Lidarr card, the Reports "Delete via Lidarr" action, the merge and remove-track unmonitor options, the Lidarr audit actions and error codes go, on the server, web and Android. The README describes acquisition as it is now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.1 KiB
Go
58 lines
2.1 KiB
Go
package web
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// inlineScriptRe matches each <script> element and captures its attributes
|
|
// and body. index.html is our own build output, so a regexp is enough: there
|
|
// is no hostile markup to out-parse.
|
|
var inlineScriptRe = regexp.MustCompile(`(?is)<script\b([^>]*)>(.*?)</script>`)
|
|
|
|
var srcAttrRe = regexp.MustCompile(`(?i)\bsrc\s*=`)
|
|
|
|
// contentSecurityPolicy builds the policy served with index.html.
|
|
//
|
|
// Scripts are allowed from our own origin plus the exact inline scripts the
|
|
// page carries: the theme bootstrap in app.html, the branding global the Vite
|
|
// plugin injects, and SvelteKit's start-up block. Their hashes are taken from
|
|
// the page AFTER the branding template has run, so they match the bytes the
|
|
// browser actually receives, whatever the operator's app name. Any script
|
|
// that differs, including one injected through an XSS, is refused.
|
|
//
|
|
// The rest:
|
|
// - style-src allows inline styles: Svelte transitions and style:
|
|
// directives write them, and inline style is not a script vector.
|
|
// - img-src admits https:/http: because catalogue search art is a remote
|
|
// URL. Images cannot run code.
|
|
// - media-src/connect-src stay on our own origin: streams, the API and the
|
|
// SSE stream are all same-origin. blob: covers Web Audio and object URLs.
|
|
func contentSecurityPolicy(indexHTML []byte) string {
|
|
scriptSrc := []string{"'self'"}
|
|
for _, m := range inlineScriptRe.FindAllSubmatch(indexHTML, -1) {
|
|
if srcAttrRe.Match(m[1]) {
|
|
continue
|
|
}
|
|
sum := sha256.Sum256(m[2])
|
|
scriptSrc = append(scriptSrc, "'sha256-"+base64.StdEncoding.EncodeToString(sum[:])+"'")
|
|
}
|
|
return strings.Join([]string{
|
|
"default-src 'self'",
|
|
"base-uri 'self'",
|
|
"object-src 'none'",
|
|
"frame-ancestors 'none'",
|
|
"form-action 'self'",
|
|
"script-src " + strings.Join(scriptSrc, " "),
|
|
"style-src 'self' 'unsafe-inline'",
|
|
"img-src 'self' data: blob: https: http:",
|
|
"font-src 'self' data:",
|
|
"media-src 'self' blob:",
|
|
"connect-src 'self'",
|
|
"worker-src 'self' blob:",
|
|
"manifest-src 'self'",
|
|
}, "; ")
|
|
}
|