[Security Review] Daily Security Review and Threat Modeling — 2026-03-07 #1173
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-03-14T13:41:03.209Z.
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
📊 Executive Summary
Review Date: 2026-03-07 | Reviewer: Security Review Agent | Version: 0.23.1
This review covers a deep analysis of the
@github/agentic-workflow-firewallcodebase, examining ~4,104 lines of security-critical code across the network filtering stack (host-iptables, setup-iptables, Squid config, container entrypoint, and Docker orchestration). The system's overall security posture is good, with multiple defense-in-depth layers. Three actionable findings are documented below.🔍 Findings from Firewall Escape Test
The
security-reviewworkflow was the most recent in-scope agentic workflow; no prior escape-test run logs were accessible viaagenticworkflows-logs(download error). Complementary findings from previous security guards are reflected where patterns overlap.🛡️ Architecture Security Analysis
Network Security Assessment — Strong
The traffic interception chain is well-designed:
src/host-iptables.ts) creates a customFW_WRAPPERchain that filters all egress from the bridge interface. Rules are ordered correctly: Squid exempt → ESTABLISHED/RELATED → localhost → trusted DNS → Squid port → API proxy port → multicast/link-local block → UDP block → default deny.containers/agent/setup-iptables.sh) redirects HTTP/HTTPS to Squid and terminates all other TCP with a final DROP rule.src/squid-config.ts) enforces domain whitelisting and blocks dangerous ports via!Safe_ports.ip6tablesis set up when available; when unavailable,sysctl net.ipv6.conf.all.disable_ipv6=1prevents unfiltered IPv6 bypass.Evidence:
Container Security Assessment — Good with Notes
NET_ADMINis added to the agent container for iptables setup and then dropped viacapshbefore user code executes (entrypoint.sh:649,cap_drop: ['NET_RAW','SYS_PTRACE','SYS_MODULE','SYS_RAWIO','MKNOD']indocker-manager.ts:872-878).no-new-privileges:trueprevents re-acquisition of dropped privileges.mem_limit: 4g,pids_limit: 1000.LD_PRELOAD=/usr/local/lib/one-shot-token.so) protects secrets from/proc/self/environre-reads after agent startup.unset_sensitive_tokens) runs after a 5-second grace period for agent initialization.Domain Validation Assessment — Strong
[a-zA-Z0-9.-]*(not.*) indomain-patterns.ts:77.*,*.*,*.*.com) are rejected.isDomainMatchedByPattern:279).Input Validation / Command Injection Assessment — Strong
eslint-rules/no-unsafe-execa.js) statically enforces thatexeca()is only called with literal string commands — no template literals with expressions or string concatenation. Only 2 justifiedeslint-disableexceptions exist (log-discovery.ts:157for container name filter,ssl-bump.ts:201for OpenSSL cert subject — both use carefully-controlled variables, not user input).execa, not through a shell.seccomp-profile.json+docker-manager.ts:885docker-manager.ts:886domain-patterns.ts:139-185squid-config.ts:10-31vssetup-iptables.sh:203-221setup-iptables.sh:156-200containers/agent/docker-stub.shdocker-manager.ts:888-892[FW_DNS_QUERY]) but iptables logs go to kernel dmesg onlyhost-iptables.ts:310-316🎯 Attack Surface Map
--allow-domains)src/cli.ts:870-878validateDomainOrPattern()rejects broad patterns; no newline check--allow-urls)src/cli.ts:1004-1038https://prefix, rejects broad patterns; no newline checksrc/squid-config.ts:119,247,256[^0-9-]/g)src/docker-manager.ts:896$$) for YAML; passes throughbash -cas intendedsrc/host-iptables.tscontainers/agent/setup-iptables.shis_valid_ipv4()validates resolved IPs before usesetup-iptables.sh:43-48entrypoint.sh:649capsh --drop=cap_net_adminbefore user code;no-new-privileges:trueentrypoint.sh:634, 647-656unset_sensitive_tokensafter 5s grace periodcontainers/agent/get-claude-key.shcontainers/agent/seccomp-profile.json📋 Evidence Collection
Finding T1: Permissive Seccomp Profile
Command run:
Docker's built-in default seccomp profile uses
SCMP_ACT_ERRNOas its default (deny-by-default allowlist). The AWF custom profile usesSCMP_ACT_ALLOW(allow-by-default blocklist). Syscalls not explicitly blocked includemount,unshare, andclonewithCLONE_NEWUSER. These are mitigated by capability drops (cap_drop: CAP_SYS_ADMINvia capsh before user code) but the profile itself provides weaker guarantees.Applied in
docker-manager.ts:885:Finding T2: AppArmor Disabled
The comment explains the rationale (procfs mounting). However,
apparmor:unconfinedremoves MAC-level process confinement for the entire container lifecycle, not just during setup. Once capsh dropsSYS_ADMIN, mount calls from user code would fail at the capability check — but AppArmor would no longer provide an independent audit trail or secondary enforcement for other operations (file access, network access from processes, etc.).Finding T3: Domain Names Not Checked for Newlines (Squid Config Injection)
The
validateDomainOrPattern()function indomain-patterns.ts:139-185validates:*,*.*,[*.]+(too broad)..(double dots).alone,*.,.*But it does not check for newline characters (
\n,\r). Domains are then embedded directly into the Squid configuration:A domain like
github.com\nhttp_access allow allwould be parsed bycommanderas a single argument containing a literal newline (if passed via a config file or programmatic API), and when written tosquid.conf, the second line would becomehttp_access allow all, allowing unrestricted access.Mitigating factors: Shell argument parsing strips/escapes newlines in practice when domains are passed via CLI. The
parseDomains()function splits on commas but not newlines. Risk is very low in normal CLI usage but theoretically present if the AWF library is used programmatically or if the allow-domains-file feature is used with malicious input.Finding T4: Dangerous Ports List Inconsistency
src/squid-config.tsDANGEROUS_PORTS (21 entries) includes ports not insetup-iptables.sh(15 entries):However, in the container's OUTPUT filter chain, the final rule is:
This catches all TCP not explicitly redirected to Squid or allowed. Ports 5984, 9200, etc. would still be dropped. The inconsistency is a defense-in-depth gap (Squid and iptables don't give identical explicit denials) but does NOT represent an actual bypass.
Good Security Controls (evidence)
ReDoS protection:
Execa injection prevention:
One-shot token protection:
✅ Recommendations
Medium Priority
M1 — Replace permissive seccomp profile with Docker's built-in default
The custom
seccomp-profile.jsonhasdefaultAction: SCMP_ACT_ALLOW(allow-by-default). Consider switching to Docker's built-in default seccomp profile (seccomp:docker-default) which uses deny-by-default and only adds specific blocks needed. If procfs mounting requires exceptions, add those specific syscalls to an allowlist-based profile rather than switching to a blocklist approach. File:containers/agent/seccomp-profile.json,src/docker-manager.ts:885.M2 — Document or scope AppArmor unconfined
If
apparmor:unconfinedis only needed for the setup phase (mounting procfs), investigate whether a custom AppArmor profile can be created that allows the specificmountoperations needed, then re-enables confinement. Alternatively, explicitly document in the security architecture that AppArmor is intentionally bypassed and what mitigations replace it (capability drops, seccomp,no-new-privileges). File:src/docker-manager.ts:886.Low Priority
L1 — Add newline sanitization to domain and URL validation
Add an explicit check for control characters (including
\n,\r,\t) invalidateDomainOrPattern()(domain-patterns.ts:139) and in the URL pattern validation incli.ts:1004. A one-line fix:This closes the theoretical Squid config injection vector, particularly relevant for the
--allow-domains-filecode path.L2 — Synchronize dangerous ports lists
Add the 6 ports present in
squid-config.tsbut missing fromsetup-iptables.shto the iptables DANGEROUS_PORTS array: 5984, 6984, 8086, 8088, 9200, 9300. This ensures defense-in-depth is consistent at both enforcement layers.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions