Merge origin/main (license, compose, #65 leak guard) into cans work
CI / test (push) Failing after 6s
CI / docker (push) Skipped

This commit is contained in:
2026-09-09 16:39:14 -05:00
22 changed files with 2293 additions and 18 deletions
+22
View File
@@ -328,3 +328,25 @@ func (h *Handlers) HandleAdminPage(w http.ResponseWriter, r *http.Request) {
// Handlers builds a web.Handlers bound to this UI.
func (u *UI) Handlers() *Handlers { return &Handlers{UI: u} }
// #59: security headers for rendered HTML pages. Applied wherever the
// response is text/html (page templates and the inline can page); JSON API
// responses and /raw content pass through untouched. script-src allows
// 'unsafe-inline' because the page templates carry inline scripts; CSP
// default-src 'self' still blocks external content and object/frame embeds,
// and frame-ancestors 'none' closes the clickjacking gap flagged in the #34
// pentest. Runs after the handler so the Content-Type is already set.
func SecurityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set before the handler runs: once a handler writes (template render
// flushes), header mutations are silently dropped. Setting the headers
// unconditionally is safe: CSP/nosniff/referrer on JSON or /raw bodies
// is harmless and arguably desirable.
h := w.Header()
h.Set("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; frame-ancestors 'none'")
h.Set("Referrer-Policy", "no-referrer")
h.Set("X-Content-Type-Options", "nosniff")
next.ServeHTTP(w, r)
})
}