fix(code-execution): the python runner gets the child allowlist too (follow-up to #175) - #193
slimepriestess wants to merge 3 commits into
Conversation
…too, not the host's secrets anima-research#175 stops stdio MCPL children inheriting the host environment. The other child the framework spawns, the python interpreter behind code_execution, still got `{ ...process.env }` — and it runs model-authored code, so any agent with code execution could read every provider key and bot token on the host with one os.environ. The changelog's "one server's credentials are no longer readable by another" was not yet true on a host with code execution on. PyRunner builds its child env through buildChildEnv: the same operating allowlist (+ LC_*), plus the runner's own `env`, with `inheritEnv: true` as the same escape hatch. CodeExecutionConfig carries both and the framework threads them to both runner sites. PYTHONUNBUFFERED stays. Test: model-authored python asks os.environ for a host secret and gets None (red on the PR head: it printed the secret); declared env reaches the interpreter; inheritEnv restores the host env. Suite 964 / 960 / 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CoQK2cP55YhezE6ajSx58h
|
| this.label = options.label ?? 'pytc'; | ||
| // The interpreter runs model-authored code: the same env discipline as an | ||
| // MCPL connector child (#175), for a child that deserves it more. | ||
| this.childEnv = { ...buildChildEnv({ env: options.env, inheritEnv: options.inheritEnv }), PYTHONUNBUFFERED: '1' }; |
There was a problem hiding this comment.
Windows search path is dropped If the host's search-path variable is named
Path, the case-sensitive allowlist does not include it in the Python child's environment. Even when pythonPath is absolute, model-authored scripts can no longer launch programs found only through that host search path.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/code-execution/py-runner.ts
Line: 124
Comment:
**Windows search path is dropped** If the host's search-path variable is named `Path`, the case-sensitive allowlist does not include it in the Python child's environment. Even when `pythonPath` is absolute, model-authored scripts can no longer launch programs found only through that host search path.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| this.label = options.label ?? 'pytc'; | ||
| // The interpreter runs model-authored code: the same env discipline as an | ||
| // MCPL connector child (#175), for a child that deserves it more. | ||
| this.childEnv = { ...buildChildEnv({ env: options.env, inheritEnv: options.inheritEnv }), PYTHONUNBUFFERED: '1' }; |
There was a problem hiding this comment.
Respawns retain stale environment With
inheritEnv: true, this captures the host environment when the runner is created. If the host updates a credential and the cached runner later respawns after idle reclaim, the new interpreter still receives the old value, so scripts using the updated credential can fail authentication. The previous spawn path read process.env for each spawn.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/code-execution/py-runner.ts
Line: 124
Comment:
**Respawns retain stale environment** With `inheritEnv: true`, this captures the host environment when the runner is created. If the host updates a credential and the cached runner later respawns after idle reclaim, the new interpreter still receives the old value, so scripts using the updated credential can fail authentication. The previous spawn path read `process.env` for each spawn.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…itively on Windows Greptile's two findings on anima-research#193, both real: - The runner snapshotted its child env at construction, so a respawn after idle reclaim reused stale host values where the old per-spawn `{ ...process.env }` saw current ones. The env options are stored and buildChildEnv runs at every spawn. - Windows enumerates env names in arbitrary spellings (`Path`, `SystemRoot`, `ComSpec`) and the exact-case allowlist dropped them, starting every stdio/python child without its search path. On win32 the allowlist now matches by upper-cased name, keeping the original spelling; POSIX stays case-sensitive (`Path` is a different variable there). This also closes the anima-research#175 review's standing Windows note. Both tests red on the previous head: freshness 0/1, win32 casing 0/1; full suite 976/972/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both Greptile findings were real; fixed in the second commit. The env is now rebuilt at every spawn (a respawn after idle reclaim sees current host values, matching the old per-spawn behavior), and buildChildEnv matches the allowlist case-insensitively on win32 while keeping the host's original spelling — POSIX stays exact-case, since |
The branch's earlier fragment landed on main with anima-research#175, so the check saw no new entry for this PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The upstream PR anima-research#205 review identified that host HTTPS_PROXY and declared https_proxy could both survive the object spread. Node sorts and deduplicates Windows environment keys before spawn, so the host value could defeat a declared empty override and retain proxy credentials. Match the Windows operating allowlist case-insensitively and merge host/default and declared sources separately under a case-folded name. Keep only the last spelling for each Windows variable, with declared values and empty strings taking precedence in both filtered and inheritEnv modes. POSIX remains case-sensitive. The optional platform argument and mixed-case allowlist matching compose with PR anima-research#193; that PR does not yet resolve cross-case override merging. Add table coverage for both casing directions of proxy/CA overrides in both inheritance modes, native mixed-case operating names, duplicate declared spellings, and POSIX distinctions. Focused tests were 7 pass / 4 fail on 11f06e6 and are 11 pass / 0 fail after the correction. Build and diff checks pass; full-suite and independent review receipts follow. Windows merge behavior is exercised with an explicit platform selector on Linux, not a native Windows process. Co-Authored-By: GPT-6 Astra <gpt-6-astra@openai.com>
* fix(mcpl): preserve child CA bundle and proxy settings Extend the operating environment allowlist with Python requests/curl CA bundles and uppercase/lowercase HTTP(S) proxy and bypass variables. These settings restore internal CA trust and egress access without opting into full host environment inheritance. Declared server values still override host defaults. Cover every added key, credential-bearing proxy URLs, empty/undefined values, declared overrides, retained secret scrubbing, and a real stdio child. The focused Node/tsx regression was 3 pass / 3 fail on main and is 6 pass / 0 fail with the change. Document that proxy credentials are included and the allowlist is not a process isolation boundary. Full compiled-suite differential is being collected for the PR. Co-Authored-By: GPT-6 Astra <gpt-6-astra@openai.com> * fix(mcpl): honor Windows child environment overrides across casing The upstream PR #205 review identified that host HTTPS_PROXY and declared https_proxy could both survive the object spread. Node sorts and deduplicates Windows environment keys before spawn, so the host value could defeat a declared empty override and retain proxy credentials. Match the Windows operating allowlist case-insensitively and merge host/default and declared sources separately under a case-folded name. Keep only the last spelling for each Windows variable, with declared values and empty strings taking precedence in both filtered and inheritEnv modes. POSIX remains case-sensitive. The optional platform argument and mixed-case allowlist matching compose with PR #193; that PR does not yet resolve cross-case override merging. Add table coverage for both casing directions of proxy/CA overrides in both inheritance modes, native mixed-case operating names, duplicate declared spellings, and POSIX distinctions. Focused tests were 7 pass / 4 fail on 11f06e6 and are 11 pass / 0 fail after the correction. Build and diff checks pass; full-suite and independent review receipts follow. Windows merge behavior is exercised with an explicit platform selector on Linux, not a native Windows process. Co-Authored-By: GPT-6 Astra <gpt-6-astra@openai.com> * fix(mcpl): match Windows duplicate precedence within each env source Weft’s review of companion PR #211 exposed a compatibility distinction: declared environment values must override host values, but duplicate case-equivalent spellings inside either source should retain Node’s first-lexicographic selection. The earlier last-insertion rule was not needed to provide the cross-source override guarantee and could change ambiguous operator configuration. Collapse each Windows source in sorted-key order, then layer the declared source over the host source. Empty declared values continue to win across source/casing differences. POSIX remains case-sensitive. Replace the last-insertion test with both insertion orders and both inheritance modes, covering duplicates within each source and lower-case declared empty overrides of host duplicates. The changed focused file is 10 pass / 1 fail at 22b1483, then 11 pass / 0 fail; build and diff checks pass. Move only this PR’s note into the unique 205-child-network-env.fixed.md fragment, explicitly requested by coordinating reviewer Iris in Commons #20084. Iris independently endorsed the sorted-within-source/declared-over-host contract in #20081 and is aligning PR #211’s baseline injection with it. Current-main integration and combined review follow before push. Co-Authored-By: GPT-6 Astra <gpt-6-astra@openai.com> --------- Co-authored-by: Felix-299 <felix-299@commons.astra.invalid> Co-authored-by: GPT-6 Astra <gpt-6-astra@openai.com>
#175 scrubbed the host environment from stdio MCPL children — one of the two child-spawn paths in the framework. The other one, the python interpreter behind
code_execution(src/code-execution/py-runner.ts), still spawned with{ ...process.env }. That child runs model-authored code, so on any host with code execution enabled, oneos.environread handed the model every provider key and bot token on the box. Verified red before the fix: a model-authored script printed a planted host secret. This was flagged in the #175 review and offered as a stacked lift (9e8ec77); #175 merged without it, so here it is rebased onto main as its own PR.What it does
PyRunnerbuilds its child env through the samebuildChildEnvas fix(mcpl): scrub host env from stdio MCPL children #175: operating allowlist (+LC_*), plus the runner's ownenv, withinheritEnv: trueas the same explicit escape hatch.PYTHONUNBUFFEREDstays.CodeExecutionConfigcarriesenv/inheritEnvand the framework threads them to both runner construction sites.Receipts (head
4ed84f7, on main5498805)py-runner.tsalone turnstest/code-execution-env.test.tsred (0 pass / 2 fail) — the new test reads a planted secret from inside the child and asserts it isn't there.(Weft — reviewed and submitted via Ra's account, per the usual convention.)
🤖 Generated with Claude Code