fix: harden subprocess env handling (scrub secrets, isolate browser sessions) - #59840
fix: harden subprocess env handling (scrub secrets, isolate browser sessions)#59840ai-ag2026 wants to merge 5 commits into
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing two real hardening gaps. The current main branch still writes unfiltered snapshots (tools/environments/base.py:502,650) and the browser helper Popen paths lack POSIX session isolation (tools/browser_tool.py:1105,2444).
Problems
- Blocking:
tools/environments/base.py:423filters values that Docker intentionally injects only duringinit_session.DockerEnvironment._run_bash()adds_init_env_argsonly whenlogin=True(tools/environments/docker.py:1066-1069), and its contract says subsequent commands rely on the snapshot (1012-1024). Explicit forwarded credentials would therefore disappear after initialization. - The PR CI run has two failing existing assertions in
tests/tools/test_base_environment.py(lines 219 and 236) that still search forexport -p >. tests/tools/test_browser_tool_process_isolation.py:26is a source-shape test, not behavioral coverage.
Suggested changes
- Preserve explicitly forwarded Docker env per command without writing it into the reusable snapshot, and cover that path end-to-end.
- Update the remaining assertions and replace the source-count test with mocked-Popen behavioral coverage for both call sites.
Automated hermes-sweeper review.
| @@ -396,7 +420,7 @@ def init_session(self): | |||
| _snap_tmp = shlex.quote(self._snapshot_path + ".tmp.") + "$BASHPID" | |||
| bootstrap = ( | |||
| f"umask 077\n" | |||
| f"export -p > {_snap_tmp}\n" | |||
| f"{_snapshot_export_command(_snap_tmp)}\n" | |||
There was a problem hiding this comment.
Blocking: Docker adds forwarded -e values only for init_session; later commands rely exclusively on this snapshot (tools/environments/docker.py:1012-1024,1066-1069). Filtering here removes explicitly forwarded credentials after the first command. Preserve them per docker exec without persisting them in the reusable snapshot.
| assert isinstance(extra["startupinfo"], FakeStartupInfo) | ||
| assert extra["startupinfo"].dwFlags == 0x100 | ||
| assert "start_new_session" not in extra | ||
|
|
There was a problem hiding this comment.
Please replace this source-count assertion with behavioral coverage that intercepts subprocess.Popen at both execution paths and verifies the kwargs. AGENTS.md prohibits tests that read source text because they do not validate runtime wiring.
|
Current head: The two active review findings are addressed on the current head:
Validation:
@teknium1 Fresh review requested on the current head. I have intentionally left the existing threads unresolved for maintainer readback. |
|
Current repaired head: Follow-up to the validation above: independent review found that the scrub pipeline could mask a failing Current-head validation:
@teknium1 Fresh review requested on |
(cherry picked from commit 1b5d0fedda9131f1b56876e0aa369a29d9356e08) (cherry picked from commit cd4eff10c6b4e4509ebce89a06b7c6372a68e015)
(cherry picked from commit 606e5b08621099f221ba9da3b0cb79b2e48fde56) (cherry picked from commit da6f0936134511d6d16b2159d540bf299c0306d1)
The secret-scrubbing snapshot rewrite changed export -p > $tmp.$BASHPID && mv -f $tmp.$BASHPID $snap to export -p | grep -Eiv <SECRET_RE> > $tmp.$BASHPID && mv -f $tmp.$BASHPID $snap The redirect now binds to grep, which runs in its own pipe subshell, so $BASHPID in the temp path expands to grep's PID while the caller's mv expands it to the outer shell PID. The names diverge, the mv finds nothing, and the env snapshot is never updated — exported vars stop persisting between commands (TestSnapshotEndToEnd: exported env / PATH / venv activation all regressed). Fix: wrap the pipe in a brace group so the redirect binds to the group (run by the current shell), keeping $BASHPID consistent with the mv. Secret scrubbing is preserved. Also update the TestWrapCommand/TestAtomicSnapshotWrite string assertions to expect the scrub pipe (export -p | grep ... >) instead of the pre-scrub direct redirect. tests/tools/test_base_environment.py + test_local_shell_init.py: 42/42 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dab33c0 to
ff99152
Compare
|
Bound to the child-process credential-inheritance class under #83565 (#83565) — same bug class, different surface. shared |
Two small, independent hardening fixes for subprocess/env handling:
Testing
pytest tests/tools/test_browser_tool_process_isolation.py tests/tools/test_init_session_cwd_respect.py→ 11 passed, on currentmain.