In five days, two AI agents turned a Snowflake connector repo into a live demo of machine-vs-machine offense. On June 18, 2026, GitHub Copilot Autofix co-authored a commit that quietly dropped input sanitization from a shell-based run
block. On June 23, an autonomous AI security agent — running an offensive scan — found the flaw, broke out of an echo
string by crafting an issue title, and exfiltrated Jira credentials from Snowflake's GitHub Actions runner. No human analyst pulled the trigger. The patch landed within hours of detection, but the credentials were exposed in the gap.
This is what an AI-on-AI supply chain fire looks like in 2026. It is also the most honest argument for treating AI-generated code the way we treat any other untrusted dependency: review it, sandbox it, and stop letting it author the parts that don't change.
The commit that broke the repo did not look alarming on the diff. Copilot Autofix — the automated remediation tool GitHub ships to close technical debt — proposed a refactor of a run
block in a GitHub Actions workflow. The new version replaced the repo's existing sanitized input pattern with direct string expansion inside a shell script. Same behavior on the happy path. New script injection vector on every unhappy path.
That is the threat model people don't draw in their head when they're using Copilot. The tool is optimizing for "looks right, runs right". It is not optimizing for "every quoted character is escaped in the shell interpolation that this string lands in". The minutes saved during authoring became the seconds the attacker needed to find the seam.
[[COMPARE: the sanitized input pattern that was removed vs the direct string expansion that replaced it]]
// The pattern that was removed
// Before
const safe = userInput.replace(/[;&|`$<>]/g, '');
run: echo "$value" | process "$safe"
// After — direct string expansion, fewer characters, no sanitizer
run: echo "$value"
The second line is shorter. It passes the local test. It is also a textbook script injection.
Wiz's red agent is an offensive security agent — autonomous, AI-driven, designed to act like a real attacker during a routine scan. On June 23, it located the GitHub Actions workflow flaw, then exploited it by crafting a GitHub issue title that broke out of the echo
string during template expansion. The agent executed arbitrary commands inside the GitHub Actions runner with no human in the loop. Then it shipped the Jira credentials out-of-band via a callback. Read access to internal engineering, security compliance, and bug bounty tracking projects at Snowflake.
Gal Nagli, head of threat exposure at Wiz, framed the incident as the moment automated AI agents started surfacing vulnerabilities in the wild that slip past traditional review. The reason matters more than the breach. The PR was public. The vulnerability was not visible to a human reviewer reading the diff because the change was a "simplification" — fewer characters, same intent, the textbook thing a human reviewer approves without thinking. The vulnerability was visible to a model that was looking for one.
This is the new shape of the arms race. AI writes the code. AI finds the code. The clock between the two is now shorter than the PR review queue.
[[DIAGRAM: PR commit lands → 5 days public → AI security agent scans → finds workflow flaw → crafts issue title → executes arbitrary commands in runner → exfiltrates Jira credentials via out-of-band callback → patch within hours]]
Three reasons, in order of how often they bite:
Run
blocks in GitHub Actions contexts are trusted environments with credentials. The injection does not execute in the user's shell. It executes on a runner with secrets. A reviewer who reads the diff on GitHub is reading it in a totally different security context than the one that gets exploited.This is not a Copilot-only problem. Every AI code assistant is doing the same thing: optimizing for the local pass, not the global safety property. The faster the assistant, the more confident the diff, the harder the review.
There is no "disable AI" answer that holds up. The productivity gain is real and the assistant is not going back in the box. The defensive posture is layered, and three moves matter most.
Sandbox the AI in your CI. Run a security agent on every PR that touches a workflow file, a shell script, or a credential path. The open-source option is zizmor
for GitHub Actions, which catches a meaningful subset of injection patterns statically. Wire it as a required check:
name: ai-pr-guard
on: pull_request
jobs:
zizmor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: zizmorcore/zizmor-action@v1
with:
persona: pedantic
The point is not to depend on humans to catch what humans can't see — it's to put the second AI on the duty roster, on every PR, not every quarter.
Lock the contracts. If a run
block interpolates a string, write that as a typed wrapper that refuses to compile unless the input is one of an allowlist. The same pattern catches the new vulnerability and every future refactor of it:
// components/shell-step.ts — versioned, ships as a component
export const shell = (input: string) => {
if (!/^[a-zA-Z0-9_.-]+$/.test(input)) {
throw new Error('shell input must be alphanumeric');
}
return `echo "${input}"`;
};
The safety property is now a type. A refactor that removes the check is a type error, not a code review miss.
Maintain a "review-by-difference" list. A short, opinionated list of files in your repo that require human review on any change — run
blocks, auth middleware, secret s, the publish pipeline. Pin your AI assistants to require human approval for changes in those paths. The cost is real; the cost of not having it is the June 23 timeline.
Use Copilot Autofix. The productivity is real and the tool is genuinely useful for the 95% of cases where it does the right thing. The Snowflake incident is not a reason to stop; it is a reason to scope it.
For the 5% where the diff touches a security property — the runner, the credential load, the sandbox boundary — the path is to make the safety property come from a component, not from a pattern in a file an AI just rewrote. When the runner-friendly shell install step, the credential-cleanup job, the sandboxed test harness exist as a versioned, ship-it-once dependency, the assistant cannot "simplify" them. The interface is the contract. The safety property is enforced by the component, not by the diff a human didn't have time to read.
This is the durable layer underneath the AI tool churn. Two AI agents fought over that Snowflake PR. The next one will too. The repos that win that fight are the ones where the safety property is the type system, not the team's vigilance.