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
A comprehensive, evidence-based security review of the gh-aw-firewall repository was conducted on 2026-03-06. The review analysed 3,009 lines of security-critical code across the iptables management layer, Squid proxy configuration, container entrypoint, domain-pattern validation, and the one-shot-token LD_PRELOAD library.
Overall posture: Strong. The defence-in-depth architecture (host iptables → Squid ACL → capability dropping → token isolation) is well-designed. Three low-to-medium findings were identified; none allow remote exploitation without physical access to the CLI flags. No critical vulnerabilities were found, and the npm dependency audit returned 0 vulnerabilities.
🔍 Findings from Previous Security Runs
No recent runs of firewall-escape-test or secret-digger workflows were reachable via the agentic-workflow log API during this run (tool returned exit status 1). Analysis relies solely on static code review and local command evidence.
DNS exfiltration is prevented: only explicitly listed trusted servers (default 8.8.8.8, 8.8.4.4) can receive port-53 traffic; all other UDP is REJECTed ([FW_BLOCKED_UDP] prefix).
IPv6 fall-through is blocked: when ip6tables is unavailable, IPv6 is disabled via sysctl net.ipv6.conf.all.disable_ipv6=1 (src/host-iptables.ts:96–100, containers/agent/setup-iptables.sh:33–37).
Dangerous port list is enforced at both Squid (L7) and iptables NAT (L3/L4) levels.
// src/host-iptables.ts:395-404// 3. Allow essential ICMPv6 (required for IPv6 functionality)awaitexeca('ip6tables',['-t','filter','-A',CHAIN_NAME_V6,'-p','ipv6-icmp','-j','ACCEPT',]);
When the user specifies IPv6 DNS servers (e.g., --dns-servers 2001:4860:4860::8888), the IPv6 chain is created and ALL ICMPv6 message types are allowed. This includes:
Type 128/129 (echo request/reply) — usable for ICMP tunnel exfiltration (ping-tunnel, icmptunnel, etc.)
Type 133-137 (Neighbour/Router Discovery) — could allow rogue RA injection
ICMP tunneling tools can carry arbitrary payloads over ICMP echo, completely bypassing Squid domain filtering. The risk is limited to networks that route IPv6, but the control should be tightened.
Recommended fix — replace the blanket accept with specific types:
// Allow only essential ICMPv6 types (NOT echo request/reply which enable tunneling)constessentialIcmpv6Types=[1,// Destination Unreachable2,// Packet Too Big (required for PMTU)3,// Time Exceeded135,// Neighbor Solicitation (NDP)136,// Neighbor Advertisement (NDP)];for(consttypeofessentialIcmpv6Types){awaitexeca('ip6tables',['-t','filter','-A',CHAIN_NAME_V6,'-p','ipv6-icmp','--icmpv6-type',type.toString(),'-j','ACCEPT']);}
Container Security Assessment — ✅ Strong with one observation
Evidence commands:
grep -n "cap_drop\|cap_add\|security_opt\|no-new-priv" src/docker-manager.ts
python3 -c "import json; d=json.load(open('containers/agent/seccomp-profile.json')); print(d['defaultAction']); [print('BLOCKED:',r['names']) for r in d['syscalls'] if r['action']=='SCMP_ACT_ERRNO']"```Output:```
SCMP_ACT_ALLOW
BLOCKED: ['ptrace', 'process_vm_readv', 'process_vm_writev']
BLOCKED: ['kexec_load', 'kexec_file_load', 'reboot', 'init_module', 'finit_module', ... 'pivot_root', ...]
BLOCKED: ['umount', 'umount2']
⚠️ Finding 2 — Low: Seccomp profile uses allow-by-default with a narrow blocklist
The seccomp profile (containers/agent/seccomp-profile.json) has defaultAction: SCMP_ACT_ALLOW and only explicitly blocks 3 groups. The following dangerous syscalls are not blocked:
unshare / clone (user namespace creation — can be used to gain faux-root inside a new namespace)
mount (blocked by capability, but not by seccomp)
chroot (blocked by CAP_SYS_CHROOT drop in chroot mode; open otherwise)
Mitigating factors that reduce severity:
no-new-privileges:true is set (src/docker-manager.ts:884)
CAP_SYS_ADMIN is dropped by capsh before user code runs (entrypoint.sh:278)
NET_ADMIN, SYS_PTRACE, SYS_MODULE, MKNOD, SYS_RAWIO are in the container-level cap_drop list
pivot_root is blocked by seccomp
Recommendation: Adopt a deny-by-default seccomp profile (based on Docker's default allowlist) rather than an allow-by-default profile. At minimum, add unshare, clone3, chroot, and mount to the SCMP_ACT_ERRNO list.
AppArmor note: The agent container runs apparmor:unconfined (src/docker-manager.ts:886). This is necessary to allow procfs mounting (mount -t proc), which is needed by .NET, JVM, and other runtimes. The comment notes that SYS_ADMIN is dropped before user code, limiting the practical window of exposure.
The parseUrlPatterns function in src/ssl-bump.ts:313–338 does not strip newline characters from URL patterns. If a pattern containing \n is passed via --allow-urls, the generated squid.conf would contain injected directives.
Exploitation path:
Attacker controls the --allow-urls CLI flag
Passes a pattern like https://github.com/org/*\nhttp_access allow all
This injects http_access allow all into squid.conf, bypassing all domain restrictions
Severity: LOW — requires the attacker to directly invoke the AWF CLI with crafted flags. Typical AI agent workloads do not control CLI flags. However, if AWF CLI flags are assembled from untrusted input (e.g., a YAML template that interpolates user-supplied values), this could elevate to HIGH.
The parseDomains helper used to split --allow-urls (src/cli.ts:995) does call .trim() which strips leading/trailing whitespace, but internal newlines survive.
Recommended fix: Add a newline/control-character check in URL pattern validation:
// In cli.ts, URL pattern validation block (~line 1005):if(/[\r\n\x00-\x1f]/.test(url)){logger.error(`URL pattern "\$\{url}" contains illegal control characters`);process.exit(1);}```---## ⚠️ Threat Model| # | Threat (STRIDE) | Attack Vector | Likelihood | Impact | Mitigations ||---|---|---|---|---|---|| T1 | **Info Disclosure** — ICMP tunnel over IPv6 | IPv6 network + ICMPv6 ACCEPT rule | Low (requires IPv6 DNS config) | High (full data exfil) | IPv6 disabled by default; only active when user specifies IPv6 DNS || T2 | **Elevation of Privilege** — User namespace escape | `unshare-r` → faux-root → capability confusion | Low (no SYS_ADMIN in user code) | Medium | `no-new-privileges`, `cap_drop` in `capsh`, `pivot_root` blocked || T3 | **Tampering** — squid.conf injection via URL pattern | Malicious `--allow-urls` flag | Low (requires CLI access) | High (all traffic allowed) | Requires `--ssl-bump`; user already has shell access || T4 | **Info Disclosure** — Token leak via static binary | Statically-linked binary bypasses LD_PRELOAD | Medium (Go/Rust tools common) | Medium (token exposure in memory, not exfiltrated) | Network blocks exfiltration; documented limitation || T5 | **Info Disclosure** — Task-level /proc environ | `/proc/PID/task/TID/environ` | Low (requires same-container process) | Low | Token cleared from main environ; one-shot-token logs warnings || T6 | **DoS** — Resource exhaustion | Process fork bomb inside container | Medium | Medium | `pids_limit: 1000`, `mem_limit: 4g` (`docker-manager.ts:889–891`) || T7 | **Repudiation** — Log tampering | Agent writes to squid-logs mount | Low (read-only access needed) | Low | Logs owned by `proxy` user (container), agent runs as `awfuser` || T8 | **Spoofing** — DNS poisoning to whitelist bypass | Compromise of Google DNS (8.8.8.8) | Very Low | High | Squid connects to resolved IPs; SNI checked independently |---## 🎯 Attack Surface Map| Entry Point | File:Line | What It Does | Current Protections | Risk ||---|---|---|---|---|| `--allow-domains` | `src/cli.ts:920–930` | Domains added to Squid ACL | `validateDomainOrPattern()`, ReDoS-safe regex | Low || `--allow-urls` | `src/ssl-bump.ts:313` | Patterns embedded in squid.conf | Dangerous-pattern checks; **no newline strip** | Low→Med || `--allow-host-ports` | `src/squid-config.ts:484` | Ports added to Safe_ports ACL | `sanitizedPort.replace(/[^0-9-]/g,'')` | Low || `--dns-servers` | `src/cli.ts:849` | Controls DNS whitelist | IPv4/v6 address validation | Low || `AWF_USER_UID` env | `src/docker-manager.ts:44–59` | Sets container UID | `validateIdNotInSystemRange` (min 1000) | Low || `agentCommand` (Docker) | `src/docker-manager.ts:896` | Shell command in container | `$`→`$$` escape for Compose interpolation only | By-design || IPv6 ICMPv6 chain | `src/host-iptables.ts:395–404` | Controls IPv6 exit traffic | None for echo request/reply | Medium || Seccomp profile | `containers/agent/seccomp-profile.json` | Syscall filtering | 3 rule groups, allow-by-default | Low || LD_PRELOAD (one-shot-token) | `containers/agent/one-shot-token/` | Token env isolation | Intercepts `getenv`/`secure_getenv` | Low (documented bypass) || `/dev/null` credential overlays | `src/docker-manager.ts:755–800` | Hide credential files | 15 specific file paths hidden | Low |---## 📋 Evidence Collection<details><summary>npm audit — 0 vulnerabilities</summary>```Command: cd/home/runner/work/gh-aw-firewall/gh-aw-firewall&&npmauditOutput: found0vulnerabilities```</details><details><summary>DANGEROUS_PORTS comparison — Squid vs iptables</summary>```Command: diff<(grepportsinsquid-config.ts)<(grepportsinsetup-iptables.sh)squid-config.tsports(22total): 22,23,25,110,143,445,1433,1521,3306,3389,5432,5984,6379,6984,8086,8088,9200,9300,27017,27018,28017setup-iptables.shports(15total): 22,23,25,110,143,445,1433,1521,3306,3389,5432,6379,27017,27018,28017Missingfromiptables DANGEROUS_PORTS:
5984(CouchDB),6984(CouchDBSSL),8086(InfluxDBHTTP),8088(InfluxDBRPC),9200(ElasticsearchHTTP),9300(Elasticsearchtransport)
Risk: LOW—theseportsareNOTredirectedtoSquid(noDNATrule)ANDhitthedefaultDROPruleintheOUTPUTfilterchain.Theyareblockedbythefinal`iptables -A OUTPUT -p tcp -j DROP`ruleregardless.```</details><details><summary>Seccomp profile analysis</summary>```
Command: python3-c"import json; d=json.load(open('containers/agent/seccomp-profile.json')); ..."
Output:
Default action: SCMP_ACT_ALLOWBLOCKEDsyscalls(3groups):
-ptrace,process_vm_readv,process_vm_writev-kexec_load,kexec_file_load,reboot,init_module, ... pivot_root, ...
-umount,umount2NOT blocked: unshare,clone,mount,chroot```</details><details><summary>URL pattern newline injection PoC</summary>```
Command: python3newlinesimulation
Pattern input: 'https://github.com/myorg/*\nhttp_access allow all'Resultinsquid.conf:
aclallowed_url_0url_regex^(github/redacted)\.com/myorg/[^\s]*http_accessallowall$
Contains newline: True
✅ Recommendations
🔴 Medium — Address Soon
1. Restrict ICMPv6 types in IPv6 chain (src/host-iptables.ts:395–404)
Replace '-p', 'ipv6-icmp', '-j', 'ACCEPT' with specific type allows
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
A comprehensive, evidence-based security review of the gh-aw-firewall repository was conducted on 2026-03-06. The review analysed 3,009 lines of security-critical code across the iptables management layer, Squid proxy configuration, container entrypoint, domain-pattern validation, and the one-shot-token LD_PRELOAD library.
Overall posture: Strong. The defence-in-depth architecture (host iptables → Squid ACL → capability dropping → token isolation) is well-designed. Three low-to-medium findings were identified; none allow remote exploitation without physical access to the CLI flags. No critical vulnerabilities were found, and the npm dependency audit returned 0 vulnerabilities.
🔍 Findings from Previous Security Runs
No recent runs of firewall-escape-test or secret-digger workflows were reachable via the agentic-workflow log API during this run (tool returned exit status 1). Analysis relies solely on static code review and local command evidence.
🛡️ Architecture Security Analysis
Network Security Assessment — ✅ Strong
Evidence commands:
Finding: Firewall rule ordering is correct. Host-level
DOCKER-USER→ customFW_WRAPPERchain → Squid ACLs form a coherent multi-layer stack.Key observations:
host-iptables.ts).8.8.8.8, 8.8.4.4) can receive port-53 traffic; all other UDP is REJECTed ([FW_BLOCKED_UDP]prefix).ip6tablesis unavailable, IPv6 is disabled viasysctl net.ipv6.conf.all.disable_ipv6=1(src/host-iptables.ts:96–100,containers/agent/setup-iptables.sh:33–37).When the user specifies IPv6 DNS servers (e.g.,
--dns-servers 2001:4860:4860::8888), the IPv6 chain is created and ALL ICMPv6 message types are allowed. This includes:ICMP tunneling tools can carry arbitrary payloads over ICMP echo, completely bypassing Squid domain filtering. The risk is limited to networks that route IPv6, but the control should be tightened.
Recommended fix — replace the blanket accept with specific types:
Container Security Assessment — ✅ Strong with one observation
Evidence commands:
The seccomp profile (
containers/agent/seccomp-profile.json) hasdefaultAction: SCMP_ACT_ALLOWand only explicitly blocks 3 groups. The following dangerous syscalls are not blocked:unshare/clone(user namespace creation — can be used to gain faux-root inside a new namespace)mount(blocked by capability, but not by seccomp)chroot(blocked byCAP_SYS_CHROOTdrop in chroot mode; open otherwise)Mitigating factors that reduce severity:
no-new-privileges:trueis set (src/docker-manager.ts:884)CAP_SYS_ADMINis dropped bycapshbefore user code runs (entrypoint.sh:278)NET_ADMIN,SYS_PTRACE,SYS_MODULE,MKNOD,SYS_RAWIOare in the container-levelcap_droplistpivot_rootis blocked by seccompRecommendation: Adopt a deny-by-default seccomp profile (based on Docker's default allowlist) rather than an allow-by-default profile. At minimum, add
unshare,clone3,chroot, andmountto the SCMP_ACT_ERRNO list.AppArmor note: The agent container runs
apparmor:unconfined(src/docker-manager.ts:886). This is necessary to allow procfs mounting (mount -t proc), which is needed by .NET, JVM, and other runtimes. The comment notes that SYS_ADMIN is dropped before user code, limiting the practical window of exposure.Domain Validation Assessment — ✅ Excellent
Evidence command:
Key strengths:
[a-zA-Z0-9.-]*(not.*) —src/domain-patterns.ts:83*,*.*, patterns with>50%wildcard segments —src/domain-patterns.ts:155–178src/domain-patterns.ts:248(redacted)https://`) properly handledInput Validation — URL Pattern Injection
Evidence:
The
parseUrlPatternsfunction insrc/ssl-bump.ts:313–338does not strip newline characters from URL patterns. If a pattern containing\nis passed via--allow-urls, the generatedsquid.confwould contain injected directives.Exploitation path:
--allow-urlsCLI flaghttps://github.com/org/*\nhttp_access allow allhttp_access allow allintosquid.conf, bypassing all domain restrictionsSeverity: LOW — requires the attacker to directly invoke the AWF CLI with crafted flags. Typical AI agent workloads do not control CLI flags. However, if AWF CLI flags are assembled from untrusted input (e.g., a YAML template that interpolates user-supplied values), this could elevate to HIGH.
The
parseDomainshelper used to split--allow-urls(src/cli.ts:995) does call.trim()which strips leading/trailing whitespace, but internal newlines survive.Recommended fix: Add a newline/control-character check in URL pattern validation:
✅ Recommendations
🔴 Medium — Address Soon
1. Restrict ICMPv6 types in IPv6 chain (
src/host-iptables.ts:395–404)'-p', 'ipv6-icmp', '-j', 'ACCEPT'with specific type allows🟡 Low — Plan to Address
2. Add newline/control-char validation for
--allow-urls(src/cli.ts:~1005)\r,\n, or other control characters (/[\r\n\x00-\x1f]/)3. Harden seccomp profile to deny-by-default (
containers/agent/seccomp-profile.json)SCMP_ACT_ALLOWdefault with Docker's standard deny-by-default allowlistunshare,clone3,chroot,mountto the ERRNO blocklist4. Align DANGEROUS_PORTS between squid-config.ts and setup-iptables.sh
setup-iptables.shDANGEROUS_PORTS🟢 Informational
5. Consider specific ICMPv4 restriction (
src/host-iptables.ts)FW_WRAPPERchainhping3could theoretically use ICMP echo for tunneling over IPv4 tooNET_RAWdropped), limited practical impact6. Document LD_PRELOAD bypass in AGENTS.md / CLAUDE.md
containers/agent/one-shot-token/README.mdbut not in AGENTS.md📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions