Skip to content

Conversation

@ochafik
Copy link
Collaborator

@ochafik ochafik commented Jan 11, 2026

This PR tightens CSP handling in basic-host (which was setting very loose permissions so far).

Changes

  • CSP via HTTP headers instead of meta tags - Server sets CSP headers based on query params, guest content cannot modify them
  • Added frameDomains, baseUriDomains (borrowed from feat: enhance sandbox capability negotiation #158, cc/ @idosal @domfarolino) - Extend CSP configuration options
  • Added worker-src directive - Needed for WebGL apps (CesiumJS, Three.js) that use Web Workers for tile decoding, terrain processing, etc.
  • Use document.write() instead of srcdoc - Fixes WebGL rendering issues (srcdoc creates opaque origin that breaks canvas updates)

(last two points fix the upcoming Map App example #235 )

Implementation

  1. Host passes CSP config as query param to sandbox.html
  2. serve.ts parses query param and sets CSP via HTTP response headers
  3. sandbox.ts no longer injects meta tags (relies on headers)

Note: basic-host is still not to be considered as a hardened production host.

🤖 Generated with Claude Code

…Domains, permissions

Security improvements:
- CSP is now set via HTTP headers in serve.ts instead of meta tags
  (meta tag CSP can be tampered with by same-origin content)
- CSP passed as query param to sandbox.html for header-based enforcement

New CSP/permissions features (borrowed from PR #158):
- frameDomains: control frame-src directive for nested iframes
- baseUriDomains: control base-uri directive
- permissions: camera, microphone, geolocation via iframe allow attribute

WebGL fix:
- Use document.write() instead of srcdoc for inner iframe content
  (srcdoc creates opaque origin that breaks WebGL canvas updates)
- Add worker-src directive with blob: support (critical for WebGL apps
  like CesiumJS/Three.js that use workers for tile decoding, terrain
  processing, image processing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 11, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/ext-apps@234

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-react@234

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-basic-vanillajs@234

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-budget-allocator@234

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-cohort-heatmap@234

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-customer-segmentation@234

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-scenario-modeler@234

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-system-monitor@234

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-threejs@234

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/modelcontextprotocol/ext-apps/@modelcontextprotocol/server-wiki-explorer@234

commit: adf7088

Keep this PR focused on CSP security fixes only.
Permissions (camera, microphone, geolocation) will be
handled in #158.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ochafik ochafik changed the title fix: Harden CSP handling in basic-host (HTTP headers + worker-src) fix: proper CSP handling in basic-host sandboxing (HTTP headers, worker-src, document.write) Jan 11, 2026
@ochafik ochafik marked this pull request as ready for review January 11, 2026 13:41
@ochafik ochafik requested a review from idosal January 11, 2026 13:41
ochafik added a commit to ochafik/mcp-ui that referenced this pull request Jan 11, 2026
Add support for passing CSP configuration via URL query parameter (?csp=<json>)
to the sandbox proxy. This enables proxy servers to set Content-Security-Policy
via HTTP headers (tamper-proof) rather than relying on meta tags or postMessage.

Changes:
- AppFrame.tsx: Build sandbox URL with CSP query param before loading iframe
- SandboxConfig.csp: Updated docs explaining query-param + postMessage fallback
- using-a-proxy.md: Added CSP Query Parameter section with server-side example
- Updated architecture diagram to show CSP flow through server

The CSP is still sent via postMessage as a fallback for proxies that don't
support the query parameter approach.

See: modelcontextprotocol/ext-apps#234
Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%)
Claude-Steers: 2
Claude-Permission-Prompts: 0
Claude-Escapes: 0
// Images: same-origin + data/blob URIs + specified domains
`img-src 'self' data: blob: ${resourceDomains}`.trim(),
// Fonts: same-origin + data/blob URIs + specified domains
`font-src 'self' data: blob: ${resourceDomains}`.trim(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude do I understand correctly that we have a risk of csp injection here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a fix, ptal @antonpk1

Validate CSP domain entries to reject characters that could:
- Break out of CSP directives (semicolons, newlines)
- Inject CSP keywords like 'unsafe-eval' (quotes)
- Inject multiple sources in one entry (spaces)

This prevents injection attacks where malicious domains could
override the security policy.
@ochafik ochafik requested a review from antonpk1 January 11, 2026 14:30
@ochafik ochafik merged commit 8b09f42 into main Jan 11, 2026
19 checks passed
modifiedHtml = cspMetaTag + modifiedHtml;
}
// Use document.write instead of srcdoc for WebGL compatibility.
// srcdoc creates an opaque origin which prevents WebGL canvas updates

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this bit. srcdoc iframes are not all opaque-origined. For example, navigate to https://example.com and run this in DevTools:

const iframe = document.createElement('iframe');
iframe.srcdoc = `<p id=log></p><script>log.textContent = self.origin</script>`;
document.body.append(iframe);

By default, srcdoc iframes inherit their navigation initiator's origin (i.e., the origin of the document that set the srcdoc attribute), unless their sandbox attribute overrides this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry good catch, will send update

Real reason: was to work around browser canvas tainting checks that seemed to treat about:srcdoc URLs as tainted, even when the iframe is same-origin with its parent. Will confirm and update the comment. in a followup

severity: "high",
},
{
target: "Element.prototype",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What dictates what's in this list? Is there documentation somewhere? APIs like setHTMLUnsafe() come to mind (maybe it's fine since it's already unsafe), but I was just curious how these were chosen.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this wasn't meant to be submitted, sorry :-(

@ochafik ochafik deleted the ochafik/fix-csp branch January 12, 2026 21:29
@ochafik ochafik restored the ochafik/fix-csp branch January 12, 2026 21:29
ochafik added a commit that referenced this pull request Jan 12, 2026
Follow-up to PR #234. This file was accidentally included.
ochafik added a commit that referenced this pull request Jan 12, 2026
Update comment to accurately explain the canvas tainting issue rather than
incorrectly claiming srcdoc creates an opaque origin.

Follow-up to PR #234 per domfarolino's review feedback.
ochafik added a commit that referenced this pull request Jan 12, 2026
Follow-up to PR #234 per domfarolino's review feedback.
ochafik added a commit that referenced this pull request Jan 12, 2026
…nt.write to equiv. srcdoc) (#248)

* chore: remove tamper-detection.ts (not meant to be submitted)

Follow-up to PR #234. This file was accidentally included.

* revert to srcdoc

Follow-up to PR #234 per domfarolino's review feedback.

* regen

* regen
@ochafik ochafik mentioned this pull request Jan 12, 2026
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.

4 participants