GHSA-j77w-g4jj-hp99
Command Injection in Compiled Workflow via Unsanitized sandbox.mcp.env Exports
01Record
| Identifier | GHSA-j77w-g4jj-hp99 |
|---|---|
| Project | gh-aw |
| Component | pkg/workflow/mcp_setup_generator.go |
| CWE | CWE-78 |
| CVSS | 9.6 |
| Disclosed | 2026-08-07 |
| Endor reference | ENDOR-VUL-2026-0906 |
02Detail
Description: gh aw compile writes MCP-gateway environment variables into the generated GitHub Actions workflow (.lock.yml) through an unescaped fmt.Fprintf:
for _, envVarName := range envVarNames {
fmt.Fprintf(yaml, " export %s=%s\n", envVarName, gatewayConfig.Env[envVarName])
}
The values arrive verbatim from the sandbox.mcp.env map in a workflow's frontmatter, and the workflow JSON schema constrains no characters in them. A value carrying ;, $(...), a backtick, or a newline breaks out of the export statement and lands in command position inside a run: shell block.
The adjacent export ten lines earlier does the right thing, which is what makes this a miss rather than a design choice:
escapedCLIServersJSON := shellEscapeArg(string(cliServersJSON))
yaml.WriteString(" export GH_AW_MCP_CLI_SERVERS=" + escapedCLIServersJSON + "\n")
Nothing validates the source either: frontmatter_extraction_security.go copies both key and value straight out of the parsed map.
The trust boundary is what carries the severity. gh-aw supports importing and packaging workflow components from third-party repositories, so a malicious shared component that declares sandbox.mcp.env executes arbitrary commands in the CI environment of any repository that imports it, compiles, and runs the workflow. Compiling a payload of three values in a sealed no-network container produced this in the generated lock file:
export AAA_INJECT=legit; echo PWNED_$(id) > /tmp/pwned #
export BBB_NEWLINE=ok
echo PWNED_NEWLINE <- newline broke into command position
export CCC_BACKTICK=`touch /tmp/PWNED_BACKTICK`
Affects gh-aw through 0.77.5.
Impact: Command Execution in the GitHub Actions runner, with the job's GITHUB_TOKEN and secrets. Scope is Changed: the injected commands escape the authority of a workflow definition into the runner's execution environment.
Fixed in 0.86.0. No CVE was assigned, and the advisory is published at the repository rather than in the global GitHub Advisory Database.
Discovered by Peyton Kennedy (p80n).