feat(web): add sandbox CSP builder library (closes #1558)#1588
Conversation
|
@claude review |
|
Claude finished @cliffhall's task in 2m 31s —— View job Review: sandbox CSP builder libraryNice, self-contained PR. The library is pure, well-documented, and the tests genuinely exercise the security-relevant behavior (injection rejection, escaping, wrapper ordering, the 1.
|
Address round-1 @claude review of #1588 (no behavior change to the enforced policy): - approveCspSources / buildSandboxCspPolicy: document that resourceDomains intentionally feeds script-src/style-src per the McpUiResourceCsp contract, and that approve screens injection-safety only (not breadth — a bare `*` is accepted; safe under the opaque-origin sandbox). - CSP_KEYS: replace the `satisfies` (proves listed keys valid) with an exhaustiveCspKeys() helper that ALSO fails to compile if the upstream ext-apps type gains a domain key CSP_KEYS omits, so a requested restriction can never be silently dropped. - SAFE_CSP_SOURCE: accept case-insensitive schemes (URL schemes are case-insensitive); previously over-rejected `HTTPS://…`. Adds tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
|
Thanks for the review — addressed in cb54de5. 1. 2. 3. Case-sensitive scheme — Fixed. 4. CSP No behavior change to the enforced policy string. |
|
@claude review |
|
Claude finished @cliffhall's task in 1m 25s —— View job Re-review: fixes in
|
|
📋 Smoke test & AGENTS.md audit for this PR: #1558 (comment) (posted on the linked issue #1558). |
…ase change; tree unchanged)
Closes #1558
Wave 1 of the PR #1510 decomposition (tracking issue #1579). Adds a pure, DOM-free CSP-builder library at
clients/web/src/lib/sandbox-csp.tsthat the MCP Apps sandbox will use to enforce app-supplied_meta.ui.csprequests. Wiring into the app bridge is a follow-up issue — this PR is the standalone, unit-testable library only.What it does
SAFE_CSP_SOURCE— strict regex accepting only common source forms (scheme://host[:port][/path], scheme-only likedata:/blob:,*, wildcard hosts). Rejects anything that could inject a directive (;) or break out of the meta attribute (",<,>, whitespace).approveCspSources()— filters an untrustedMcpUiResourceCspdown to enforceable entries, dropping unsafe values with aconsole.warnand omitting keys with no accepted source. Returns what the host will echo back to the view (granted, not requested).buildSandboxCspPolicy()— translates the approved CSP into the enforced policy string:default-src 'none'catch-all,form-action/object-src/worker-src 'none', per-directive source allowlists,'unsafe-inline'for the app's own inline script/style.escapeHtmlAttr()/wrapSandboxedHtml()— defense-in-depth wrapper so the CSP<meta>is always the literal first<head>child and untrusted bytes never precede the applied policy.Testing
sandbox-csp.test.ts) covering defaults, source filtering, escaping, and the wrapper ordering guarantees.sandbox-csp.ts.includelist (by filename, so the untested legacysrc/libsiblings are not pulled under the gate) so it is genuinely gated at ≥90.npm run validate(web) andnpm run test:coverage(web) both pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX