fix(security): scope bundled API route module reuse to project and env identity - #4372
Conversation
…v identity The process-global bundled-module cache introduced in #3912 keyed entries on projectDir, modulePath, and generated source only. In hosted proxy execution every non-local project resolves to the host runtime's shared project dir and per-request env isolation is applied with runWithProjectEnv, so a route module whose top-level initialization ran under one project's or environment's env overlay could be served from cache to a later load in a different scope when the path and bundled output matched — leaking module-level clients, secrets, and mutable state across tenants and environments. Fold a scope discriminator into the cache owner: the ambient registry scope (project, mode, version) and a digest of the active project-env overlay. Local single-tenant loads carry neither and keep their current key, so dev-mode module-state reuse is unchanged. Both directions are pinned by tests: a module is not reused across different env overlays or different hosted project scopes, and the same scope keeps reusing its own module. Tested: deno test --preload=src/testing/preload.ts --no-check --allow-all --unstable-worker-options --unstable-net src/routing/api/module-loader/loader.test.ts Tested: deno check src/routing/api/module-loader/loader.ts src/routing/api/module-loader/loader.test.ts Tested: deno lint src/routing/api/module-loader/loader.ts src/routing/api/module-loader/loader.test.ts Tested: deno fmt --check src/routing/api/module-loader/loader.ts src/routing/api/module-loader/loader.test.ts Claude-Session: https://claude.ai/code/session_01QfWNMiUhvWMKWi6BGfVdY3
|
You have reached your Codex usage limits for security reviews. Please try again later. |
There was a problem hiding this comment.
kojiwakayama has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
📝 WalkthroughWalkthroughThe bundled route module cache now uses registry, environment, and project-env scope data in its identity. It uses captured intrinsic operations, removes the size cap, handles failed loads explicitly, and adds isolation and retention tests. Config-less hosted API handlers bypass caching when a project environment snapshot is active. ChangesBundled module scope isolation
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to This change scopes bundled route-module caching by hosted project and environment overlay while retaining reuse within a scope. The remaining note is limited to strengthening a regression test, with no identified current production defect. Sequence Diagram(s)sequenceDiagram
participant RouteLoader
participant ScopeDiscriminator
participant RequestContext
participant ProjectEnvStorage
participant BundledModuleCache
RouteLoader->>ScopeDiscriminator: compute scoped module identity
ScopeDiscriminator->>RequestContext: read request environment
ScopeDiscriminator->>ProjectEnvStorage: read project-env snapshot
ScopeDiscriminator-->>RouteLoader: return scope hash
RouteLoader->>BundledModuleCache: load using scoped owner key
BundledModuleCache-->>RouteLoader: return retained module
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
📦 Client bundle boundary
A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in |
|
Note Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime. Code Review ✅ ApprovedScopes the bundled API route module cache to project and environment identity to prevent module-level state leaks across tenants and environments in hosted proxy execution. The fix folds registry scope id and project-env overlay digest into the cache key alongside OptionsDisplay: compact → Showing less information. Comment with these commands to change the behavior for this request:
Important Your trial ends in 6 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more. Was this helpful? React with 👍 / 👎 | Gitar |
Summary
The process-global bundled-module cache below
APIRouteHandler(introduced in #3912) keyed entries only onprojectDir,modulePath, and the generated source hash. In hosted proxy execution every non-local project resolves to the host runtime's shared project directory, and per-request environment isolation is applied withrunWithProjectEnvbefore route loading and execution. A route module therefore runs its top-level initialization under one project's or environment's env overlay and can then be served from the cache to a later load in a different scope whenever the virtual path and bundled output match — making module-level clients, secrets, and mutable state initialized in the first scope available to the second. The leak fires between tenants and even between environments of a single project (identical release code, different env vars). It is reachable when host-realm API execution is granted (operator-granted host execution with worker isolation disabled); the default shared-runtime path that denies host project execution is not affected.Finding
dbf02b16ffc881919b8edfcdc189d40b(finding 29)Fix
loadAndTranspileModulenow folds a scope discriminator into the bundled-module cache owner alongsideprojectDirandmodulePath:tryGetRegistryScopeId(): project, mode, version) — the same scope identity the higher-level API handler cache uses, so two hosted projects sharing the virtual project dir and byte-identical bundled output never share a module; andgetProjectEnvSnapshot()), so a module initialized under one environment's env overlay is never reused under another, including between environments of one project. Snapshot keys are sorted and free of NUL/=and values are free of NUL, so the digested serialization is canonical.Local single-tenant loads (no request context, no env overlay) carry an empty discriminator and keep their existing cache key, preserving the dev-mode module-state reuse that #3912 introduced.
Test evidence
Two regression tests added to
src/routing/api/module-loader/loader.test.ts, both failing on the unfixed loader and passing with the fix:does not reuse a bundled module across different project env overlays— a bundled route with module-level state loaded underrunWithProjectEnv({TENANT_SECRET:"a"})is not reused under{TENANT_SECRET:"b"}, while a repeat load under the same overlay keeps its module.does not reuse a bundled module across different hosted project scopes— the same path/output under two differentrunWithCacheKeyContextproject identities yields distinct modules.Verified locally:
deno test --preload=src/testing/preload.ts --no-check --allow-all --unstable-worker-options --unstable-net src/routing/api/module-loader/loader.test.ts— 9 passed (167 steps), 0 faileddeno check src/routing/api/module-loader/loader.ts src/routing/api/module-loader/loader.test.ts— cleandeno lintanddeno fmt --checkon both touched files — cleanhttps://claude.ai/code/session_01QfWNMiUhvWMKWi6BGfVdY3
Summary by CodeRabbit