Skip to content

Commit c3ef33b

Browse files
benvinegarclaude
andcommitted
docs: document the surface-isolation security model and CSP usage
The Architecture invariants in AGENTS.md (the agent dev guide) had the core "agent content only renders in a sandboxed iframe" rule but predated this security pass. Expand it to capture what we now rely on: - the /s/:id `Content-Security-Policy: sandbox allow-scripts` response header, which keeps a top-level load opaque-origin (the iframe attribute only covers the embedded case); - how we use CSP — buildCsp (html parts: CDN allowlist, connect-src is CDNs only) vs buildRichCsp (rich parts: no connect-src at all), and that the resource policy is independent of the sandbox that makes the origin opaque; - the bridge trust model — author:"user" is reserved for the composer (sendPrompt posts "surface"; MCP reply_to_user coerces "user"→"agent"), openLink is re-validated host-side, and every request body is capped. No runtime change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3752061 commit c3ef33b

2 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
---
3+
4+
Docs only: document the surface-isolation security model and CSP usage in the
5+
agent dev guide (AGENTS.md). No runtime change.

AGENTS.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,23 @@ consciously, not as a side effect):
9999
happen at an opaque origin. When you add a part kind, pick (a) or (b); never a
100100
third way. The iframes are sandboxed without `allow-same-origin` (opaque
101101
origin) and `connect-src`-free for rich parts (no exfil even if contained
102-
script runs); never weaken this.
102+
script runs); never weaken this. `/s/:id` is same-origin, so the iframe
103+
`sandbox` attribute only covers embedding — the route also sends a
104+
`Content-Security-Policy: sandbox allow-scripts` **header** (a meta tag can't
105+
carry `sandbox`) so a top-level load stays opaque too; never add
106+
`allow-same-origin`.
107+
- **CSP layers.** `buildCsp` (html parts): `default-src 'none'`, CDN allowlist,
108+
`connect-src` = those CDNs only (no board API); origin named in
109+
`img-src`/`media-src` since `'self'` is nothing at an opaque origin.
110+
`buildRichCsp` (rich parts, `renderSandboxedPart`): bridge-only inline script,
111+
no `connect-src`. CSP limits _resources_; the sandbox makes the _origin_
112+
opaque — keep both.
113+
- **Surface→host bridge — gate every crossing.** `author:"user"` is reserved
114+
for the composer: `sendPrompt` posts `author:"surface"` (never reaches the
115+
agent), MCP `reply_to_user` coerces `"user"``"agent"`. `openLink` is
116+
re-validated host-side (`http(s)` only). Request bodies are capped
117+
(`bodyLimit`). A new host-affecting message or write endpoint gets gated
118+
likewise.
103119
- WebKit quirk in sandboxed iframes: ResizeObserver's initial callback may not
104120
fire and `documentElement.scrollHeight` ratchets to viewport height — the
105121
bridge reports `body.scrollHeight` on `load` plus staggered timers. Don't

0 commit comments

Comments
 (0)