Skip to content

fix(auth): add a Content-Security-Policy to every Explorer page - #21

Merged
jzhao234 merged 1 commit into
mainfrom
fix/content-security-policy
Sep 14, 2026
Merged

jzhao234 merged 1 commit into
mainfrom
fix/content-security-policy

Conversation

@jzhao234

Copy link
Copy Markdown
Contributor

TLDR

Every Explorer page ships a Content-Security-Policy: scripts only from this origin plus the page's nonced inline blocks, styles only from this origin, images from this origin plus https: and data: (sanitized email bodies keep <img src>), frame-ancestors 'none', object-src 'none', base-uri 'none'. Non-HTML responses get default-src 'none'.

Why the policy can be strict on styles

The templates have no inline styles and s3_email.py strips style attributes and <style> blocks from email bodies before rendering, so style-src 'self' breaks nothing. Scripts: four inline blocks, all nonced; flatpickr loads from /static.

Deliberate omission

No form-action: Elcano-mode logout is a form POST that redirects to the auth host, and browsers apply form-action to that redirect.

Tests

Inbox page CSP nonce matches its inline scripts; no bare <script>, inline handlers, or style attributes; nonce rotates per response; /health gets the closed policy. Full suite, ruff check, ruff format green.

TL;DR: Explorer pages now ship a Content-Security-Policy that lets only this origin's scripts plus the page's own nonced inline blocks run, allows styles only from this origin, and permits remote images because email bodies reference them. An injected script or event handler cannot execute even if some markup slips through.

Problem:
Explorer set X-Content-Type-Options, X-Frame-Options and Referrer-Policy but no CSP. It renders sanitized email bodies inline and holds a session cookie, which is exactly the page you want a script policy on. Auth got a nonce-based CSP in its first release; the applications did not.

Fix:
- security_headers middleware generates a per-response nonce, sets a page policy on text/html responses and a closed default-src 'none' policy on everything else.
- Page policy: default-src 'self'; script-src 'self' plus the nonce; style-src 'self'; img-src 'self' data: https:; font-src and connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'.
- The four inline <script> blocks (theme bootstrap and loading bar in base.html, inbox behaviour, detail behaviour) carry the nonce. The flatpickr vendor script loads from /static and needs none.
- style-src is strict because the templates have no inline styles and the email sanitizer strips style attributes and <style> blocks; img-src allows https: and data: because sanitized email bodies keep <img src> over https, data and cid (cid never resolves).
- No form-action: Elcano-mode logout is a form POST that redirects to the auth host, and browsers apply form-action to that redirect.

Tests:
- New: the inbox page's CSP names a nonce matching its inline scripts, the page has no bare <script>, inline handlers or style attributes, the nonce differs per response, and /health gets the closed policy.
- Ran: pytest -q (full suite), ruff check, ruff format --check.
@jzhao234
jzhao234 merged commit fcc4f8e into main Sep 14, 2026
3 checks passed
@jzhao234
jzhao234 deleted the fix/content-security-policy branch September 14, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant