Skip to content

Remove unpkg.com dependency and serve manifold assets locally#396

Open
tracygardner wants to merge 3 commits intomainfrom
claude/csp-safe-unpkg-alternative-Uy5dW
Open

Remove unpkg.com dependency and serve manifold assets locally#396
tracygardner wants to merge 3 commits intomainfrom
claude/csp-safe-unpkg-alternative-Uy5dW

Conversation

@tracygardner
Copy link
Contributor

@tracygardner tracygardner commented Mar 15, 2026

Summary

This PR removes the external dependency on unpkg.com for manifold-3d assets by serving both the WASM binary and its JavaScript companion from a local wasm/ directory. This improves security by eliminating an external origin from the Content Security Policy and reduces reliance on external CDNs.

Key Changes

  • Updated CSP policies across all configuration files (CSP_POLICY.md, vite.config.mjs, index.html) to remove https://unpkg.com from both script-src and connect-src directives
  • Modified asset loading logic in api/shapes.js to serve both manifold.wasm and manifold.js from the local wasm/ directory instead of fetching from unpkg.com
  • Updated build configuration in vite.config.mjs to copy manifold.js alongside the existing manifold.wasm file during the build process
  • Updated documentation in CSP_POLICY.md to remove the explanation for the unpkg.com origin requirement

Implementation Details

The getManifold() function now intercepts both .wasm and .js file requests from the manifold-3d module and redirects them to the local wasm/ directory. This ensures that the manifold library's initialization files are served locally, eliminating the need for external network access to unpkg.com while maintaining full functionality.

https://claude.ai/code/session_01AemiiYusMHWjBimfoovWBp

Summary by CodeRabbit

  • Security
    • Tightened Content Security Policy: removed an external CDN from script and connect sources, reducing external dependencies and improving site safety.
  • New Features / Stability
    • Improved 3D engine startup: preloads the manifold/mesh engine to improve initialization reliability and 3D performance.
  • Public API
    • Exposed an additional manifold accessor for integrations that rely on advanced geometry features.

manifold-3d's Emscripten-compiled JS calls locateFile for both the .wasm
binary and any .js companion files (e.g. worker scripts). The .wasm was
already redirected to the local wasm/ directory, but the .js file fell
through and was resolved against the package's original unpkg.com base URL,
requiring https://unpkg.com in both script-src and connect-src.

Fix:
- Copy node_modules/manifold-3d/manifold.js to wasm/ at build time via
  viteStaticCopy (alongside the existing manifold.wasm copy).
- Extend locateFile in api/shapes.js to redirect .js files to wasm/<name>
  as well, so all manifold-3d assets are served from self.
- Remove https://unpkg.com from script-src and connect-src in the CSP
  (vite.config.mjs, index.html, docs/CSP_POLICY.md).

https://claude.ai/code/session_01AemiiYusMHWjBimfoovWBp
@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62e99db7-edcb-4144-a5b2-c191c1a70506

📥 Commits

Reviewing files that changed from the base of the PR and between acffbcc and 9e2dda4.

📒 Files selected for processing (3)
  • api/shapes.js
  • flock.js
  • vite.config.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/shapes.js
  • vite.config.mjs

📝 Walkthrough

Walkthrough

Tightens Content-Security-Policy entries (removes unpkg.com) and exposes a new getManifold export; flock initialization now preloads and injects manifold wasm/mesh instances into InitializeCSG2Async via an options object.

Changes

Cohort / File(s) Summary
CSP updates
index.html, docs/CSP_POLICY.md, vite.config.mjs
Removed https://unpkg.com from CSP script-src/connect-src directives; updated documentation and build-time CSP meta policy accordingly.
API export surface
api/shapes.js
Added named export getManifold to the public API surface.
Initialization / runtime
flock.js
Now preloads manifold wasm/mesh instances and calls InitializeCSG2Async({ manifoldInstance, manifoldMeshInstance }) instead of calling it with no args; also imports/exports getManifold from ./api/shapes.

Sequence Diagram(s)

sequenceDiagram
  participant App as App
  participant Shapes as api/shapes.js
  participant Wasm as Manifold WASM
  participant Flock as flock.js

  rect rgba(200,230,201,0.5)
  App->>Shapes: import { getManifold }
  end

  rect rgba(187,222,251,0.5)
  Shapes->>Wasm: instantiate/preload manifold WASM
  Wasm-->>Shapes: return manifoldInstance, manifoldMeshInstance
  end

  rect rgba(255,224,178,0.5)
  App->>Flock: call InitializeCSG2Async()
  Flock->>Shapes: getManifold()
  Shapes-->>Flock: { manifoldInstance, manifoldMeshInstance }
  Flock->>Wasm: InitializeCSG2Async({ manifoldInstance, manifoldMeshInstance })
  Wasm-->>Flock: initialization complete
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in with code in hand,
Tightened fences round the land,
Brought my manifold to play,
Loaded wasm the local way,
Goodbye CDN, hello local sand! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: removing unpkg.com dependency and serving manifold assets locally, which is the core objective reflected across all file modifications (CSP policy removals, local asset serving configuration, and initialization updates).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/csp-safe-unpkg-alternative-Uy5dW
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/CSP_POLICY.md`:
- Line 39: The CSP connect-src documented in CSP_POLICY.md is inconsistent with
the runtime policy emitted by the build (the Vite config and index HTML include
a broad "https:" scheme); either update the documented connect-src line in
CSP_POLICY.md to include the https: scheme alongside the existing hosts, or
remove the generic "https:" allowance from the runtime policy in the Vite
configuration and from the meta tag in index HTML so the implementation matches
the restrictive list in the doc—locate the connect-src declaration in
CSP_POLICY.md and the corresponding connect-src values produced by the Vite
config and index HTML and make the values identical.

In `@vite.config.mjs`:
- Line 13: The CSP string in CSP_META_POLICY contains a broad connect-src token
"https:" which allows any HTTPS origin; update the CSP_META_POLICY definition to
remove the "https:" scheme from the connect-src directive and instead list only
the explicit required origins (e.g., https://www.googletagmanager.com,
https://www.google-analytics.com, https://region1.google-analytics.com,
https://stats.g.doubleclick.net) or, if you intentionally want to permit
arbitrary HTTPS endpoints, add a clear comment above CSP_META_POLICY documenting
that decision and its risks; locate and edit the CSP_META_POLICY constant in
vite.config.mjs and adjust the connect-src clause accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70d948f0-ee70-4765-8a2c-15ab55e2c4c2

📥 Commits

Reviewing files that changed from the base of the PR and between 57b9947 and acffbcc.

📒 Files selected for processing (4)
  • api/shapes.js
  • docs/CSP_POLICY.md
  • index.html
  • vite.config.mjs

img-src 'self' data: blob: https://www.google-analytics.com https://www.googletagmanager.com;
font-src 'self' data:;
connect-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://region1.google-analytics.com https://stats.g.doubleclick.net https://unpkg.com;
connect-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://region1.google-analytics.com https://stats.g.doubleclick.net;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documentation does not match actual CSP implementation.

The documented connect-src directive differs from what's defined in vite.config.mjs (line 13) and index.html (line 7). The implementation includes a broad https: scheme allowance that is not reflected in this documentation:

Source connect-src value
This doc 'self' https://www.googletagmanager.com https://www.google-analytics.com ...
vite.config.mjs / index.html 'self' https: https://www.googletagmanager.com ...

Please either update the documentation to reflect the actual policy, or remove the https: from the implementation files to match this more restrictive documented policy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/CSP_POLICY.md` at line 39, The CSP connect-src documented in
CSP_POLICY.md is inconsistent with the runtime policy emitted by the build (the
Vite config and index HTML include a broad "https:" scheme); either update the
documented connect-src line in CSP_POLICY.md to include the https: scheme
alongside the existing hosts, or remove the generic "https:" allowance from the
runtime policy in the Vite configuration and from the meta tag in index HTML so
the implementation matches the restrictive list in the doc—locate the
connect-src declaration in CSP_POLICY.md and the corresponding connect-src
values produced by the Vite config and index HTML and make the values identical.


// `frame-ancestors` is only enforced from HTTP headers (ignored in CSP meta tags).
const CSP_META_POLICY = "default-src 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://www.googletagmanager.com https://unpkg.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://www.google-analytics.com https://www.googletagmanager.com; font-src 'self' data:; connect-src 'self' https: https://www.googletagmanager.com https://www.google-analytics.com https://region1.google-analytics.com https://stats.g.doubleclick.net https://unpkg.com; media-src 'self' data: blob:; worker-src 'self' blob:; frame-src 'self'; manifest-src 'self'";
const CSP_META_POLICY = "default-src 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://www.google-analytics.com https://www.googletagmanager.com; font-src 'self' data:; connect-src 'self' https: https://www.googletagmanager.com https://www.google-analytics.com https://region1.google-analytics.com https://stats.g.doubleclick.net; media-src 'self' data: blob:; worker-src 'self' blob:; frame-src 'self'; manifest-src 'self'";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

connect-src includes overly broad https: scheme allowance.

The CSP policy contains connect-src 'self' https: ... which permits connections to any HTTPS endpoint. This undermines the security benefit of removing unpkg.com, since the policy now allows fetching from any external HTTPS origin.

If the intent is to allow arbitrary HTTPS connections (e.g., for user-provided URLs), this is acceptable but should be documented. Otherwise, consider removing https: and explicitly listing only the required origins.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vite.config.mjs` at line 13, The CSP string in CSP_META_POLICY contains a
broad connect-src token "https:" which allows any HTTPS origin; update the
CSP_META_POLICY definition to remove the "https:" scheme from the connect-src
directive and instead list only the explicit required origins (e.g.,
https://www.googletagmanager.com, https://www.google-analytics.com,
https://region1.google-analytics.com, https://stats.g.doubleclick.net) or, if
you intentionally want to permit arbitrary HTTPS endpoints, add a clear comment
above CSP_META_POLICY documenting that decision and its risks; locate and edit
the CSP_META_POLICY constant in vite.config.mjs and adjust the connect-src
clause accordingly.

claude added 2 commits March 15, 2026 08:06
Instead of redirecting manifold-3d's .js companion files through locateFile
(which served a raw copy that still had the hardcoded unpkg.com scriptDirectory,
breaking WASM loading in workers), strip the hardcoded URL at import time with
a Vite transform plugin.

The manifoldUnpkgPatchPlugin transform hook matches node_modules/manifold-3d/
manifold.js and replaces 'https://unpkg.com/manifold-3d...' with '' so
Emscripten falls back to deriving scriptDirectory from the script's own URL
(local server in dev, the bundle chunk in prod). This means no fetch ever
leaves the origin for manifold-3d assets, making the https://unpkg.com CSP
allowance unnecessary.

Also reverts the broken locateFile .js redirect (caused 'ManifoldMesh is not a
constructor' because the raw worker copy tried to load WASM from unpkg.com
which was then blocked by CSP) and removes the now-unneeded raw manifold.js
static copy from viteStaticCopy.

https://claude.ai/code/session_01AemiiYusMHWjBimfoovWBp
Root cause: BABYLON.InitializeCSG2Async() with no arguments fetches
manifold-3d from https://unpkg.com/manifold-3d@3.3.0 at runtime. With
unpkg.com removed from the CSP, that fetch is blocked, leaving BabylonJS
without an initialized manifold module. Any subsequent CSG2 operation then
throws "ManifoldMesh is not a constructor".

Fix: InitializeCSG2Async accepts an options object with manifoldInstance and
manifoldMeshInstance. We pre-load the wasm module via the existing getManifold()
helper in api/shapes.js (which already serves the WASM from the locally-hosted
/wasm/manifold.wasm via locateFile) and inject the resulting Manifold and Mesh
constructors into BabylonJS, so it never makes an external request.

Also removes the manifoldUnpkgPatchPlugin Vite transform that was targeting the
wrong file — the unpkg.com load was coming from BabylonJS, not from the
manifold-3d npm package itself.

https://claude.ai/code/session_01AemiiYusMHWjBimfoovWBp
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.

2 participants