Three things in this story present themselves as “AI did it,” and the work is counting them properly. Rex Factor went to the commit record, and the count comes back short: the credit on the merged commit and the security review that ran before it resolve to one GitHub App wearing two badges. The only separate system on the record belonged to the attacker. The distinction decides whether a second reader is a second opinion or an echo. — Muximus
On 23 June 2026, an autonomous agent operated by Wiz Research opened a GitHub issue on Snowflake’s public .NET connector repository with a crafted title, executed commands on the CI runner, and walked out with a Jira token. According to Wiz, the token authenticated as qa@snowflake.net against Snowflake’s internal Atlassian instance, with read access across its engineering, security compliance and bug bounty projects. The vulnerable pattern had been live five days.
That is the outcome. The durable part is the attribution: AI appears here in three roles - credited as co-author on the commit that shipped the flaw, reviewer of that pull request before the merge, and the attacker that found and exploited the result.
The first two are the same GitHub App. The trailer on the squash commit reads Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> - Copilot Autofix is a code-scanning feature of GitHub Advanced Security, and github-advanced-security[bot] is the account that posted the pre-merge review. Three roles, two systems.
The public record does not show an AI writing this vulnerability. It shows an AI reading it and not objecting, on a pull request that already carried its name.
The credit is a squash-merge inheritance
The squash commit that merged on 18 June 2026 carries that trailer. The line is real, and it is evidence of nothing about the code that broke.
PR #1218 began in August 2025 as a tidy-up of jira_close.yml. GitHub Advanced Security flagged a problem in that file, Copilot Autofix supplied the fix, and it landed as 6d0e2fa - “copilot suggestion” - where the trailer originates. On 25 August 2025 a separate commit, 094038e, rewrote a different file, jira_issue.yml, and removed the escaping, carrying no Copilot attribution. Ten months later the branch was squashed, and a squash inherits every co-author trailer in it - 4a1b8ce carries three, from three different authors.
Wiz says so itself: Copilot Autofix’s documented contribution was a separate fix to jira_close.yml within the same pull request. Whether 094038e was written with AI assistance is not established by the public record, and Wiz’s 19:57 UTC update on 17 August says the same - “It’s unclear whether the code-change was AI-assisted.” The post’s page title still reads “Missed by Github Copilot.”
The reviewer was the same app, on the other file
GitHub Advanced Security’s bot - badged “AI” in GitHub’s own interface - reviewed the final revision, 51dc738, on 18 June 2026. Its comment thread was on jira_close.yml, disposition Dismissed. Wiz’s body text puts the rest exactly: the scan extracted the vulnerable jira_issue.yml workflow and did not flag the injection. A Snowflake reviewer approved the pull request the same day and merged it with 83 of 87 checks passing.
Nothing in the record says why it missed, and the absence is the finding. ${{ github.event.issue.title }} in a run: block is the textbook GitHub Actions script-injection pattern, and on our reading it is legible as one from the merged file alone, with no repository history - a reading of the diff, not a finding any source here states. Neither GitHub nor Wiz explains the gap.
The app whose suggestion sits in the commit’s byline is the app that reviewed the pull request before it merged: same scanning system, same rules, same missing context about why the deleted code existed. The pull request got a second reader. It did not get a second party.
What went wrong: a guard that carried no marker
The old jira_issue.yml passed the issue title into an env: variable, escaped backticks explicitly, and built the Jira payload with jq -n --arg, where the title enters as a value, never as text spliced into a template. The rewrite deleted all of it and put the raw expression into the shell:
TITLE=$(echo '${{ github.event.issue.title }}' | sed 's/"/\\"/g' | sed "s/'/\\\'/g")
GitHub expands ${{ }} before bash runs, so the sed escaping arrives too late to matter: a single quote in an issue title closes the echo string, and whatever follows is a command on the runner.
The strongest receipt in the diff is a comment. # Escape special characters in title and body appears on both sides of it: the stated intent survived the cleanup, the mechanism that delivered it did not. Nothing marked the env: indirection and the jq --arg call as the escaping, so to a cold reader they are ceremony around a curl command - the pull request’s own description calls the workflows it was tidying “unecessarily complex.” A safe pattern with no comment explaining why it exists reads, to a reader without the history, as complexity worth removing.
One more line looks protective. It is a two-branch OR, and Wiz’s post prints only the branch that fires on issue events:
# second branch only, abridged from Wiz's post
if: (github.event_name == 'issues' && github.event.pull_request.user.login != 'whitesource-for-github-com[bot]')
On issues events github.event.pull_request is null, so the test is always true and every GitHub user passed it. The identical condition sits on both sides of the diff - this pull request did not introduce it. Same failure inverted, a few lines apart: a pattern that looks like a guard and is not, above one that was and did not look like it.
The attacker was the other system
Wiz sells cloud security, and this disclosure is also marketing for Red Agent, the autonomous tool that found the flaw. What follows is the vendor’s own uncorroborated account; the commits, dates and diff around it check out against the public repository.
On that account, Red Agent scanned Snowflake’s GitHub organization, flagged jira_issue.yml, and built a payload. The first attempt used # to comment out the rest of the line; the runner returned a bash syntax error, because the comment also consumed the closing parenthesis of TITLE=$(...). The agent read its own error, worked out it had to close the shell block rather than comment past it, rewrote the payload to use ; echo ', and got its callback within seconds - from a GitHub Actions runner at Azure IP 20.106.182.197, carrying base64-encoded credentials.
The recovery is the part that matters. A failed payload, an error message, a diagnosis, a corrected payload, no human anywhere in the sequence. Five days separated the pattern going live on 18 June from Wiz finding it on 23 June - live-to-discovery, not the time anyone took to fix anything.
What Snowflake got right
Wiz reported through Snowflake’s HackerOne program on 23 June 2026, report #3819931. Snowflake patched the same day, restoring the env: variables and rebuilding the payload with jq -n --arg, and rotated the Jira token on 24 June. It says its audit-log review found no third party in that window, all anomalous queries matching Wiz’s testing IPs - relayed by Wiz, not independently verifiable, and the reason this is a disclosure rather than a breach story. Its written statement to Wiz says the report was immediately investigated and remediated, with no evidence of unauthorized access. The patch also carries a comment above the restored jq call saying what it is for: the guard now says it is a guard.
The decision this leaves on the desk
The rule that survives is keyed on the shape of the change, not on who or what signed it: an agent may not replace a structured parser with direct string interpolation. jq --arg for echo, a prepared statement for a concatenated query - that substitution looks like simplification in every diff it appears in, and it is checkable in review, enforceable in a linter.
The review-parity question is harder. Wiz’s key takeaway asks that AI-generated pull requests get the same static analysis and scrutiny as human code - while its own correction says nobody can tell whether this one was. A triage rule keyed on that trailer would have flagged the wrong file, and the analysis it escalated to is the one that already ran and did not flag it.
So the question to take into a review process is not how many AI systems touched a change. It is how many independent parties reviewed it, whether the byline and the review came from the same vendor, and whether that review is logged as a check performed or a check attempted. Here the first two roles were one app under two badges, and the only separate system in the record was the one attacking.