diff --git a/apps/mana/apps/web/src/hooks.server.ts b/apps/mana/apps/web/src/hooks.server.ts index a5411edae..c15809951 100644 --- a/apps/mana/apps/web/src/hooks.server.ts +++ b/apps/mana/apps/web/src/hooks.server.ts @@ -103,6 +103,11 @@ window.__PUBLIC_GLITCHTIP_DSN__ = ${JSON.stringify(PUBLIC_GLITCHTIP_DSN)}; const isDev = process.env.NODE_ENV !== 'production'; setSecurityHeaders(response, { + // @huggingface/transformers (used by @mana/local-llm) lazy-loads the + // onnxruntime-web WASM loader from jsDelivr at backend selection + // time via a dynamic import(). Browsers route dynamic imports + // through script-src. + scriptSrc: ['https://cdn.jsdelivr.net'], connectSrc: [ PUBLIC_MANA_AUTH_URL_CLIENT, PUBLIC_SYNC_SERVER_URL_CLIENT, @@ -112,6 +117,11 @@ window.__PUBLIC_GLITCHTIP_DSN__ = ${JSON.stringify(PUBLIC_GLITCHTIP_DSN)}; PUBLIC_MANA_EVENTS_URL_CLIENT, PUBLIC_MANA_API_URL_CLIENT, 'wss://sync.mana.how', + // transformers.js *also* fetch()es the .wasm binary and the .mjs + // loader factory directly to pre-warm the runtime — those go + // through connect-src, not script-src, so jsDelivr has to be in + // both lists for the WebGPU backend resolver to succeed. + 'https://cdn.jsdelivr.net', // @mana/local-llm (transformers.js) pulls model config + ONNX // shards from the HuggingFace ecosystem. HF currently uses three // distinct CDN domains depending on file type and rollout state: diff --git a/packages/shared-utils/src/security-headers.ts b/packages/shared-utils/src/security-headers.ts index b62af52cd..5b2f35e93 100644 --- a/packages/shared-utils/src/security-headers.ts +++ b/packages/shared-utils/src/security-headers.ts @@ -67,21 +67,10 @@ export function setSecurityHeaders(response: Response, options: SecurityHeadersO // WebAssembly compilation, NOT eval()/new Function() — much narrower // than the legacy 'unsafe-eval' source. Supported by all evergreen // browsers. - // - // cdn.jsdelivr.net is allowlisted because @huggingface/transformers - // loads onnxruntime-web via a runtime dynamic `import()` from - // jsDelivr (the package itself is bundled, but the WASM-loader - // shim is fetched lazily so transformers.js v4 can pick the - // right backend without bloating the static bundle). - `script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://stats.mana.how https://glitchtip.mana.how https://cdn.jsdelivr.net ${scriptSrc.join(' ')}`.trim(), + `script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://stats.mana.how https://glitchtip.mana.how ${scriptSrc.join(' ')}`.trim(), "style-src 'self' 'unsafe-inline'", `img-src 'self' data: blob: https: ${imgSrc.join(' ')}`.trim(), - // jsDelivr also has to be in connect-src because @huggingface/transformers - // pre-loads the WASM binary and the loader .mjs via plain fetch() (not - // just dynamic import) when selecting the ONNX backend. The script-src - // allowlist alone covers the import() but not the fetch() — both are - // required for the WebGPU backend resolver to succeed. - `connect-src 'self' https://stats.mana.how https://glitchtip.mana.how https://cdn.jsdelivr.net ${connectSrc.join(' ')}`.trim(), + `connect-src 'self' https://stats.mana.how https://glitchtip.mana.how ${connectSrc.join(' ')}`.trim(), `font-src 'self' ${fontSrc.join(' ')}`.trim(), mediaSrc.length > 0 ? `media-src 'self' ${mediaSrc.join(' ')}`.trim() : '', "object-src 'none'",