π΄ Red Team Security Audit
Audit focus: Category F (Supply Chain) + Category D (Credential/Privilege Exposure via MCP Docker config)
Severity: Medium
Findings
| # |
Vulnerability |
Severity |
File(s) |
Exploitable? |
| 1 |
Dangerous MCP Docker flags compile to warnings, not errors |
Medium |
src/compile/standalone.rs:679-685 |
Yes (requires write access to pipeline def) |
| 2 |
--userns, --cgroupns, --device missing from DANGEROUS_DOCKER_FLAGS |
Medium |
src/compile/standalone.rs:796-806 |
Yes (no warning emitted at all) |
| 3 |
MCPG + AWF Docker images pinned by tag only, no @sha256 digest |
Medium |
templates/base.yml:202-204, src/compile/common.rs:731-734 |
Theoretical (requires registry compromise) |
Details
Finding 1: Dangerous MCP Docker flags compile as warnings, not errors
Description: The functions validate_docker_args() and validate_mount_source() detect dangerous Docker flags (--privileged, --cap-add, --security-opt, --network, --pid, -u) and sensitive mounts (docker socket, /etc, /root, etc.) but only emit eprintln! warnings. Compilation succeeds and the dangerous flags are serialized verbatim into the MCPG JSON config (McpgServerConfig.args / .mounts fields).
Attack vector: An author with write access to a pipeline definition (or a reviewer who doesn't inspect stderr warnings during CI compilation) can include:
mcp-servers:
escape-tool:
container: "node:20-slim"
entrypoint: "node"
entrypoint-args: ["server.js"]
args: ["--privileged"] # β only a warning, not a compile error
mounts: ["/:/host:rw"] # β only a warning, not a compile error
```
At runtime, MCPG spawns the MCP container as:
```
docker run --privileged -v /:/host:rw node:20-slim ...
The privileged container has full host access, defeating AWF's L7 network isolation.
Proof of concept:
- Add the above
mcp-servers: block to any agent markdown file
- Run
ado-aw compile agent.md β compilation succeeds with only a warning to stderr
- Deploy the pipeline
- The MCP container spawned at runtime has
--privileged and a full host bind-mount
Impact: Container escape from the AWF sandbox. An attacker inside the MCP server can access the host filesystem, exfiltrate secrets, pivot to other containers, or disable network isolation.
Suggested fix: Change validate_docker_args and validate_mount_source to return Result<()> and bail! on dangerous flags instead of returning warnings. Fail compilation with a hard error for --privileged, --cap-add, and bind mounts of sensitive paths. Warnings can remain for lower-risk flags.
Finding 2: --userns, --cgroupns, --device missing from DANGEROUS_DOCKER_FLAGS
Description: The DANGEROUS_DOCKER_FLAGS constant covers --privileged, --cap-add, --security-opt, --pid, --network, --ipc, --user, -u, --add-host, --entrypoint. However it omits several flags that can be used to escape or weaken container isolation with no warning at all:
--userns host β disables user-namespace isolation, maps container root to host root
--cgroupns host β shares the host cgroup namespace
--device /dev/mem or --device /dev/kmem β direct hardware access
--cgroup-parent β places container in an arbitrary host cgroup
Proof of concept:
args: ["--userns", "host"] # No warning emitted during compilation
Impact: --userns host effectively gives the container root process the same UID 0 as the host, bypassing the user-namespace boundary that limits container escapes. Combining this with a writable mount of /proc or a device file could lead to host compromise.
Suggested fix: Add --userns, --cgroupns, --device, --cgroup-parent to DANGEROUS_DOCKER_FLAGS. Also change dangerous flag detection to fail compilation rather than warn (see Finding 1).
Finding 3: Docker images pinned by tag only (no digest)
Description: Three Docker images used in generated pipelines are referenced by tag only, not by immutable @sha256 digest:
| Image |
Reference in code |
ghcr.io/github/gh-aw-mcpg |
src/compile/common.rs:734, MCPG_VERSION = "0.2.19" β :v0.2.19 |
ghcr.io/github/gh-aw-firewall/squid |
templates/base.yml:200 β :\{\{ firewall_version }} |
ghcr.io/github/gh-aw-firewall/agent |
templates/base.yml:201 β :\{\{ firewall_version }} |
If a tag is overwritten (accidentally or via a supply-chain compromise of ghcr.io/github), every pipeline compiled from that version of ado-aw would silently pull and execute the new (potentially malicious) image. Unlike the ado-aw and awf binaries (which are verified via sha256sum -c checksums.txt), Docker images have no integrity check beyond the tag.
Proof of concept: An attacker with write access to ghcr.io/github/gh-aw-mcpg pushes a malicious image tagged v0.2.19. All pipelines running docker pull ghcr.io/github/gh-aw-mcpg:v0.2.19 would get the malicious image.
Impact: Arbitrary code execution inside the CI runner as part of MCPG startup, with access to the Docker socket and host network.
Suggested fix: Pin images by digest in the compiled pipeline output:
# Instead of:
docker pull ghcr.io/github/gh-aw-mcpg:v0.2.19
# Use:
docker pull ghcr.io/github/gh-aw-mcpg@sha256:<pinned-digest>
The MCPG_VERSION/MCPG_IMAGE constants in common.rs can be extended with a corresponding MCPG_DIGEST constant. At a minimum, a docker inspect after pull should verify the expected digest.
Audit Coverage
| Category |
Status |
| A: Input Sanitization & Injection |
β
Scanned β all prior findings fixed |
| B: Path Traversal & File System |
β
Scanned β no vulnerabilities found |
| C: Network & Domain Allowlist Bypass |
β
Scanned β all prior findings fixed |
| D: Credential & Secret Exposure |
β
Scanned β token handling correct; MCP flag issues above |
| E: Logic & Authorization Flaws |
β
Scanned β budget/allowlist enforcement correct |
| F: Supply Chain & Dependency Integrity |
β
Scanned β binary checksums OK; Docker image pinning gap |
This issue was created by the automated red team security auditor.
Generated by Red Team Security Auditor Β· β 3.3M Β· β·
π΄ Red Team Security Audit
Audit focus: Category F (Supply Chain) + Category D (Credential/Privilege Exposure via MCP Docker config)
Severity: Medium
Findings
src/compile/standalone.rs:679-685--userns,--cgroupns,--devicemissing fromDANGEROUS_DOCKER_FLAGSsrc/compile/standalone.rs:796-806@sha256digesttemplates/base.yml:202-204,src/compile/common.rs:731-734Details
Finding 1: Dangerous MCP Docker flags compile as warnings, not errors
Description: The functions
validate_docker_args()andvalidate_mount_source()detect dangerous Docker flags (--privileged,--cap-add,--security-opt,--network,--pid,-u) and sensitive mounts (docker socket,/etc,/root, etc.) but only emiteprintln!warnings. Compilation succeeds and the dangerous flags are serialized verbatim into the MCPG JSON config (McpgServerConfig.args/.mountsfields).Attack vector: An author with write access to a pipeline definition (or a reviewer who doesn't inspect stderr warnings during CI compilation) can include:
The privileged container has full host access, defeating AWF's L7 network isolation.
Proof of concept:
mcp-servers:block to any agent markdown fileado-aw compile agent.mdβ compilation succeeds with only a warning to stderr--privilegedand a full host bind-mountImpact: Container escape from the AWF sandbox. An attacker inside the MCP server can access the host filesystem, exfiltrate secrets, pivot to other containers, or disable network isolation.
Suggested fix: Change
validate_docker_argsandvalidate_mount_sourceto returnResult<()>andbail!on dangerous flags instead of returning warnings. Fail compilation with a hard error for--privileged,--cap-add, and bind mounts of sensitive paths. Warnings can remain for lower-risk flags.Finding 2:
--userns,--cgroupns,--devicemissing fromDANGEROUS_DOCKER_FLAGSDescription: The
DANGEROUS_DOCKER_FLAGSconstant covers--privileged,--cap-add,--security-opt,--pid,--network,--ipc,--user,-u,--add-host,--entrypoint. However it omits several flags that can be used to escape or weaken container isolation with no warning at all:--userns hostβ disables user-namespace isolation, maps container root to host root--cgroupns hostβ shares the host cgroup namespace--device /dev/memor--device /dev/kmemβ direct hardware access--cgroup-parentβ places container in an arbitrary host cgroupProof of concept:
Impact:
--userns hosteffectively gives the container root process the same UID 0 as the host, bypassing the user-namespace boundary that limits container escapes. Combining this with a writable mount of/procor a device file could lead to host compromise.Suggested fix: Add
--userns,--cgroupns,--device,--cgroup-parenttoDANGEROUS_DOCKER_FLAGS. Also change dangerous flag detection to fail compilation rather than warn (see Finding 1).Finding 3: Docker images pinned by tag only (no digest)
Description: Three Docker images used in generated pipelines are referenced by tag only, not by immutable
@sha256digest:ghcr.io/github/gh-aw-mcpgsrc/compile/common.rs:734,MCPG_VERSION = "0.2.19"β:v0.2.19ghcr.io/github/gh-aw-firewall/squidtemplates/base.yml:200β:\{\{ firewall_version }}ghcr.io/github/gh-aw-firewall/agenttemplates/base.yml:201β:\{\{ firewall_version }}If a tag is overwritten (accidentally or via a supply-chain compromise of
ghcr.io/github), every pipeline compiled from that version ofado-awwould silently pull and execute the new (potentially malicious) image. Unlike theado-awandawfbinaries (which are verified viasha256sum -c checksums.txt), Docker images have no integrity check beyond the tag.Proof of concept: An attacker with write access to
ghcr.io/github/gh-aw-mcpgpushes a malicious image taggedv0.2.19. All pipelines runningdocker pull ghcr.io/github/gh-aw-mcpg:v0.2.19would get the malicious image.Impact: Arbitrary code execution inside the CI runner as part of MCPG startup, with access to the Docker socket and host network.
Suggested fix: Pin images by digest in the compiled pipeline output:
The
MCPG_VERSION/MCPG_IMAGEconstants incommon.rscan be extended with a correspondingMCPG_DIGESTconstant. At a minimum, adocker inspectafter pull should verify the expected digest.Audit Coverage
This issue was created by the automated red team security auditor.