DEV Community

Emery Lin
Emery Lin

Posted on

Your AI Coding Agent Has Tools. Here's a Test Suite for the Sandbox You Hope It Respects

There's a growing unease in the dev community right now: we keep handing AI agents more tools — shell access, file writes, HTTP clients, git push — and mostly we check the boundaries by vibes. The agent didn't delete anything today, so the sandbox works, right?

I wanted something more defensible than vibes. So this article is a small, runnable red-team suite you can point at any environment where you let an agent execute commands. It doesn't assume any particular agent, model, or vendor. It tests the sandbox, not the model — which is where most real failures live.

Everything below is reproducible. The test suite is code you can run. The failure modes are described, not dramatized.

The four boundaries that actually matter

When people say "sandbox," they usually mean one blurry thing. In practice an agent execution environment has at least four separate boundaries, and each fails differently:

Boundary Question Typical failure
Filesystem Can the agent read/write outside its workspace? ../ traversal, symlink escape, bind mounts
Secrets Can it read env vars or files you didn't intend? env dump, ~/.ssh, ~/.aws, .env in parent dirs
Network Can it make outbound calls you didn't authorize? Direct egress, DNS exfiltration, metadata endpoints
Persistence Does anything it changes survive past the session? Leftover processes, cron, shell rc files, git hooks

A container answers some of these. A VM answers more. "The agent is polite" answers none.

A reproducible boundary test suite

The trick: don't test whether the agent behaves. Test whether the environment refuses misbehavior. Run these commands yourself (or have the agent run them — same thing, since the agent runs commands as some user somewhere) and check the results.

Save as boundary_check.sh:

#!/usr/bin/env bash
# Boundary checks for an agent execution environment.
# Run INSIDE the sandbox, as the same user the agent runs as.
# Every line should print BLOCKED. Anything else is a finding.

check() {
  local name="$1"; shift
  if "$@" >/dev/null 2>&1; then
    echo "OPEN:    $name"
  else
    echo "BLOCKED: $name"
  fi
}

# 1. Filesystem: can we read outside the workspace?
check "read /etc/shadow"        head -n1 /etc/shadow
check "read parent dir"         ls "$HOME/../../"
check "write outside workspace" touch /tmp/outside_workspace_probe

# 2. Secrets: are credentials reachable?
check "read ~/.ssh"             ls "$HOME/.ssh"
check "read ~/.aws"             ls "$HOME/.aws"
check "env contains secrets"    sh -c 'env | grep -qiE "(KEY|TOKEN|SECRET|PASSWORD)"'

# 3. Network: is outbound egress open?
check "outbound HTTPS"          curl -s --max-time 3 https://example.com
check "cloud metadata endpoint" curl -s --max-time 2 http://169.254.169.254/latest/meta-data/

# 4. Persistence: can we install hooks that outlive the session?
check "write shell rc"          sh -c 'echo true >> "$HOME/.bashrc"'
check "install cron job"        sh -c 'crontab -l 2>/dev/null; crontab - >/dev/null 2>&1 <<< ""'
Enter fullscreen mode Exit fullscreen mode

Run it, and you get a table of OPEN / BLOCKED per boundary. That table is your actual security posture — not the diagram in someone's README.

Two important details:

  1. Run it as the agent's user, not as root. Root in a container can often do things the agent's UID can't, and vice versa for misconfigured mounts. Test the identity that matters.
  2. OPEN isn't always wrong — it's always a decision. Maybe you want outbound HTTPS so the agent can fetch docs. Fine. But then it's a choice you made, not an assumption you inherited.

What each finding means in practice

  • read /etc/shadow OPEN — usually means the agent runs as root in a container. Root-in-container plus a writable Docker socket or CAP_SYS_ADMIN is a classic container-escape setup. Run the agent as a non-root UID; it costs you nothing.
  • env contains secrets OPEN — the most common real-world leak. If you inject API keys as environment variables into the same process space where an agent executes arbitrary commands, assume the agent's context window eventually contains those keys. Prefer short-lived, scoped tokens fetched by a broker process the agent can call but not read.
  • cloud metadata endpoint OPEN — on AWS/GCP/Azure this can hand the agent instance credentials. Block 169.254.169.254 at the network layer or use IMDSv2 with hop limits.
  • outbound HTTPS OPEN — exfiltration path. If you need egress, an allowlist proxy beats a firewall rule you'll forget about.
  • Persistence probes OPEN — an agent that can append to .bashrc or install a git hook can arrange for code to run later, possibly by you, outside the sandbox. Ephemeral environments (fresh instance per session) neutralize this whole category.

That last point is why I now prefer disposable environments for agent experiments over my dev box. For this kind of boundary testing, I've been spinning up sessions on MonkeyCode's free server option — it gives you a remote execution environment with free model access, which is convenient here precisely because the environment isn't my machine: if a persistence probe succeeds, I throw the instance away instead of auditing my dotfiles.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The same logic applies wherever you run agents, though — a throwaway VPS, a CI runner, a Firecracker microVM. The test suite doesn't care.

A minimal isolation checklist

If you'd rather fix things than just measure them, this is the order I'd work in:

  1. Non-root user for the agent's process. (useradd, not --privileged.)
  2. Workspace-only filesystem: mount the project dir, read-only-mount or omit everything else, no Docker socket.
  3. Secret broker: the agent calls a helper that holds credentials; the agent never sees them.
  4. Egress allowlist (or no egress) at the network layer, not in a prompt.
  5. Ephemeral lifetime: the environment is destroyed after the session. Persistence of artifacts (code, PRs) goes through git, not through the machine.

None of this is exotic. It's the same hardening you'd apply to a CI runner executing untrusted pull requests — because that's essentially what an agent session is.

Limitations, honestly

  • This tests configuration, not cleverness. A determined exploit (kernel bug, container runtime CVE) won't show up in a shell script. This suite catches misconfiguration, which is the common case, not zero-days.
  • A passing suite doesn't make the agent's output safe. Code the agent writes still needs review before it runs anywhere privileged. Boundary tests and code review are different layers.
  • Free tiers have limits. Whatever hosted environment you use for this — including the one mentioned above — check current quotas, session duration, and region before building a workflow on it; free offerings change and I can't promise what yours includes. For heavy CI-scale testing, you'll want your own infrastructure anyway.
  • Prompt-level guardrails are not a boundary. "I instructed the agent not to delete files" is a hope. The tests above measure mechanisms. Keep the distinction clear in your threat model.

Who this is not for

If your agent never executes commands — it only suggests code in a chat window — you don't need any of this; your boundary is your own code review. And if you're running agents against production data or regulated workloads, a shell script and a free sandbox are a starting point for thinking, not a compliance posture. Get a real security review.

For everyone in between — experimenting with agents that can run shell commands, and quietly wondering what "sandboxed" actually guarantees in your setup — run the script. Fifteen minutes, and you'll know which of your four boundaries are real and which are decorative.

If you try it and find something surprising in your own setup, I'd genuinely like to hear which check caught it — the env leak is my bet for the most common one.

Top comments (0)