Summary
The shipped ai-moderator.md workflow, which runs on github/gh-aw itself,
combines four settings that together hand an unauthenticated GitHub user code
execution in the agent job. The agent job holds GITHUB_TOKEN and the engine API
keys. The compiler emits no warning for this combination. This is live on
github/gh-aw right now and reachable by any GitHub account with no special
access. It is the most immediately exploitable of the issues I found in this
review of gh-aw.
I verified this against current main at commit 966ccb8 (2026-07-12). It was
first verified at bdfdb8e (2026-06-24, ~v0.80.9); the intervening commits did
not change any of the four ingredients.
Details
gh-aw treats issue/PR/comment content from arbitrary GitHub users as
attacker-controlled, and its compiler normally env-indirects those expressions
out of shell contexts. This workflow disables the controls that enforce that
model. The four ingredients:
-
roles: all removes the activation gate. needsRoleCheck in
pkg/workflow/role_checks.go:348-353 returns false when roles is ["all"],
so the compiled lock has no check_membership step. In the compiled
ai-moderator.lock.yml the activation condition is just
rate_limit_ok && skip_roles_ok && skip_bots_ok. The workflow triggers on
issues: [opened], issue_comment: [created], and pull_request: [opened]
with forks: "*", all reachable by an external account.
-
tools.github.min-integrity: none defeats the author-association filter that
public repos otherwise default to approved
(actions/setup/js/determine_automatic_lockdown.cjs:57); a configured value
is never overridden (:61-67). threat-detection: false removes the
injection pre-screen. The prompt then instructs the agent to fetch raw
content: "Use the GitHub MCP server tools to fetch the original context ...
unsanitized content directly from GitHub API" and "Do NOT use the
pre-sanitized text from the activation job".
-
engine: codex compiles to codex exec --dangerously-bypass-approvals-and-sandbox
(pkg/workflow/codex_engine.go:238), so the agent auto-executes shell with
no approval. The other auto-executing engines are equivalent: gemini emits
--yolo (pkg/workflow/gemini_engine.go:181), copilot emits
--allow-all-tools (pkg/workflow/copilot_engine_tools.go:78). The AWF
firewall around the engine filters network egress only, not local execution.
-
An issue/PR/comment trigger reachable by arbitrary users (above).
There is no compile-time diagnostic for open activation + min-integrity: none
- an auto-executing engine.
PoC
I reproduced the full chain on my own repo on a real runner: an external issue
activates the agent with no membership check, the raw issue body is built into
the prompt, and the auto-approved agent runs shell via the engine and posts back
to the issue. I used the gemini engine for the live run because I had a free key
rather than codex credits; the mechanism is engine-independent, and the shipped
ai-moderator uses codex. Markers were benign (PWNED_*).
Impact
Any GitHub account can drive shell execution inside the agent job on a repo
running this pattern. Containment on most adopters: --exclude-env strips
engine/MCP keys, the agent token is read-only, egress is allowlisted. The last
step is XPIA (the model must follow the payload). Every control that would
normally make that unreliable is disabled by this configuration, the payload
surface is the whole body/diff, and the prompt instructs the model to trust raw
content.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L (7.5). Up to 9.1 (Critical) where
the agent has an arbitrary-shell tool.
Suggested fix
Fail the build, or emit a hard warning, when open activation is combined with
min-integrity: none/unapproved and an auto-executing engine. Stop shipping
samples that instruct the agent to fetch unsanitized content. Reconsider
bypassing the engine sandbox for agents that read untrusted input.
Summary
The shipped
ai-moderator.mdworkflow, which runs on github/gh-aw itself,combines four settings that together hand an unauthenticated GitHub user code
execution in the agent job. The agent job holds GITHUB_TOKEN and the engine API
keys. The compiler emits no warning for this combination. This is live on
github/gh-aw right now and reachable by any GitHub account with no special
access. It is the most immediately exploitable of the issues I found in this
review of gh-aw.
I verified this against current
mainat commit966ccb8(2026-07-12). It wasfirst verified at
bdfdb8e(2026-06-24, ~v0.80.9); the intervening commits didnot change any of the four ingredients.
Details
gh-aw treats issue/PR/comment content from arbitrary GitHub users as
attacker-controlled, and its compiler normally env-indirects those expressions
out of shell contexts. This workflow disables the controls that enforce that
model. The four ingredients:
roles: allremoves the activation gate.needsRoleCheckinpkg/workflow/role_checks.go:348-353returns false when roles is["all"],so the compiled lock has no
check_membershipstep. In the compiledai-moderator.lock.ymlthe activation condition is justrate_limit_ok && skip_roles_ok && skip_bots_ok. The workflow triggers onissues: [opened],issue_comment: [created], andpull_request: [opened]with
forks: "*", all reachable by an external account.tools.github.min-integrity: nonedefeats the author-association filter thatpublic repos otherwise default to
approved(
actions/setup/js/determine_automatic_lockdown.cjs:57); a configured valueis never overridden (
:61-67).threat-detection: falseremoves theinjection pre-screen. The prompt then instructs the agent to fetch raw
content: "Use the GitHub MCP server tools to fetch the original context ...
unsanitized content directly from GitHub API" and "Do NOT use the
pre-sanitized text from the activation job".
engine: codexcompiles tocodex exec --dangerously-bypass-approvals-and-sandbox(
pkg/workflow/codex_engine.go:238), so the agent auto-executes shell withno approval. The other auto-executing engines are equivalent: gemini emits
--yolo(pkg/workflow/gemini_engine.go:181), copilot emits--allow-all-tools(pkg/workflow/copilot_engine_tools.go:78). The AWFfirewall around the engine filters network egress only, not local execution.
An issue/PR/comment trigger reachable by arbitrary users (above).
There is no compile-time diagnostic for open activation +
min-integrity: nonePoC
I reproduced the full chain on my own repo on a real runner: an external issue
activates the agent with no membership check, the raw issue body is built into
the prompt, and the auto-approved agent runs shell via the engine and posts back
to the issue. I used the gemini engine for the live run because I had a free key
rather than codex credits; the mechanism is engine-independent, and the shipped
ai-moderator uses codex. Markers were benign (
PWNED_*).Impact
Any GitHub account can drive shell execution inside the agent job on a repo
running this pattern. Containment on most adopters:
--exclude-envstripsengine/MCP keys, the agent token is read-only, egress is allowlisted. The last
step is XPIA (the model must follow the payload). Every control that would
normally make that unreliable is disabled by this configuration, the payload
surface is the whole body/diff, and the prompt instructs the model to trust raw
content.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L (7.5). Up to 9.1 (Critical) where
the agent has an arbitrary-shell tool.
Suggested fix
Fail the build, or emit a hard warning, when open activation is combined with
min-integrity: none/unapprovedand an auto-executing engine. Stop shippingsamples that instruct the agent to fetch unsanitized content. Reconsider
bypassing the engine sandbox for agents that read untrusted input.