Closed general

Widget frames block WebAssembly: add 'wasm-unsafe-eval' to the widget-frame script-src

John Lauer · 1mo ago ·closed by Colby Knox

Widget frames block WebAssembly: add 'wasm-unsafe-eval' to the widget-frame script-src

The new adom-symbol-variants widget (registered in adom/widgets, script served from /widget-script/adom-symbol-variants.js) runs adom-symbol's Rust symbol generator compiled to wasm32, so the browser produces byte-identical .kicad_sym to the CLI. In the widget frame it fails on load:

WebAssembly.instantiate(): Compiling or instantiating WebAssembly module violates the following
Content Security Policy directive because 'unsafe-eval' is not an allowed source of script in the
following Content Security Policy directive: "script-src 'unsafe-inline' https:".

Repro: https://wiki.adom.inc/adom/adom-symbol/widget/adom-symbol-variants?src=%2Fray%2Fch340g%2Frender%2Fpage.json&layout=sqg (the frame the tag on adom/adom-symbol's README embeds).

Ask

Add 'wasm-unsafe-eval' to the widget frame's script-src (only that directive, only for /OWNER/SLUG/widget/<name> frames):

script-src 'unsafe-inline' 'wasm-unsafe-eval' https:

'wasm-unsafe-eval' permits WebAssembly compilation only; it does NOT enable eval() / new Function() (that is what 'unsafe-eval' does). The frame keeps its opaque origin, storage shim, and wiki-only connect-src, so the trust model is unchanged: a wasm module can do nothing a plain script in the same frame cannot already do. Chrome 97+, Firefox 102+, Safari 16+ understand it; older browsers ignore the unknown token, which is the current behaviour.

Until this lands the widget falls back to a JavaScript port of the same generator, so it still renders; the wasm path is the one that guarantees parity with the CLI output.

1 Reply

Colby Knox · 1mo ago

Shipped, on prod. The widget frame's script-src is now exactly what you asked for:

script-src 'unsafe-inline' 'wasm-unsafe-eval' https:

Scoped as specified: only the /OWNER/SLUG/widget/ frames. The README and blob HTML frames keep the previous policy byte for byte; a test asserts the ONLY difference between the two policies is the wasm token, that 'unsafe-eval' never appears, and that the sandbox flags (opaque origin, no allow-same-origin) are unchanged. Your trust-model argument is the one I used to justify it: a frame already running arbitrary publisher JS gains nothing from wasm, which has no I/O of its own, and we made the identical call for the Draco decoder in the custom viewer and native 3D viewer CSPs.

Verified live on your repro URL: the response header carries the token. The wasm path should now win over the JS fallback; if the widget still reports the CSP violation, a stale cached frame is the first suspect (Cache-Control is no-store on that route, so a hard reload should be enough).

Log in to reply.