Repository navigation
fix(wallet): ship the security headers config in the build output - #683
Conversation
The with-ic-assets plugin generated .ic-assets.json into apps/wallet/public from the writeBundle hook. Vite copies public/ before that hook runs, so a clean build never had the file in its output, and the deterministic release artifact never carried it. Deploys of that artifact therefore uploaded the wallet without its headers: app.orbit.global serves no Content-Security-Policy, X-Frame-Options, X-Content-Type-Options or Referrer-Policy, and none of the cache rules apply. Write the file into the build output instead, and keep a leftover copy in public/ out of the versioned UI directory, where it would override the root config. Add a test for the plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2vTHJDxWCtmbSkf3682e
There was a problem hiding this comment.
🟡 Changes recommended
The enforced connect-src policy blocks both WASM chunk store subdomains, causing registry-based disaster recovery to fail before WASM and Candid can be loaded.
1 open issue
What changed in this PR
This PR ensures clean wallet builds include the asset configuration needed to apply security headers and cache rules when deploying release artifacts.
Changes:
- Write
.ic-assets.jsondirectly into the build output and remove the unusedpublicDiroption. - Exclude stale asset configuration files when copying public assets into versioned directories.
- Add unit tests for output placement, security headers, and inline-script hashes.
| File | Description |
|---|---|
apps/wallet/build/plugins/with-versioned-entrypoint.plugin.ts |
Prevent stale nested asset configurations from overriding the root configuration. |
apps/wallet/build/plugins/with-ic-assets.plugin.ts |
Generate the asset configuration directly in the build output. |
apps/wallet/build/plugins/with-ic-assets.plugin.spec.ts |
Test configuration generation and security policy contents. |
Review effort: Balanced
Disaster recovery downloads registry modules from the wasm chunk store's HTTP interface at https://<store>.icp0.io. The host source https://icp0.io does not match subdomains, so once the CSP is actually served the download is blocked. Allow the production and playground chunk stores, and test that every wasm_chunk_store in canister_ids.json is allowed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2vTHJDxWCtmbSkf3682e
|
✅ No security or compliance issues detected. Reviewed everything up to 13b4396. Security OverviewDetected Code Changes
|
There was a problem hiding this comment.
🔵 Needs a closer look
Activating the previously unenforced wallet CSP requires release-artifact and deployed-header checks plus playground browser validation of login, versioned navigation, and disaster-recovery downloads, since unit tests alone cannot establish runtime compatibility.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
The cut was forced with `patch` because nx 19.8.3 does not attribute squash-merged commits whose message contains GitHub's "---------" co-author separator, so nx wrote "no code changes". This restores #683 in the CHANGELOG and in the release notes in .release.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VP2vTHJDxWCtmbSkf3682e

Summary
The wallet's security headers never reach the canister when it is deployed from a release artifact. app.orbit.global (and playground.orbitwallet.io) currently serve no
Content-Security-Policy,X-Frame-Options,X-Content-Type-OptionsorReferrer-Policy, and none of the cache rules from the wallet's asset config apply. orbit.global and docs.orbit.global, whose.ic-assets.jsonis committed, serve theirs.Cause
with-ic-assetsgenerates.ic-assets.jsonin itswriteBundlehook and wrote it toapps/wallet/public/. Vite copiespublic/into the output before that hook runs, so the file only reached the output of a later build on the same machine. A clean build, such as the deterministic Docker build behind every release, never contains it. The 0.8.1 release tarball has no.ic-assets.json, soscripts/deploy-appuploads the assets without headers.Changes
with-ic-assets.plugin.ts: write.ic-assets.jsoninto the build output (dir), next to the assets it configures. ThepublicDiroption is dropped; nothing passed it.with-versioned-entrypoint.plugin.ts: don't copy a leftoverpublic/.ic-assets.json(from builds before this change) intodist/v<version>/. A nested config there would override the root one for the versioned UI.with-ic-assets.plugin.spec.ts: new tests. The file is written into the output, setsX-Frame-Options: DENY,nosniff, a CSP withdefault-src 'none'andframe-ancestors 'none', and allows inline scripts ofindex.htmlby hash.The released
index.htmlandv0.8.1/index.htmlcontain no inline scripts, so the root CSP applies to the versioned UIs unchanged.Testing
.ic-assets.jsonat its root.Generated by Claude Code