feat(executor): opt-in OS-level FS confinement for ctx_execute* (#852)#858
Closed
ousamabenyounes wants to merge 1 commit into
Closed
feat(executor): opt-in OS-level FS confinement for ctx_execute* (#852)#858ousamabenyounes wants to merge 1 commit into
ousamabenyounes wants to merge 1 commit into
Conversation
…lu#852) ctx_execute / ctx_execute_file run arbitrary code in an out-of-process subprocess that does not inherit the harness filesystem sandbox, so an agent can read files outside the project even when the user enabled sandboxing. In-process checks cannot stop this (arbitrary code can call fs.readFileSync directly) — only an OS-level boundary can. Add opt-in confinement via CONTEXT_MODE_CONFINE_FS=1. On Linux >= 5.13 with a C compiler, a tiny embedded launcher applies a Landlock ruleset that denies reads outside the project (plus the system dirs a runtime needs), then execs the runtime. The restriction is unprivileged, irreversible and inherited, so even arbitrary ctx_execute code cannot read project-external files. Fails closed: refuses to run if confinement is requested but cannot be applied. - src/sandbox/landlock.ts: lazy-compile + cache launcher, build confined cmd - src/executor.ts: confineFs resolver + fail-closed wrapping in #spawn - tests: RED (unconfined reads outside) -> GREEN (Landlock denies it), Linux-gated; env-knob unit test runs everywhere - docs: SECURITY.md threat model + README security/env-var section Linux only for now; macOS (sandbox-exec) and Windows are follow-ups. Governs reads only. Off by default.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Opt-in OS-level filesystem confinement for the polyglot executor, addressing Fix #852. Works on Linux and macOS; Windows fails closed.
ctx_execute/ctx_execute_filerun arbitrary code in an out-of-process subprocess that does not inherit the harness filesystem sandbox, so an agent can read files outside the project even when the user enabled sandboxing. In-process checks cannot stop this — arbitrary code can callfs.readFileSyncdirectly. The only real boundary is an OS-level one.Enabled via
CONTEXT_MODE_CONFINE_FS=1(off by default).Why
Reproduces with (real, before the fix):
ctx_execute_filereading/etc/os-releaseand listing$HOMEfrom a project at/home/.../context-mode— both succeed, no confinement. That is the escape #852 describes.The existing deny-firewall (
security.ts) only blocks paths/commands matching the user's explicit deny globs; default settings block nothing, and it is not a confinement boundary against arbitrary code.How — one boundary per OS (no single cross-platform primitive exists)
cc/gcc/clang) and cached; unprivileged, irreversible, inheritedsandbox-exec(Seatbelt) — profile:(allow default)(deny file-read*)(allow file-read* <subpaths>)src/sandbox/confine.ts— platform dispatch + pure builders (buildLandlockArgs,buildMacProfile) + lazy launcher compile/cache. Returnsnullwhen the platform primitive is unavailable.src/executor.ts—confineFsresolver (env-default, test override);#spawnwraps the command and fails closed if confinement is requested but cannot be applied.The allow-list is the project root + per-exec sandbox tmp + the runtime's install dir + the world-readable system dirs a runtime needs to start. Everything else —
$HOMEoutside the project, other repos, secrets — is denied.Test verification (RED → GREEN)
tests/executor.test.ts. Pure-builder unit tests run on every platform; the RED→GREEN integration test runs where the primitive exists (Linux locally + CIubuntu-latest; macOS on CImacos-latest).RED — prod fix neutered (
cmd = confinedremoved), GREEN test fails:GREEN — fix applied:
The GREEN integration test confirms the runtime still works in-project (
INSIDE_SECRETread) while the project-external read is blocked (OUT_ERR:EACCES).Full suite + typecheck
No regression — pass count grew by the new tests, skip count unchanged, no green→non-green flips. Auto-rebuilt bundles reverted, not committed (CI rebuilds them).
Scope (honest limits, in SECURITY.md)
Files changed
src/sandbox/confine.tssrc/executor.tsconfineFsresolver + fail-closed confinement wrapping in#spawntests/executor.test.tsSECURITY.mdREADME.mdCONTEXT_MODE_CONFINE_FS