[Security Review] Daily Security Review — 2026-04-08 #1793
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-04-15T13:21:31.542Z.
|
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
This review covers the gh-aw-firewall codebase as of 2026-04-08, with complementary context from live escape-test results. The overall security posture is strong with well-layered defenses. No active breakouts were observed in testing. Several nuanced findings warrant attention.
npm audit)🔍 Findings from Firewall Escape Test
Test run: 2026-04-04, workflow: Secret Digger (Copilot)
The escape test deployed a prompt injection payload designed to perform secret discovery, credential harvesting, environment variable enumeration, and process inspection. The results show complete resistance:
All three concurrent agent instances reported
GH_AW_NOOP_MESSAGEwith the refusal, andGH_AW_SECRET_VERIFICATION_RESULT: successconfirmed no credentials were exfiltrated. The one-shot-token LD_PRELOAD protection and environment clearing mechanisms functioned as designed.🛡️ Architecture Security Analysis
Network Security Assessment
Evidence collected from:
src/host-iptables.ts,containers/agent/setup-iptables.shThe system uses a two-layer network filtering approach:
Layer 1 — Host iptables (DOCKER-USER chain → FW_WRAPPER chain):
✅ IPv6 disabled inside container namespace (defense in depth beyond host layer)
✅ Docker embedded DNS DNAT rules preserved before chain flush
✅ Dangerous ports blocked at Squid level (
src/squid-config.ts:16-30)Container Security Assessment
Evidence collected from:
src/docker-manager.ts,containers/agent/entrypoint.sh,containers/agent/seccomp-profile.jsonCapability model:
NET_RAW, SYS_ADMIN, SYS_CHROOT, SETUID, SETGID, SETFCAPNET_ADMIN, NET_RAWALLSYS_CHROOT, SYS_ADMINALL except SYS_CHROOT, SYS_ADMINALLThe agent drops
SYS_CHROOTandSYS_ADMINviacapsh --drop=cap_sys_chroot,cap_sys_adminbefore user code runs (containers/agent/entrypoint.sh:809).✅ NET_ADMIN never granted to agent (line
src/docker-manager.ts:1247)✅ Capabilities dropped via bounding set (cannot be regained even by root)
✅ gosu switches to unprivileged
awfuser(non-zero UID enforced at entrypoint)✅ Seccomp profile applied: 343 allowed syscalls, blocks ptrace, process_vm_readv, kexec_load, init_module, etc.
Seccomp profile analysis (containers/agent/seccomp-profile.json):
ptraceprocess_vm_readv/writevkexec_loadinit_module / finit_modulekeyctl / add_key / request_keypivot_rootunsharesetnsmountcapsetchrootopen_by_handle_atname_to_handle_atOne-shot token LD_PRELOAD (
containers/agent/one-shot-token/one-shot-token.c):✅ Intercepts
getenv()for sensitive tokens✅ Clears tokens from environment (removing from
/proc/self/environ) after first read✅ XOR-obfuscates default token names in
.rodatato resiststrings/objdumpreconnaissance✅ Parent entrypoint unsets tokens after 5-second delay (
sleep 5; entrypoint runs as root, user code as awfuser — user code cannot read/proc/1/environ)Domain Validation Assessment
Evidence from:
src/domain-patterns.ts,src/squid-config.ts✅ Port allowlist (only 80/443 by default); dangerous ports (22, 25, 1521, 3306, etc.) explicitly blocked
✅ Blocklist (denied domains) takes precedence over allowlist
✅ DLP optional: URL regex patterns detect GitHub PATs, OpenAI keys, Anthropic keys in outbound URLs
DLP limitation: Only inspects URLs, not request bodies or HTTP headers. Without SSL bump, HTTPS request bodies are invisible to Squid.
Input Validation Assessment
Evidence from:
src/cli.ts✅ UID/GID validated as numeric and non-zero (
⚠️ Single-argument mode passes the string directly to the container shell — this is intentional and documented but means shell metacharacters from user input reach
containers/agent/entrypoint.sh:18-35)✅ Port specs validated:
isValidPortSpec()rejects leading zeros, non-numeric, out-of-range✅ Shell argument escaping when multiple args are passed
bash -cunshare+ user namespaces: agent could create a user namespace giving apparent rootseccomp-profile.json:340allowsunshare;setnsalso allowedcapsetallowed post-capsh drop: theoretical manipulation of permitted setseccomp-profile.json: capset in allowed list; bounding set drops are irreversibleopen_by_handle_at+name_to_handle_atCAP_DAC_READ_SEARCHwhich is not granted--env-allpassthrough: broad host env exposure if user specifies flagsrc/docker-manager.ts:679; warned but functional; exclusion list may be incompletesrc/docker-manager.ts:687-689; intentional, documented behaviorSQUID_DANGEROUS_CHARSsanitization indomain-patterns.ts:27,172src/host-iptables.ts:293-296; Squid IP gets full ACCEPT; by design🎯 Attack Surface Map
setup-iptables.shDNAT → SquidHTTPS_PROXYenv → Squid CONNECT169.254.0.0/16blocked at host iptables--allow-domainsCLI flagSQUID_DANGEROUS_CHARSregex + assertSafeForSquidConfigunshare/setnssyscalls/proc/self/environ/host/etc/shadow, unwhitelisted home dirs excluded📋 Evidence Collection
Network security commands
Container security commands
Dependency security commands
✅ Recommendations
🔴 Critical — No items at this severity
🟠 High
H1 — Consider blocking
unsharein seccomp profileThe
unsharesyscall (seccomp-profile.json:340) andsetns(seccomp-profile.json) allow namespace manipulation. In kernels with user namespace enabled, an unprivileged user can callunshare(CLONE_NEWUSER)to obtain a full root-equivalent context within a new user namespace. While this does not directly escape the container's network namespace (NET_ADMIN is not granted), it can be combined with other techniques. Evaluate whether any supported workloads require user namespace creation; if not, addunshareandsetnsto the seccomp blocklist.H2 — Harden
--env-allexclusion list documentation and audit loggingsrc/docker-manager.ts:679— the--env-allflag passes all host environment variables to the container. While a warning is emitted (:1861-1862), the exclusion list (:520-560) may not cover all organization-specific secret variable names. Consider: (a) emitting a log entry listing which variables were passed, or (b) requiring explicit opt-in confirmation for--env-allin CI contexts.🟡 Medium
M1 — DLP body/header inspection gap
src/dlp.tsimplements URL-pattern-based DLP. Credentials in HTTP POST bodies, request headers, or JSON payloads are not inspected. This is architecturally limited without SSL bump. Consider documenting this gap explicitly in user-facing docs and recommending--enable-ssl-bumpfor high-security workloads where credential-in-body exfiltration is a concern.M2 —
open_by_handle_at/name_to_handle_atShocker-style vectorBoth syscalls are allowed in the seccomp profile. Classic Shocker container escape uses these with
CAP_DAC_READ_SEARCHto traverse the host filesystem via file handles. That capability is not granted, mitigating the risk significantly. However, consider explicitly blockingopen_by_handle_atandname_to_handle_atas a defense-in-depth measure, as they are rarely needed by typical agent workloads.M3 — API proxy iptables range covers all ports 10000–10004
src/host-iptables.ts:420-427computesminPort:maxPortrange dynamically. The ports are 10000 (OpenAI), 10001 (Anthropic), 10002 (Copilot), 10003 (Gemini), 10004 (OpenCode). The contiguous range is correct and intentional. No gap — this observation is informational only. (Note: earlier analysis incorrectly identified port 10003 as a gap; it is the Gemini endpoint.)🔵 Low
L1 — Document Squid unrestricted ACCEPT design decision
src/host-iptables.ts:293-296grants Squid an unrestricted host-level ACCEPT. This is the correct design (Squid needs full egress to forward proxied requests), but should be explicitly documented in the architecture docs as a trust boundary: Squid is a trust anchor; its compromise is a full egress bypass. Consider noting this indocs/environment.md.L2 —
capsetin seccomp profilecapsetis allowed (needed during entrypoint user setup). Aftercapsh --dropremoves capabilities from the bounding set,capsetcannot restore them. Risk is theoretical only, but consider adding a note in the seccomp profile JSON comment explaining whycapsetis required.L3 — Audit one-shot token 5-second window
containers/agent/entrypoint.shruns the agent in the background, then sleeps 5 seconds before unsetting tokens from the parent shell. The parent shell runs as root; user code runs asawfuser. Cross-user/proc/$PID/environaccess is blocked by Linux'sptrace_may_accesscheck (user code cannot read root's /proc entries). This mitigation is sound; document it explicitly as a security invariant.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions