Skip to content

docs(wasm): say what csp the rendered output needs - #718

Merged
andiwand merged 3 commits into
mainfrom
docs/wasm-csp
Aug 20, 2026
Merged

docs(wasm): say what csp the rendered output needs#718
andiwand merged 3 commits into
mainfrom
docs/wasm-csp

Conversation

@andiwand

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Closes #710.

wasm/README.md said the rendered html is self-contained and can go straight into a blob: iframe. That is true of the markup, but not of what the page is allowed to load: a frame inherits the embedding page's Content-Security-Policy, so the embedder's policy governs the rendered document — and nothing said what it has to allow.

The failures are quiet. Under font-src 'self' a pdf's data: fonts are all blocked, and because a pdf's text is painted with the code points its embedded subset defines, the glyphs do not fall back to a system face — every one comes out as a replacement box. Office formats embed images rather than fonts, which makes it easy to conclude the embed works.

Measured, not assumed

Rendered an odt, ods, docx, odp, csv, a standalone image and a pdf through the package and counted what the output carries:

@font-face (data:) <img src="data:"> <style> style= attributes <script> external urls
about.odt 0 2 2 123 2 0
style-various-1.docx 0 1 2 2 0
comment.ods 0 0 3 23 3 0
style-various-1.odp 0 0 2 2 0
style-various-1.pdf 7 of 8 0 2 7 1 0
fantastic-landscape.jpg 0 1 1 0 0

Which confirms the issue's table, and adds two things it did not name:

  • style-src cannot be narrowed to a nonce or a hash — most of the styling is style attributes, and only 'unsafe-inline' (or 'unsafe-hashes') covers those.
  • audio and video stay linked resources rather than data urls, so a media file needs media-src pointing at wherever the resource was written.

Also: nothing is fetched from another origin, so no host ever has to be allow-listed — worth stating, since that is the property the library is meant to have.

The new section gives a working policy, says which part of the output each directive is for, and calls out that a blocked inline script is silent (the layout is css, so the document still looks right while search, editing and the spreadsheet behaviour simply do not run). The blob: iframe paragraph in Use now points at it.

Docs only — no code changes. Independent of #709, which covers the script-src the module itself needs at load time and touches a different part of the file.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b34e1e483d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread wasm/README.md
andiwand and others added 3 commits August 20, 2026 14:33
"Self-contained" is true of the markup but not of what a page may load:
a frame inherits the embedding page's policy, so the embedder's CSP
governs the rendered document, and nothing said what that has to allow.
The failures are quiet — under `font-src 'self'` a pdf's `data:` fonts
are blocked, and because they carry the code points the text is painted
with, every glyph comes out as a replacement box instead of falling back
to a system face.

Measured across an odt, ods, docx and pdf rendered through the package:
`font-src data:`, `img-src data:`, `style-src 'unsafe-inline'` (blocks
and, more to the point, hundreds of `style` attributes, which a nonce
cannot cover) and `script-src 'unsafe-inline'`. Nothing is fetched from
another origin.

Closes #710

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDs5aK3ZGSZsEvqUUwBBXU
Recommending it without qualification was one-sided: it permits
`javascript:` urls too, and a document may carry one — odrcore filters a
pdf link action down to an allowlist of navigable schemes, but an odt or
docx hyperlink is only attribute-escaped. In a `blob:` frame, which is
same-origin with the embedder by design, such a link runs with the
embedder's origin.

The section now says so, and gives the way out for untrusted input:
`sandbox="allow-scripts"` without `allow-same-origin`, which keeps the
renderer's own scripts working inside the frame and gives up the
`contentWindow.odr` API. It also records why hashes or a nonce are not an
option here — `embed_shipped_resources`, which would leave no inline
script at all, is not bound in the wasm build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDs5aK3ZGSZsEvqUUwBBXU
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d5RcmsA777vwXiuafjx6k
@andiwand
andiwand merged commit 0e935d9 into main Aug 20, 2026
25 checks passed
@andiwand
andiwand deleted the docs/wasm-csp branch August 20, 2026 12:34
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.

Document the CSP that rendered output requires (PDF fonts are data: URLs and render as tofu without it)

1 participant