[Security Review] Daily Security Review and Threat Modeling — 2026-03-23 #1398
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-03-30T14:01:01.555Z.
|
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-firewallcodebase as of 2026-03-23, analyzing network security, container hardening, domain validation, input handling, and the full threat surface. The overall security posture is strong, with a defense-in-depth architecture combining host-level iptables, Squid L7 proxy filtering, container capability dropping, seccomp allowlisting, one-shot token protection, and tmpfs overlays. No critical vulnerabilities were found. Two medium-severity design gaps and two low-severity observations are documented below, with actionable recommendations.no-new-privileges, resource limitsnpm audit: 0 vulnerabilities🔍 Findings from Previous Security Testing
No "firewall-escape-test" workflow was found in the workflow registry (
agenticworkflows-status). The closest workflow issecurity-review(this workflow, running daily). Thesecret-digger-claude,secret-digger-codex, andsecret-digger-copilotworkflows run hourly and provide complementary secret-leakage detection. No prior run logs were accessible from within this workflow context. All findings below are independently gathered from codebase analysis.🛡️ Architecture Security Analysis
Network Security Assessment
Host-level filtering (
src/host-iptables.ts):Rules are correctly ordered: Squid egress allow → conntrack → localhost → DNS → deny all. The "allow before final deny" pattern is correct.
Container-level NAT (
containers/agent/setup-iptables.sh):127.0.0.11) are saved and restored after NAT flush — prevents silent DNS breakageIPv6 fallback: When
ip6tablesis unavailable, both host (src/host-iptables.ts:108) and container (containers/agent/setup-iptables.sh:35-37) disable IPv6 viasysctl net.ipv6.conf.all.disable_ipv6=1, preventing an unfiltered bypass path.Squid configuration (
src/squid-config.ts:549-554):Direct IP-address connections are blocked before domain allowlist checks, preventing domain filter bypass via raw IPs.
Container Security Assessment
Seccomp profile (
containers/agent/seccomp-profile.json):SCMP_ACT_ERRNO— default-deny allowlist model (excellent)ptrace,process_vm_readv/writev(memory injection prevention),kexec_*,reboot, kernel module loading,pivot_root,keyctl,umountx86_64,x86,aarch64Capabilities (
src/docker-manager.ts:1013-1022):SYS_ADMINis dropped viacapshincontainers/agent/entrypoint.sh:307before user code runs:Resource limits: 2 GB memory (no swap), 1000 PIDs, default CPU shares — prevents container-level DoS.
Credential isolation: One-shot token LD_PRELOAD library (
/usr/local/lib/one-shot-token.so) caches specified env vars in process memory and clears them from/proc/self/environon first read. Protected tokens:COPILOT_GITHUB_TOKEN,GITHUB_TOKEN,GH_TOKEN,GITHUB_API_TOKEN,ANTHROPIC_API_KEY,OPENAI_API_KEY, etc. (src/docker-manager.ts:422).Docker-compose.yml secrets exposure: Mitigated by tmpfs overlay over
workDir(src/docker-manager.ts:996):DinD disabled:
containers/agent/docker-stub.shreplaced the Docker binary with a stub that exits 127 with an error message (removed in v0.9.1 per PR #205).Domain Validation Assessment
src/domain-patterns.tsimplements:*,*.*, and any pattern matching/^[*.]+$/— prevents "allow all" configurationswildcardToRegex()converts*→[a-zA-Z0-9.-]*(not.*) — prevents catastrophic ReDoS backtrackingisDomainMatchedByPattern()(redacted)https://`, or both)Example of proper validation:
Input Validation Assessment
Agent command escaping (
src/docker-manager.ts:1050):Dollar signs are doubled to prevent Docker Compose YAML variable interpolation (
$VAR→$$VAR). The command is passed as an array to Docker Compose (not a raw shell string), reducing shell injection risk on the Docker API boundary.Secret redaction in logs (
src/redact-secrets.ts):Covers Bearer tokens, env-var-style assignments, and GitHub token format patterns. Applied in
src/cli.ts:1721before debug logging.GITHUB_TOKEN/GH_TOKENalways forwarded to agent (see F-2)--env-allpasses cloud provider credentials to agentSYS_ADMINwindow duringentrypoint.shbeforecapshdropmountsyscall withSYS_ADMINactiveunshareallowed in seccomp; user namespaces could be attempted🎯 Attack Surface Map
setup-iptables.sh+ Squidsetup-iptables.sh:120-150src/host-iptables.ts:385src/squid-config.ts:549-554http_access deny dst_ipv4/dst_ipv6src/docker-manager.ts:420-422src/docker-manager.ts:996src/docker-manager.ts:798/dev/nullby defaultsrc/docker-manager.ts:596-707/etc/shadownot mounted~/.npmrc,~/.cargo/credentials)src/docker-manager.ts:900-904/dev/nulloverlay mountssrc/docker-manager.ts:1050+src/cli.ts:1349$$escaping + array argssrc/domain-patterns.ts:validateDomainOrPatterncontainers/agent/entrypoint.sh:365src/host-iptables.ts:104-108🔎 Detailed Findings
F-1: AppArmor Disabled for Agent Container (Medium)
Evidence:
Comment in code: "AppArmor is set to unconfined to allow mounting procfs at /host/proc"
Analysis: AppArmor is intentionally disabled because Docker's default profile blocks the
mountsyscall, which is needed to mount a container-scoped procfs at/host/proc. The seccomp profile,no-new-privileges, and capability dropping (capsh) are all present as compensating controls. SinceSYS_ADMINis dropped before user code runs, user code cannot actually mount anything even with AppArmor disabled.Residual risk: During the window before
capshdropsSYS_ADMIN(i.e., duringentrypoint.shexecution), AppArmor is not restricting file access patterns. A bug inentrypoint.shcould be exploited with bothSYS_ADMINand no AppArmor.Recommendation (Medium): Consider a custom AppArmor profile that permits
mountonly for procfs (proc on * type proc) but restricts other dangerous mount types, rather than usingunconfined. This would add the file-access controls AppArmor provides while still allowing the needed procfs mount.F-2: GITHUB_TOKEN Forwarded Without API-Proxy Guard (Medium)
Evidence:
COPILOT_GITHUB_TOKENhas a!config.enableApiProxyguard and is replaced with a placeholder.GITHUB_TOKENandGH_TOKENhave no such guard and are forwarded unconditionally regardless of whether--enable-api-proxyis active.Mitigating control:
GITHUB_TOKENis inAWF_ONE_SHOT_TOKENS(src/docker-manager.ts:422), so it's protected by the one-shot LD_PRELOAD mechanism. After the agent reads the token once, it's cleared from/proc/self/environ.Residual risk: If the agent makes an authorized GitHub API call (e.g., to the allowlisted
api.github.com), the token can be used and potentially exfiltrated via the response payload going to an allowed domain, or reflected in agent output.Recommendation (Medium): Consider adding the
!config.enableApiProxyguard toGITHUB_TOKEN/GH_TOKENforwarding (same pattern asCOPILOT_GITHUB_TOKEN). When--enable-api-proxyis active and GitHub API calls should be proxied, set a placeholder or route them through the sidecar.F-3:
--env-allPasses Arbitrary Host Credentials (Low–Medium, by design)Evidence:
EXCLUDED_ENV_VARScontains:PATH,PWD,OLDPWD,SHLVL,_,SUDO_*. It does not containAWS_ACCESS_KEY_ID,AZURE_CLIENT_SECRET,GCP_SA_KEY, etc.Analysis:
--env-allis an opt-in flag with a documented warning (src/cli.ts:1661-1662). However, on GitHub Actions runners or developer machines, the host environment often contains cloud provider credentials that would be silently forwarded into the container.Recommendation (Low): Document in
--env-allhelp text that cloud provider credentials (AWS, Azure, GCP) will be forwarded. Consider adding a--no-inherit-env (pattern)option or explicitly listing common cloud credential env var names inEXCLUDED_ENV_VARSwith a note that users can override with--env.F-4: No Request Rate Limiting at Squid Level (Low)
Evidence: No
delay_poolsormax_connectionsdirectives are present in the generatedsquid.conf(src/squid-config.ts).Analysis: An AI agent can make unlimited HTTP/HTTPS requests to allowlisted domains. A prompt-injected agent could saturate bandwidth, trigger rate limits on the allowlisted APIs (potentially denying service to legitimate users), or abuse services at high frequency.
Recommendation (Low): Consider adding opt-in
delay_poolsor per-hostmax_connectionsSquid directives, configurable via a--max-requests-per-domainCLI flag.📋 Evidence Collection
Commands run during this review
✅ Recommendations (Prioritized)
🔴 Critical
None identified.
🟠 High
None identified.
🟡 Medium
F-2 — GITHUB_TOKEN consistency: Add
!config.enableApiProxyguard toGITHUB_TOKEN/GH_TOKENforwarding insrc/docker-manager.ts:501-502, consistent with the existingCOPILOT_GITHUB_TOKENguard on line 511. If GitHub API calls still need to work, set a one-shot placeholder likeCOPILOT_GITHUB_TOKEN.F-1 — Custom AppArmor profile: Create a minimal AppArmor profile that allows
mountonly for typeprocwhile maintaining file-access controls. This replacesapparmor:unconfinedand restores AppArmor's MAC layer for theentrypoint.shwindow beforeSYS_ADMINis dropped.🔵 Low
F-3 — Document
--env-allcredential risks: Expand the--env-allwarning (src/cli.ts:1661) to enumerate common cloud credential patterns (AWS_*,AZURE_*,GOOGLE_*). Consider adding a--exclude-env-patternoption.F-4 — Rate limiting: Add optional
delay_pools/max_connectionssupport insrc/squid-config.tsto constrain per-domain request frequency.unsharein seccomp allowlist:unshareis present in the allowed syscalls. Whileno-new-privileges:truelimits user namespace creation, consider removing it unless a specific use case requires it.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions