[Security Review] Daily Security Review — 2026-03-13 #1293
Replies: 3 comments
-
|
🔮 The ancient spirits stir; the smoke test agent has passed through these halls.
|
Beta Was this translation helpful? Give feedback.
-
|
🔮 The ancient spirits stir, and the oracle marks this hall: the smoke test agent was here. The omens are read, the runes recorded, and the veil remains unbroken.
|
Beta Was this translation helpful? Give feedback.
-
|
This discussion was automatically closed because it expired on 2026-03-20T13:55:07.613Z.
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
📊 Executive Summary
Review date: 2026-03-13
Lines of security-critical code analyzed: 6,165 (across 7 core files)
Attack surfaces identified: 9
STRIDE threats documented: 6 categories
Findings: 1 High · 3 Medium · 4 Low
The overall security architecture is well-designed: dual-layer enforcement (iptables + Squid ACLs), capability dropping, seccomp profiles, privilege de-escalation via
capsh/gosu, and a default-deny network posture. One high-severity gap in IPv6 egress filtering was discovered. The remaining findings are defense-in-depth inconsistencies and hardening opportunities.🔍 Findings from Firewall Escape Test Agent
The
firewall-escape-testworkflow was not found in this repository's workflow list. The security-guard and security-review workflows are present. The findings below are based solely on static analysis of the codebase.🛡️ Architecture Security Analysis
Network Security Assessment
Architecture: Two-tier enforcement — host-level
DOCKER-USERchain (FW_WRAPPER) + container-levelOUTPUTfilter chain + Squid domain ACL.Findings:
✅ Strengths
DOCKER-USERchain (FW_WRAPPER) intercepts all bridge egress traffic before routing.ESTABLISHED,RELATEDconntrack rule allows return traffic correctly without over-permitting.127.0.0.11(Docker embedded DNS); upstream resolvers controlled viadocker-compose dns:field.sysctlwhenip6tablesunavailable — prevents IPv6 from becoming an unfiltered bypass path.dstdomainACLs always prefix domains with.viaformatDomainForSquid(), ensuring subdomain matching.Evidence:
Impact: When
ip6tablesis available on the host (common on modern Linux), IPv6 traffic originating from the agent container is not filtered by either the container-levelOUTPUTchain or the host-levelDOCKER-USERchain. The NAT table has no IPv6 DNAT redirect either, so IPv6 HTTP/HTTPS traffic bypasses Squid entirely. An agent that can resolve IPv6 addresses for target services could potentially exfiltrate data or reach external servers unfiltered.Mitigation:
ip6tables -A OUTPUT -p tcp -j DROPandip6tables -A OUTPUT -p udp -j DROPat the end ofsetup-iptables.shwhenIP6TABLES_AVAILABLE=true.FW_WRAPPER_V6intoip6tables DOCKER-USERunconditionally whenip6tablesis available (not just when IPv6 DNS servers are configured), with a default-REJECT policy.Container Security Assessment
✅ Strengths
initcontainer handles iptables setup withNET_ADMIN + NET_RAW, agent container never receivesNET_ADMIN(line 958,docker-manager.ts): "SECURITY: NET_ADMIN is NOT granted to the agent container."cap_drop: ALLapplied to agent container with only required capabilities added back.no-new-privileges:trueviasecurity_optprevents SUID escalation.containers/agent/seccomp-profile.json) withdefaultAction: SCMP_ACT_ERRNO.AWF_USER_UID/AWF_USER_GIDvalidated: non-empty, numeric, and non-zero (root) before use (entrypoint.shlines 15-37).capsh --drop=...before user command execution.mount,unshare,setnsEvidence:
Impact:
mountis allowed — whileCAP_SYS_ADMINis needed for most mounts, bind mounts and certain filesystem operations are possible without it in some kernel configurations.unshare(specificallyCLONE_NEWUSER) can create unprivileged user namespaces, which when combined withCLONE_NEWNETcreates a new network namespace potentially bypassing container network rules. This is a known container escape vector (userspace network namespace → fresh route table).setnscan move the process into an existing namespace if a file descriptor to it is available.Note:
CAP_SYS_ADMINis dropped viacap_drop: ALL+ selective add-back, which limits damage. In chroot mode,CAP_SYS_ADMINandCAP_SYS_CHROOTare explicitly dropped.Mitigation: Consider adding
unshare,mount, andsetnsto the seccomp DENY list (SCMP_ACT_ERRNO) if these syscalls are not needed by agent workloads.Domain Validation Assessment
✅ Strengths
validateDomainOrPattern()correctly blocks*,*.*, and/^[*.]+$/(all-wildcard) patterns.[a-zA-Z0-9.-]*character class instead of.*— prevents ReDoS attacks.(redacted)https://`) supported and enforced separately.Evidence (
src/domain-patterns.tslines 163-177):Pattern like
*.*.github.com(2 wildcards, 4 segments) passes validation since2 < 4-1=3. This results in the regex^[a-zA-Z0-9.-]*\.[a-zA-Z0-9.-]*\.github\.com$which matches arbitrary two-level subdomains. While this is technically correct for matchingapi.v2.github.com, it's more permissive than likely intended and could be confusing. The validation checkwildcardSegments >= totalSegments - 1would needwildcardSegments = 3before rejecting a 4-segment pattern.Mitigation: Document the intended semantics clearly. Consider adding a warning (not error) when more than one wildcard segment is used, since
*.github.comcovers 99% of use cases.Input Validation Assessment
✅ Strengths
escapeShellArg()incli.ts(line 679) wraps args in single quotes and escapes embedded single quotes.agentCommand.replace(/\$/g, '$$$$')indocker-manager.ts(line 1003) escapes$for Docker Compose interpolation prevention.AWF_USER_UID/AWF_USER_GIDvalidated as numeric non-zero beforeusermod/groupmod.1 <= START <= END <= 65535, checked againstDANGEROUS_PORTS.Evidence:
Impact: The NAT-level pre-blocking in
setup-iptables.shis a defense-in-depth measure that prevents dangerous ports from even being redirected to Squid. The 6 missing ports are still protected by the finaliptables -A OUTPUT -p tcp -j DROPrule, but the inconsistency creates a maintenance risk: future changes to one list may not be reflected in the other. Additionally, Squid'sSafe_portsACL also enforces these, but the multiple-layer validation is desirable.Mitigation: Synchronize both
DANGEROUS_PORTSlists and add a comment referencingsquid-config.tsfromsetup-iptables.sh.setup-iptables.sh:283-285,host-iptables.ts:293-326seccomp-profile.json(allowsunshare)src/dlp.ts,src/cli.ts:1343-1346/proc/self/environif agent runs as root or same UID (mitigated by privilege drop)containers/agent/one-shot-token/src/squid-config.tsmountsyscall allowed in seccomp + ifCAP_SYS_ADMINnot fully dropped, agent may perform bind-mounts over sensitive pathsseccomp-profile.json,entrypoint.sh:305-311🎯 Attack Surface Map
cli.ts:1166,docker-manager.ts:1003escapeShellArg(), Docker Compose$$escapingdomain-patterns.ts:validateDomainOrPattern()setup-iptables.sh,host-iptables.tssetup-iptables.sh,host-iptables.tsentrypoint.sh:68-86,docker-manager.ts:511-516127.0.0.11in resolv.conf, upstream restricteddocker-manager.ts:967-981cap_drop: ALL+ seccomp +no-new-privilegesseccomp-profile.jsonSCMP_ACT_ERRNOdefault, explicit allow-listcontainers/api-proxy/server.jssrc/dlp.ts,src/squid-config.ts--enable-dlp📋 Evidence Collection
IPv6 Container-Level Filter Gap (setup-iptables.sh:283-285)
IPv6 Host-Level Gap (host-iptables.ts:293-326)
✅ Recommendations
🔴 High Priority
H1: Plug IPv6 Egress Gap
Two-part fix:
containers/agent/setup-iptables.sh— Add IPv6 DROP rules whenip6tablesis available:src/host-iptables.ts— Create and attachFW_WRAPPER_V6unconditionally whenip6tablesis available (not only when IPv6 DNS servers are configured), with a default-REJECT rule:🟠 Medium Priority
M1: Restrict
unshare/setnsin Seccomp ProfileAdd to
containers/agent/seccomp-profile.jsondeny list:{ "names": ["unshare", "setns"], "action": "SCMP_ACT_ERRNO" }Note: Verify no legitimate agent workload needs these syscalls.
mountcan remain if bind-mounts are needed for certain tools, but should be documented.M2: Enable DLP by Default (or Warn)
The
--enable-dlpflag should be on by default or at least emit a warning when not set. Credential patterns in URLs represent an undetected exfiltration vector in standard deployments.M3: Synchronize DANGEROUS_PORTS Lists
Add the 6 missing ports (5984, 6984, 8086, 8088, 9200, 9300) to
containers/agent/setup-iptables.shDANGEROUS_PORTS array to matchsquid-config.ts. Add a comment insetup-iptables.shnoting it must stay in sync withsrc/squid-config.ts.🟡 Low Priority
L1: Squid Connection Limits
Add
client_ip_max_connectionsandserver_ip_max_connectionsdirectives to the generated Squid config to prevent connection exhaustion DoS.L2: Update markdownlint-cli2 DevDependency
(Resolves 4 moderate npm audit findings in devDependencies.)
L3: Document
mount/unshare/setnsSeccomp IntentAdd inline comments in
seccomp-profile.jsonexplaining why these syscalls are allowed, to prevent future accidental tightening without understanding the impact.L4: Multi-Wildcard Pattern Warning
Consider emitting a warning (not error) to stderr when
--allow-domainsincludes patterns with more than one wildcard segment, as these may be broader than intended.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions