Repository navigation
[APPS-2961] Add: limit local call processes with Node's permission model - #617
Conversation
Live Debugger Runtime BenchmarkSDK-loaded dormant-probe runtime overhead, measured against an uninstrumented bundle in the same browser session.
What do the Tiny and Hot workloads represent? Browser Debugger SDK: Full diagnosticsRaw samples are in the |
🎉 All green!🧪 All tests passed 🔗 Commit SHA: f820dcf | Docs | View more details | Give us feedback! |
1515aa5 to
ff3d282
Compare
|
@codex review |
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. |
There was a problem hiding this comment.
🟡 Changes recommended
Disabling the existing write guards leaves a symlink-write regression that can modify otherwise read-only project files.
2 open issues
What changed in this PR
Adds Node’s permission model to the fresh-process local backend executor introduced in #608, strengthening isolation while retaining fallback guards on unsupported Node configurations.
Changes:
- Adds process-wide filesystem permissions and checks Vite module loads against readable roots and deny patterns.
- Passes Vite filesystem settings and its SSR resolver to the executor.
- Documents the sandbox and adds unit and integration coverage.
| File | Description |
|---|---|
yarn.lock |
Records the picomatch dependencies. |
packages/tests/src/_jest/fixtures/apps_backend_project/childLaterImport.backend.ts |
Adds a missing-import fixture. |
packages/plugins/apps/src/vite/local-execution-sandbox.ts |
Implements permission flags, readable roots, and module-load checks. |
packages/plugins/apps/src/vite/local-execution-sandbox.test.ts |
Tests sandbox helpers and permission flags. |
packages/plugins/apps/src/vite/local-execution-protocol.ts |
Adds the fallback-guard setting. |
packages/plugins/apps/src/vite/local-execution-process.ts |
Applies permissions and guards module requests. |
packages/plugins/apps/src/vite/local-execution-process.integration.test.ts |
Covers sandbox restrictions and fallback behavior. |
packages/plugins/apps/src/vite/local-execution-child.ts |
Conditionally installs in-process guards. |
packages/plugins/apps/src/vite/index.ts |
Supplies Vite filesystem settings and resolution. |
packages/plugins/apps/README.md |
Documents sandbox behavior and compatibility. |
packages/plugins/apps/package.json |
Adds picomatch and its types. |
Review effort: Balanced
| if (!config.installGuards) { | ||
| return load(config); |
There was a problem hiding this comment.
Accepted as a known limitation: Node's permission model follows symlinks out of granted paths, as its docs note. Local execution is a parity aid, not a security boundary, so this is listed under Out of Scope and in the README instead of re-adding the JavaScript write guard.
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
ff3d282 to
b42ad32
Compare
6c426e6 to
e842184
Compare
b42ad32 to
7dc7d64
Compare
e842184 to
44a23e7
Compare
c89cc54 to
09ba149
Compare
44a23e7 to
9f367ef
Compare
09ba149 to
82157b9
Compare
9f367ef to
c78647b
Compare
82157b9 to
1544220
Compare
c78647b to
b750a1b
Compare
1544220 to
bb0797f
Compare
Each local backend function call process now runs under Node's permission model: it reads the project, its node_modules folders and linked packages, Vite and server.fs.allow, writes only to its own temp dir and /tmp, and can't spawn subprocesses, workers or native addons. Module loads over IPC are checked against the readable folders and server.fs.deny before Vite reads them, and a load error about an unreadable file is replaced with a refusal that says whether the file matches server.fs.deny or is outside the readable folders. On a Node without the permission model, or without --allow-inspector while debugging, the process keeps the in-process guards. On a Node without module.registerHooks, such as Node 20, modules get no import.meta.resolve: Vite resolves it on a worker thread there, which the permission model refuses. The dev server warns once when a call runs that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bb0797f to
f820dcf
Compare


Motivation
Architecture
flowchart LR D["Vite dev server"] -->|"fork with --permission, --allow-fs-read, --allow-fs-write"| C["Call process"] C -->|"fetchModule over IPC"| G["Module-load guard"] G -->|"readable and not denied"| V["Vite SSR environment"] G -->|"outside the readable folders or in server.fs.deny"| R["Refusal"] V -->|"module code, or an error naming a readable file"| CChanges
6 changes across local-execution-sandbox.ts, local-execution-process.ts, local-execution-child.ts, index.ts, the README and tests
server.fs.allow; writes only its temp dirs.local-execution-sandbox.ts,local-execution-process.tsserver.fs.deny, saying which, and strips Vite's load errors about unreadable files.local-execution-sandbox.ts--allow-inspectorwhile debugging.local-execution-child.ts,local-execution-protocol.tsimport.meta.resolveunder the permission model on a Node withoutmodule.registerHooks(added in 22.15), where Vite resolves it on a worker thread, and warns once per dev server.local-execution-child.ts,local-execution-process.tsserver.fssettings and SSR resolver to the executor, and addspicomatch.index.ts,package.jsonREADME.md,local-execution-sandbox.test.ts,local-execution-process.integration.test.tsQA Instructions
15442202, as are the staging run and the Vite 8.3.3 matrix run; that matrix's parallel and log-prefix tests need [APPS-2961] Update: run local backend function calls in parallel, with per-call log prefixes #603, so they fail here.ERR_ACCESS_DENIED); no file created"projectWrite":"refused: ERR_ACCESS_DENIED",project file exists after calls: false/tmpwrites,fetch"tempWrite":"ok: x","sharedTmpWrite":"ok: written","fetch":"ok: 200"ERR_ACCESS_DENIED)"spawn":"refused: ERR_ACCESS_DENIED","worker":"refused: ERR_ACCESS_DENIED".envand/etc/hostsmodule loadscan't load "/.env": it matches Vite's `server.fs.deny`,can't load "/private/etc/hosts": it's outside the folders a backend function can readimport.meta.resolvewarning[warn|vite|apps] Local execution on Node.js 20.19.4 can't provide `import.meta.resolve` inside the sandbox; …--inspectEROFS)Debugger listening on ws://127.0.0.1:…per callQA app and harness:
dev-qa.mjs,app/app/also holdspackage.json({ "name": "qa-app", "private": true, "type": "module" }), a.env(DD_API_KEY=from-dotenv),src/main.jsimportinggreet, andnode_modules/linkingviteand@datadog/vite-pluginto this checkout.node_modules/qa-probes({ "name": "qa-probes", "type": "module", "main": "index.js" }), since backend function code can't import Node built-ins.Manual QA in a Vite 8 app against a Datadog staging org, at
15442202{ "freshState": [ 1, 1, 1 ], "sandbox": { "success": true, "result": { "data": { "projectWrite": "refused: ERR_ACCESS_DENIED", "tempWrite": "ok: x", "spawn": "refused: ERR_ACCESS_DENIED", "envKeys": "none" } } }, "projectFileExists": false }Blast Radius
/__dd/executeAction.server.fs.strictisfalse.Out of Scope / Follow-ups
7 items deferred
.envwithfs.@importor a hand-made IPC request gets past them.loadhook.--allow-fs-read, and a comma in a readable path splits it.server.fs.denyalso refuses SSR imports such as a.pemread with?raw.node_modulessymlinksDocumentation
server.fs.allowandserver.fs.deny🤖 Generated with Claude Code