CSP allows unsafe-inline scripts and styles and the README never says why #530

Open
opened 2026-10-13 11:32:25 +02:00 by clawbot · 2 comments
Collaborator

Severity: low

What is wrong. SecurityHeaders in internal/middleware/middleware.go sends Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'. REPO_POLICIES.md ("HTTP/web services must be hardened for production internet exposure") says unsafe-inline is used only when unavoidable and the reason is documented. The README's Security and Middleware Stack sections list the CSP but never mention unsafe-inline, while "Third-party browser assets" presents the CSP build of Alpine.js as chosen because "the pages' Content-Security-Policy forbids eval". What needs unsafe-inline today is three inline onsubmit="return confirm(…)" handlers in templates/source_detail.html and a few style="" attributes (templates/event_body.html, settings.html, source_detail.html). internal/handlers/event_body.go already notes that the policy therefore cannot protect a document served from this origin.

How it was observed. Every response from the tag's image carries the policy above.

Why it matters. With 'unsafe-inline' in script-src, the CSP offers no protection against an injected inline script, which is the main thing a CSP is for on an admin UI that renders payloads chosen by anyone who can reach the receiver. html/template escapes them, so no hole is known: this is defence in depth and a policy gap, not an exploit.

What acceptable looks like. Move the three confirm handlers into static/js/app.js (as Alpine CSP components, or listeners bound by a data- attribute) and the inline styles into static/css/style.css, then drop 'unsafe-inline' from script-src, and from style-src if nothing needs it; make test-browser already checks the pages under the real policy. If any unsafe-inline must stay, the README's Security section says which and why, as the policy asks.

Definition of done. Either script-src 'self' with the browser test green, or the README states the remaining inline uses and the reason for each.

Model: fable-5-1

Severity: low **What is wrong.** `SecurityHeaders` in `internal/middleware/middleware.go` sends `Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'`. `REPO_POLICIES.md` ("HTTP/web services must be hardened for production internet exposure") says `unsafe-inline` is used only when unavoidable and the reason is documented. The README's Security and Middleware Stack sections list the CSP but never mention `unsafe-inline`, while "Third-party browser assets" presents the CSP build of Alpine.js as chosen because "the pages' Content-Security-Policy forbids eval". What needs `unsafe-inline` today is three inline `onsubmit="return confirm(…)"` handlers in `templates/source_detail.html` and a few `style=""` attributes (`templates/event_body.html`, `settings.html`, `source_detail.html`). `internal/handlers/event_body.go` already notes that the policy therefore cannot protect a document served from this origin. **How it was observed.** Every response from the tag's image carries the policy above. **Why it matters.** With `'unsafe-inline'` in `script-src`, the CSP offers no protection against an injected inline script, which is the main thing a CSP is for on an admin UI that renders payloads chosen by anyone who can reach the receiver. `html/template` escapes them, so no hole is known: this is defence in depth and a policy gap, not an exploit. **What acceptable looks like.** Move the three confirm handlers into `static/js/app.js` (as Alpine CSP components, or listeners bound by a `data-` attribute) and the inline styles into `static/css/style.css`, then drop `'unsafe-inline'` from `script-src`, and from `style-src` if nothing needs it; `make test-browser` already checks the pages under the real policy. If any `unsafe-inline` must stay, the README's Security section says which and why, as the policy asks. **Definition of done.** Either `script-src 'self'` with the browser test green, or the README states the remaining inline uses and the reason for each. Model: fable-5-1
clawbot self-assigned this 2026-10-13 11:32:25 +02:00
Author
Collaborator

Plan: remove the inline script and styles and drop 'unsafe-inline' from both script-src and style-src.

  • The three onsubmit="return confirm(…)" handlers in templates/source_detail.html move to static/js/app.js, bound by a data-confirm attribute carrying the message (one listener, not three components).
  • The style="" attributes in templates/event_body.html, settings.html and source_detail.html, and the <style> block for [x-cloak] in templates/htmlheader.html, move to static/css/style.css as classes.
  • SecurityHeaders in internal/middleware/middleware.go sends script-src 'self'; style-src 'self'. Update the note in internal/handlers/event_body.go that says the policy cannot protect a document from this origin, and the README Security and Middleware Stack text so they state the policy as sent.
  • make test-browser checks every page under the real policy; extend it if it does not already catch a CSP violation from an inline style or handler. If any inline use proves unavoidable, it stays and the README Security section names it and why.

Model: opus-5-5

Plan: remove the inline script and styles and drop `'unsafe-inline'` from both `script-src` and `style-src`. - The three `onsubmit="return confirm(…)"` handlers in `templates/source_detail.html` move to `static/js/app.js`, bound by a `data-confirm` attribute carrying the message (one listener, not three components). - The `style=""` attributes in `templates/event_body.html`, `settings.html` and `source_detail.html`, and the `<style>` block for `[x-cloak]` in `templates/htmlheader.html`, move to `static/css/style.css` as classes. - `SecurityHeaders` in `internal/middleware/middleware.go` sends `script-src 'self'; style-src 'self'`. Update the note in `internal/handlers/event_body.go` that says the policy cannot protect a document from this origin, and the README Security and Middleware Stack text so they state the policy as sent. - `make test-browser` checks every page under the real policy; extend it if it does not already catch a CSP violation from an inline style or handler. If any inline use proves unavoidable, it stays and the README Security section names it and why. Model: opus-5-5
Author
Collaborator

Done in #537. No page has an inline script, event handler or style any more, so the policy is now default-src 'self'; script-src 'self'; style-src 'self'. The PR names one change from the plan: the inline styles became Tailwind classes in the templates, not new classes in static/css/style.css.

Model: opus-5-5

Done in https://git.eeqj.de/sneak/webhooker/pulls/537. No page has an inline script, event handler or style any more, so the policy is now `default-src 'self'; script-src 'self'; style-src 'self'`. The PR names one change from the plan: the inline styles became Tailwind classes in the templates, not new classes in `static/css/style.css`. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#530