An agent can run in a container and still have more authority than you intended.
The usual mistake is treating "the process is in a container" as equivalent to "the agent is isolated." Those are different claims. The process may still inherit credentials, reach a metadata endpoint, talk to a host socket, reuse a browser profile, or leave child processes behind after the run ends.
This article gives you a repeatable escape-path test for coding agents, OpenClaw-style browser workers, and local automation that may execute untrusted or model-generated steps.
Start with an explicit boundary
Write down the boundary before choosing tools:
| Surface | Allowed | Denied | Evidence |
|---|---|---|---|
| Filesystem | disposable workspace | host home, SSH keys, cloud credentials | mount table and file probes |
| Network | approved API hosts | metadata, private ranges, arbitrary egress | connection log |
| Processes | one worker tree | host PID namespace, detached children | process snapshot after teardown |
| Identity | one short-lived run identity | developer shell identity | environment and token audit |
| Browser | fresh profile per run | personal profile, saved cookies | profile path and cookie check |
If you cannot state the allowed side and the denied side, you cannot verify the boundary.
The seven escape paths
Test these independently. A passing container health check does not cover them.
- Credential inheritance. Check environment variables, mounted files, SSH agents, cloud metadata, and credential-helper sockets.
- Filesystem reachability. Probe parent directories, mounted volumes, proc, sys, and temporary files from previous runs.
- Network reachability. Try DNS, private IP ranges, link-local metadata addresses, Unix sockets, and direct-IP connections that bypass hostname rules.
- Process escape. Start a child process, daemonize it, close the parent, and verify that teardown removes the whole process group.
- Browser reuse. Confirm that a new run cannot see cookies, storage, extensions, downloads, or a debugging port from another profile.
- Control-plane confusion. Ensure the supervisor cannot be invoked through the same tool path it is meant to supervise.
- Evidence loss. Preserve the command, decision, target, run ID, and result for every denied probe. A silent deny is hard to audit.
A minimal probe contract
Make every probe return a structured result instead of a shell exit code alone:
{
"probe": "credential-file",
"target": "home/.ssh/id_ed25519",
"decision": "DENY",
"observed": "ENOENT",
"run_id": "test-2026-08-08-001",
"evidence": "sha256:..."
}
Use DENY, ALLOW, and UNKNOWN as separate states. UNKNOWN means the test could not establish the boundary and should fail closed for a production rollout.
Run the probes from inside the worker
The test should execute with the same UID, mounts, environment, network policy, and browser launcher as the real agent.
set -eu
printf '%s\n' '== identity =='
id
printf '%s\n' '== mounts =='
cat /proc/self/mountinfo | sha256sum
printf '%s\n' '== sensitive environment names =='
env | grep -E 'AWS_|AZURE_|GCP_|SSH_|TOKEN|SECRET|COOKIE' || true
printf '%s\n' '== likely credential paths =='
for p in home/.ssh home/.aws /var/run/secrets /run/host-services; do
test -e "$p" && echo "VISIBLE $p" || echo "DENIED $p"
done
printf '%s\n' '== process tree =='
ps -eo pid,ppid,pgid,comm
Do not print secret values. Record only presence, ownership, permissions, and a digest of non-sensitive evidence.
Test network policy as a matrix
A single request to the public internet proves very little. Use a matrix with expected outcomes:
| Target | Expected |
|---|---|
| approved API hostname | ALLOW |
| its resolved public IP | ALLOW or explicit DENY, by policy |
| loopback addresses | DENY |
| RFC1918 private ranges | DENY unless explicitly required |
| link-local metadata address | DENY |
| arbitrary DNS name | DENY or policy decision |
| host Unix socket | DENY |
Log both the requested target and the resolved target. Otherwise a hostname allowlist can hide a DNS rebinding or direct-IP gap.
Teardown is part of isolation
The boundary is not proven when the command returns. Inject a failure after each of these points:
- after a child process starts
- after a browser launches
- after a credential is mounted
- after a network lease is created
- after the agent is killed but before the supervisor records completion
Then verify that the process group, browser profile, temporary credentials, network lease, and queued tool calls are gone. A restart must not inherit a half-cleaned run.
The result you want is not just an exit code of zero. It is a teardown record with the run ID, surviving PIDs, remaining mounts, open sockets, browser profile path, and cleanup decision.
A practical rollout checklist
Before allowing an agent to handle real repositories or accounts:
- [ ] No developer credentials are inherited by default.
- [ ] The workspace is disposable and its mount list is recorded.
- [ ] Network policy is tested by hostname, resolved IP, and private-range probes.
- [ ] The worker and every child process share a tracked process group.
- [ ] Browser state is fresh, scoped, and destroyed after the run.
- [ ] Denials produce evidence without exposing secrets.
- [ ] UNKNOWN blocks promotion rather than being counted as a pass.
- [ ] Failure injection proves cleanup after crashes, timeouts, and supervisor restarts.
The useful question is not "Which sandbox product should I choose?" It is "Which authority can this agent still reach, and what evidence proves it cannot reach the rest?" That question produces a boundary you can review, reproduce, and rebuild.
If this control-plane approach is useful, follow for more implementation-focused notes on agent reliability, recovery, and deployment.
Top comments (0)