fix: libsodium WASM is refused by the extension CSP on every popup load, silently falling back to asm.js #182

Closed
opened 2026-08-09 16:10:35 +02:00 by clawbot · 0 comments
Collaborator

Found by driving the real popup in a containerized Chrome (see #181). Not
reachable by the unit tests, because they run in node where no extension CSP
applies.

Observed

Every single load of src/popup/index.html in a real browser emits an uncaught
page error:

Aborted(CompileError: WebAssembly.instantiate(): Refused to compile or
instantiate WebAssembly module because neither 'wasm-eval' nor 'unsafe-eval' is
an allowed source of script in the following Content Security Policy directive:
"script-src 'self'"). Build with -sASSERTIONS for more info.

libsodium-wrappers-sumo ships a WASM build and an asm.js fallback. The
extension CSP (script-src 'self', correctly, with no wasm-unsafe-eval)
refuses the WASM module, libsodium catches it and falls back to asm.js, and
everything continues to work - wallet creation, vault encryption and state
persistence were all verified working in the same session.

Why this matters anyway

vault.js is the password-based encryption for the user's seed. Three concerns,
in order of how much they should worry us:

  1. We are running an untested code path in production. Every unit test runs
    under node, where WASM instantiates fine. So the crypto backend the tests
    exercise is not the backend users actually get. Whatever confidence the test
    suite provides for vault.js, it provides it for the wrong build.
  2. The failure is silent. It surfaces as console noise that nobody reads.
    If a future libsodium version drops or breaks the asm.js fallback, this
    becomes a hard failure of wallet decryption, and the only warning we ever had
    was a log line we had learned to ignore.
  3. asm.js Argon2 is materially slower than WASM. If the KDF parameters were
    ever tuned against node timings, users are getting a slower unlock than
    intended - or, worse, parameters were softened to compensate for a slowness
    that only exists because of this.

Definition of done

  • The popup loads with no uncaught page error relating to WASM.
  • It is explicit and deliberate which libsodium backend ships, rather than
    being decided by a caught exception at runtime. Either:
    • add wasm-unsafe-eval to the extension CSP and confirm the WASM backend
      actually loads (weighed against the fact that a wallet loosening its own
      CSP deserves a hard look and may simply not be worth it), or
    • deliberately ship the asm.js build so no WASM instantiation is attempted
      at all, and document why.
  • Whichever is chosen, vault.js unit tests must exercise the backend that
    actually ships.
  • Record the decision and its reasoning in the code near the import, not only
    in this issue.
  • Verified in the #181 harness with the error no longer present, and its
    allowlist entry for this error removed.

Notes

Investigate whether the same applies to the Firefox MV2 build, whose CSP is
declared separately in manifest/firefox.json.

Recommendation: prefer shipping asm.js explicitly over loosening the CSP. A
wallet should not relax script-src for a performance gain on an operation the
user performs a handful of times a session. But measure the unlock time before
deciding, rather than assuming - if the asm.js KDF is unacceptably slow, that is
a real argument and the tradeoff should be made on numbers.

Found by driving the real popup in a containerized Chrome (see #181). Not reachable by the unit tests, because they run in node where no extension CSP applies. ## Observed Every single load of `src/popup/index.html` in a real browser emits an uncaught page error: ``` Aborted(CompileError: WebAssembly.instantiate(): Refused to compile or instantiate WebAssembly module because neither 'wasm-eval' nor 'unsafe-eval' is an allowed source of script in the following Content Security Policy directive: "script-src 'self'"). Build with -sASSERTIONS for more info. ``` `libsodium-wrappers-sumo` ships a WASM build and an asm.js fallback. The extension CSP (`script-src 'self'`, correctly, with no `wasm-unsafe-eval`) refuses the WASM module, libsodium catches it and falls back to asm.js, and everything continues to work - wallet creation, vault encryption and state persistence were all verified working in the same session. ## Why this matters anyway `vault.js` is the password-based encryption for the user's seed. Three concerns, in order of how much they should worry us: 1. **We are running an untested code path in production.** Every unit test runs under node, where WASM instantiates fine. So the crypto backend the tests exercise is not the backend users actually get. Whatever confidence the test suite provides for `vault.js`, it provides it for the wrong build. 2. **The failure is silent.** It surfaces as console noise that nobody reads. If a future libsodium version drops or breaks the asm.js fallback, this becomes a hard failure of wallet decryption, and the only warning we ever had was a log line we had learned to ignore. 3. **asm.js Argon2 is materially slower than WASM.** If the KDF parameters were ever tuned against node timings, users are getting a slower unlock than intended - or, worse, parameters were softened to compensate for a slowness that only exists because of this. ## Definition of done - The popup loads with **no uncaught page error** relating to WASM. - It is explicit and deliberate which libsodium backend ships, rather than being decided by a caught exception at runtime. Either: - add `wasm-unsafe-eval` to the extension CSP and confirm the WASM backend actually loads (weighed against the fact that a wallet loosening its own CSP deserves a hard look and may simply not be worth it), **or** - deliberately ship the asm.js build so no WASM instantiation is attempted at all, and document why. - Whichever is chosen, `vault.js` unit tests must exercise the backend that actually ships. - Record the decision and its reasoning in the code near the import, not only in this issue. - Verified in the #181 harness with the error no longer present, and its allowlist entry for this error removed. ## Notes Investigate whether the same applies to the Firefox MV2 build, whose CSP is declared separately in `manifest/firefox.json`. Recommendation: prefer shipping asm.js explicitly over loosening the CSP. A wallet should not relax `script-src` for a performance gain on an operation the user performs a handful of times a session. But measure the unlock time before deciding, rather than assuming - if the asm.js KDF is unacceptably slow, that is a real argument and the tradeoff should be made on numbers.
clawbot added this to the 1.0.0 milestone 2026-08-09 16:10:35 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#182