← All posts / Research

The Autofix That Broke In: Copilot-Generated Patch Let an AI Red Team Steal Snowflake's Jira Token

A GitHub Copilot Autofix commit stripped a safe input-sanitization pattern from a Snowflake repo and opened a shell-injection hole — which Wiz's autonomous Red Agent found, exploited, and reported within five days, exfiltrating an internal Jira token before Snowflake patched it same-day.

The Autofix That Broke In: Copilot-Generated Patch Let an AI Red Team Steal Snowflake's Jira Token

On June 18, 2026, a pull request landed in snowflake-connector-net, one of Snowflake’s public GitHub repositories. Titled “SNOW-2069227: Update jira workflows,” it looked like routine maintenance — a small cleanup of a Jira integration workflow. The commit was co-authored by GitHub Copilot Autofix, the AI feature that automatically proposes fixes for code-scanning alerts.

Five days later, an autonomous AI security agent had used that change to execute arbitrary commands inside Snowflake’s GitHub Actions runner and exfiltrate a live Jira API token.

The full write-up, published August 17 by Wiz Research’s Gal Nagli, is one of the cleanest case studies yet of the strange new loop the industry now operates in: AI writes insecure code, AI finds it, AI exploits it, and AI — on the other side — helps patch it. Every step of the attack chain, on both sides, was machine-driven.

What the Autofix actually did

The vulnerable workflow, jira_issue.yml, fires whenever a GitHub issue is opened on the repository. Its job is mundane: take the issue title and file it as a Jira ticket.

Before the Copilot-authored commit, the workflow did this safely. The issue title was passed through an env: block into an environment variable, and the JSON payload for Jira was constructed with jq --arg — a structured parser that treats the input as data, never as code.

PR #1218 removed that pattern. In its place, the Autofix commit interpolated the raw issue title directly into a shell run: block:

TITLE=$(echo '${{ github.event.issue.title }}' | sed ...)

This is the classic GitHub Actions script-injection anti-pattern. GitHub expands ${{ ... }} template expressions before the shell ever runs, and the sed escaping happens after expansion — too late to matter. A single quote in an issue title breaks out of the echo '...' string, and everything after it executes as arbitrary shell commands.

In other words: the repository already contained an explicitly engineered defense against exactly this attack, and an automated “fix” removed it — likely because the AI lacked the historical context for why that seemingly clunky env: + jq pattern existed in the first place.

The security gate that wasn’t

The workflow did include a conditional check that looked protective:

if: (github.event_name == 'issues' && github.event.pull_request.user.login != 'whitesource-for-github-com[bot]')

On issues events, however, github.event.pull_request is always null. The condition therefore reduces to null != 'whitesource-for-github-com[bot]' — which is always true. Every GitHub user passed the gate. Any anonymous account could trigger the workflow simply by opening an issue.

Enter the Red Agent

Wiz Research runs “Red Agent,” an autonomous, AI-powered security research tool, against targets enrolled in vulnerability disclosure programs — in this case Snowflake’s HackerOne bug bounty. While scanning Snowflake’s GitHub organization, the agent flagged the jira_issue.yml workflow as vulnerable to script injection via untrusted input in run: blocks. No human pointed it there.

What happened next is the most instructive part of the report. Red Agent’s first exfiltration payload used a # character to comment out the trailing shell syntax. The runner threw a bash error — the comment had also swallowed the closing parenthesis of TITLE=$(...). Rather than failing or stalling, the agent analyzed the syntax error, reasoned about the shell grammar, adjusted its payload to use ; echo ' to properly close the block, and re-ran it.

Seconds later, Wiz’s out-of-band listener received a callback from a GitHub Actions runner on an Azure IP, carrying base64-encoded values of JIRA_API_TOKEN, JIRA_USER_EMAIL, and JIRA_BASE_URL.

The token belonged to qa@snowflake.net and authenticated to snowflakecomputing.atlassian.net with read access across Snowflake’s engineering, security compliance, and bug bounty tracking projects — a meaningful foothold for anyone with less friendly intentions.

Five days, responsible disclosure, clean forensics

The timeline compresses everything that matters about modern security operations:

  • June 18 — Injection pattern introduced by commit 4a1b8ce (PR #1218), co-authored by Copilot Autofix.
  • June 23 — Wiz identifies, exploits, and reports the vulnerability via HackerOne (report #3819931).
  • June 23, same day — Snowflake patches the workflow in PR #1402, restoring the safe env: + jq --arg pattern.
  • June 24 — The Jira token is rotated.
  • August 17 — Public disclosure.

Snowflake’s audit logs confirmed that Wiz was the sole actor during the five-day exposure window, and Wiz deleted all data accessed during proof-of-concept testing. In a statement, Snowflake said it found no evidence of unauthorized access and is working with Wiz “to share these learnings with the broader industry.”

Why this matters beyond one repo

Three takeaways from the incident deserve emphasis.

AI-generated PRs are not exempt from review. Copilot Autofix predicts code from probabilistic patterns; it can resurrect deprecated, insecure idioms while genuinely intending to “fix” something. Wiz’s bottom line is blunt: AI-generated PRs must undergo the same static analysis and human security scrutiny as human code. An automated change is still a change.

Discovery windows are collapsing. The vulnerability was live for five days before an automated agent found and validated it. Attack tooling with the same capability doesn’t need a bug bounty program as an excuse to scan public repos. The practical consequences are shorter-lived credentials, faster patch cycles, and the assumption that anything exposed in CI/CD configuration is being read by machines around the clock.

Guardrails need to protect good patterns from “cleanup.” The safe pattern in this repo existed precisely to prevent shell injection, and it was removed by a tool that saw it as noise. Wiz recommends automated checks that block AI agents from replacing structured parsers like jq --arg with direct string interpolation — a policy that generalizes well beyond GitHub Actions.

The defender’s side of the ledger

The disclosure lands in the same news cycle as OpenAI president Greg Brockman’s essay “The Defender’s Window” (August 16), which called the recent OpenAI–Hugging Face agentic breach a “watershed moment” showing how threat-actor capabilities will evolve in the coming months. Brockman’s argument is that the same AI capabilities squeezing attackers’ economics can favor defenders — if organizations move now. As a personal test, he had ChatGPT Work audit his own static website: it surfaced 13 issues in 15 minutes and fixed them within the hour, from DMARC rollout to dropping an outdated jQuery dependency.

The Snowflake incident is the two-sided coin in miniature. An AI introduced the vulnerability; an AI discovered, weaponized, and reported it; humans set the rules of engagement (a disclosure program, short-lived credentials, audit logging) that kept the outcome benign. That division of labor — machines at machine speed, humans holding the high-impact decisions — is roughly the equilibrium both Wiz and Brockman are pointing toward.

For engineering teams, the actionable list is short. Treat AI-authored commits like any other untrusted contributor. Scan run: blocks in GitHub Actions workflows for ${{ github.event.* }} interpolation of user-controlled fields. Prefer environment indirection plus structured parsing. Rotate CI secrets on a schedule measured in days, not quarters. And assume the scanning is already happening — because on Snowflake’s repos, it was.