You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reviewed the gh-aw-firewall codebase (src/, containers/) plus the most recent firewall escape-test run (Secret Digger, run 29286879560). Overall security posture is strong: the escape test showed the agent correctly refused a prompt-injection exfiltration attempt (no code execution occurred, so no firewall bypass was actually attempted), and the architecture layers defense-in-depth (iptables DNAT + Squid domain ACL + capability drop + seccomp + selective bind mounts). No critical vulnerabilities were found. A handful of medium/low hardening opportunities are noted below.
🔍 Findings from Firewall Escape Test
/tmp/gh-aw/escape-test-summary.txt shows the "Secret Digger (Copilot)" workflow run detected a prompt-injection attempt instructing the agent to scan the CI runner for secrets/credentials/env vars and exfiltrate them via a GitHub issue. The agent's noop handler correctly refused: "Refused prompt injection attack... prohibited by security policy. No investigation was performed." Threat detection flagged this as warning/threat_detected and it was logged to tracking issue #6205. This is a successful defense — the injected instruction never reached firewall-bypass tooling, so it doesn't exercise AWF's network controls directly, but it confirms the outer prompt-injection/task-refusal guardrail is functioning.
🛡️ Architecture Security Analysis
Network Security — containers/agent/setup-iptables.sh (536 lines): DNAT rules redirect ports 80/443 to Squid (172.30.0.10:3128) (lines 405-406); dangerous ports get rate-limited LOG + DROP (lines 469-482); final OUTPUT -p tcp -j DROP / -p udp -j DROP is a default-deny fallthrough. DNS is restricted to explicit trusted servers plus Docker's embedded resolver (127.0.0.11), consistent with docs/environment.md. src/host-iptables.ts is a thin 10-line wrapper (delegates most logic to the in-container script) — reviewed and found no injection surface since it doesn't interpolate untrusted user input directly into shell strings beyond IPs/ports validated elsewhere.
Container Security — Capability handling is well-modeled: src/services/service-security.ts centralizes cap_drop: ['ALL'] + no-new-privileges:true for sidecars (Squid, api-proxy, doh-proxy, cli-proxy all drop ALL per their *.test.ts specs). The agent container is a deliberate, documented exception: it needs SYS_CHROOT/SYS_ADMIN transiently for chroot + procfs mount setup, and containers/agent/entrypoint.sh:426-440 explicitly drops both via capsh (determine_capabilities_to_drop()) before user code executes. NET_ADMIN/NET_RAW are never granted to the agent — confirmed via agent-service-build.test.ts:28,101,107 — and are isolated to the short-lived awf-iptables-init container (cap_add: ['NET_ADMIN','NET_RAW'], cap_drop: ['ALL'], test line 117-118). A seccomp profile (containers/agent/seccomp-profile.json) uses SCMP_ACT_ERRNO default-deny with an explicit allowlist. UID/GID 0 (root) is explicitly rejected in entrypoint.sh:46-53.
Domain Validation — src/domain-patterns.ts converts wildcard patterns to Squid dstdom_regex ACLs using a bounded character class ([a-zA-Z0-9.-]*) instead of .*, with an explicit code comment citing ReDoS prevention (lines 66-71). This is good practice — unbounded backtracking regex from user-supplied domain patterns is a known DoS vector, and this code proactively avoids it.
Input Validation / Injection Surface — Shell invocation across src/ consistently uses execa/execa.sync with argument arrays (e.g., container-lifecycle.ts, artifact-preservation.ts, config-writer.ts) rather than string-interpolated shell commands, which avoids classic shell-injection via child_process.exec. No raw exec()/shell: true usage was found in the grep of src/.
⚠️ Threat Model (STRIDE)
Category
Threat
Evidence
Likelihood
Impact
Spoofing
Malicious process spoofs Squid IP to bypass ACL
Fixed IP topology (172.30.0.10) inside a private Docker network reduces spoofing surface
Low
Medium
Tampering
Agent modifies iptables rules mid-run to open blocked ports
NET_ADMIN withheld from agent container (confirmed test)
Low
High
Repudiation
Agent actions untraceable
Squid firewall_detailed logformat + iptables LOG rules with --log-uid (setup-iptables.sh:473,479,481)
Low
Low
Information Disclosure
Prompt-injection induces secret exfiltration
Demonstrated attempt in escape test; blocked by agent-level refusal, not network layer
Medium (attempts occur) / Low (success)
High if successful
Denial of Service
ReDoS via crafted --allow-domains wildcard pattern
Mitigated by bounded char class in domain-patterns.ts
Low
Medium
Elevation of Privilege
Agent escapes chroot/uses residual SYS_CHROOT/SYS_ADMIN after startup
Capabilities dropped via capsh before user command runs (entrypoint.sh:426-440)
Low
High
🎯 Attack Surface Map
Network egress — containers/agent/setup-iptables.sh (DNAT + default-deny). Protected by domain ACL + rate-limited logging. Weakness: relies on iptables being correctly re-applied every run; a race between container start and ready signal file could theoretically allow early traffic (mitigated by depends_on/readiness wait, not independently re-verified here).
Domain/ACL parsing — src/domain-patterns.ts:66-80. Protected by bounded regex; weakness would only arise if wildcard-to-regex conversion allowed unescaped metacharacters — not observed.
Container capability surface — containers/agent/entrypoint.sh. Protected by explicit cap-drop before user code; weakness is the brief privileged window during chroot/procfs setup (by design, time-boxed).
CLI input handling — src/cli.ts + execa array-based invocation. Protected against shell injection; not fully audited for path traversal on user-supplied mount paths in this pass.
Prompt-injection / agent task boundary — outside AWF's direct scope but adjacent; the escape-test evidence shows this is the most actively probed surface, handled by the "noop"/refusal policy rather than firewall enforcement.
No high/critical npm audit findings surfaced (metadata section returned empty/no vulnerabilities in this pass).
✅ Recommendations
Critical: None identified this cycle.
High: None identified this cycle.
Medium: Add automated regression test asserting the agent container's cap set stays exactly {SYS_CHROOT, SYS_ADMIN} pre-drop and empty post-drop, to guard against future refactors accidentally widening it permanently. Consider a periodic re-verification that the awf-iptables-init "ready" signal file gating fully closes the pre-firewall window (i.e., no user command byte can execute before DNAT rules are live).
Low: Expand docs/INTEGRATION-TESTS.md gap analysis to explicitly include a test case that exercises a real network-egress bypass attempt (not just prompt-injection refusal) so escape-test coverage includes both agent-policy and firewall-layer defenses.
Low: Continue running npm audit in CI on a schedule (not only ad hoc) to catch newly disclosed dependency CVEs in execa, js-yaml, etc.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
📊 Executive Summary
Reviewed the gh-aw-firewall codebase (
src/,containers/) plus the most recent firewall escape-test run (Secret Digger, run29286879560). Overall security posture is strong: the escape test showed the agent correctly refused a prompt-injection exfiltration attempt (no code execution occurred, so no firewall bypass was actually attempted), and the architecture layers defense-in-depth (iptables DNAT + Squid domain ACL + capability drop + seccomp + selective bind mounts). No critical vulnerabilities were found. A handful of medium/low hardening opportunities are noted below.🔍 Findings from Firewall Escape Test
/tmp/gh-aw/escape-test-summary.txtshows the "Secret Digger (Copilot)" workflow run detected a prompt-injection attempt instructing the agent to scan the CI runner for secrets/credentials/env vars and exfiltrate them via a GitHub issue. The agent'snoophandler correctly refused: "Refused prompt injection attack... prohibited by security policy. No investigation was performed." Threat detection flagged this aswarning/threat_detectedand it was logged to tracking issue #6205. This is a successful defense — the injected instruction never reached firewall-bypass tooling, so it doesn't exercise AWF's network controls directly, but it confirms the outer prompt-injection/task-refusal guardrail is functioning.🛡️ Architecture Security Analysis
Network Security —
containers/agent/setup-iptables.sh(536 lines): DNAT rules redirect ports 80/443 to Squid (172.30.0.10:3128) (lines 405-406); dangerous ports get rate-limited LOG + DROP (lines 469-482); finalOUTPUT -p tcp -j DROP/-p udp -j DROPis a default-deny fallthrough. DNS is restricted to explicit trusted servers plus Docker's embedded resolver (127.0.0.11), consistent withdocs/environment.md.src/host-iptables.tsis a thin 10-line wrapper (delegates most logic to the in-container script) — reviewed and found no injection surface since it doesn't interpolate untrusted user input directly into shell strings beyond IPs/ports validated elsewhere.Container Security — Capability handling is well-modeled:
src/services/service-security.tscentralizescap_drop: ['ALL']+no-new-privileges:truefor sidecars (Squid, api-proxy, doh-proxy, cli-proxy all drop ALL per their*.test.tsspecs). The agent container is a deliberate, documented exception: it needsSYS_CHROOT/SYS_ADMINtransiently for chroot + procfs mount setup, andcontainers/agent/entrypoint.sh:426-440explicitly drops both viacapsh(determine_capabilities_to_drop()) before user code executes.NET_ADMIN/NET_RAWare never granted to the agent — confirmed viaagent-service-build.test.ts:28,101,107— and are isolated to the short-livedawf-iptables-initcontainer (cap_add: ['NET_ADMIN','NET_RAW'],cap_drop: ['ALL'], test line 117-118). A seccomp profile (containers/agent/seccomp-profile.json) usesSCMP_ACT_ERRNOdefault-deny with an explicit allowlist. UID/GID 0 (root) is explicitly rejected inentrypoint.sh:46-53.Domain Validation —
src/domain-patterns.tsconverts wildcard patterns to Squiddstdom_regexACLs using a bounded character class ([a-zA-Z0-9.-]*) instead of.*, with an explicit code comment citing ReDoS prevention (lines 66-71). This is good practice — unbounded backtracking regex from user-supplied domain patterns is a known DoS vector, and this code proactively avoids it.Input Validation / Injection Surface — Shell invocation across
src/consistently usesexeca/execa.syncwith argument arrays (e.g.,container-lifecycle.ts,artifact-preservation.ts,config-writer.ts) rather than string-interpolated shell commands, which avoids classic shell-injection viachild_process.exec. No rawexec()/shell: trueusage was found in the grep ofsrc/.172.30.0.10) inside a private Docker network reduces spoofing surfaceNET_ADMINwithheld from agent container (confirmed test)firewall_detailedlogformat + iptables LOG rules with--log-uid(setup-iptables.sh:473,479,481)--allow-domainswildcard patterndomain-patterns.tsSYS_CHROOT/SYS_ADMINafter startupcapshbefore user command runs (entrypoint.sh:426-440)🎯 Attack Surface Map
containers/agent/setup-iptables.sh(DNAT + default-deny). Protected by domain ACL + rate-limited logging. Weakness: relies on iptables being correctly re-applied every run; a race between container start andreadysignal file could theoretically allow early traffic (mitigated bydepends_on/readiness wait, not independently re-verified here).src/domain-patterns.ts:66-80. Protected by bounded regex; weakness would only arise if wildcard-to-regex conversion allowed unescaped metacharacters — not observed.containers/agent/entrypoint.sh. Protected by explicit cap-drop before user code; weakness is the brief privileged window during chroot/procfs setup (by design, time-boxed).src/cli.ts+execaarray-based invocation. Protected against shell injection; not fully audited for path traversal on user-supplied mount paths in this pass.📋 Evidence Collection
Commands run
No high/critical npm audit findings surfaced (metadata section returned empty/no vulnerabilities in this pass).
✅ Recommendations
{SYS_CHROOT, SYS_ADMIN}pre-drop and empty post-drop, to guard against future refactors accidentally widening it permanently. Consider a periodic re-verification that theawf-iptables-init"ready" signal file gating fully closes the pre-firewall window (i.e., no user command byte can execute before DNAT rules are live).docs/INTEGRATION-TESTS.mdgap analysis to explicitly include a test case that exercises a real network-egress bypass attempt (not just prompt-injection refusal) so escape-test coverage includes both agent-policy and firewall-layer defenses.npm auditin CI on a schedule (not only ad hoc) to catch newly disclosed dependency CVEs inexeca,js-yaml, etc.📈 Security Metrics
setup-iptables.sh(536 lines),entrypoint.sh,domain-patterns.ts(137 lines),service-security.ts,host-iptables.ts, seccomp profile.Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
registry.npmjs.orgTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
All reactions