Сгенерированная абстрактная иллюстрация на тёмно-грифельном поле: длинная горизонтальная лента тонких бледно-голубых и янтарных световых линий проходит от края до края, разрывается в одной точной точке около центра, а один яркий янтарный осколок устремляется вверх в пустой тёмный канал над ней; слабые концентрические окружности и разлинованные линии сетки намекают на пайплайн. Текст, люди и логотипы организаций отсутствуют. Логотип OrcaRouter наложен в правом нижнем углу.
AI Safety Incidents

Jira-токен Snowflake: уязвимость, которую в соавторстве подписал Copilot, а человек, по данным GitHub, её написал

Автор

Gideon Frost

Дата публикации

Новые модели · 20Все модели →
Бенчмарки: Artificial Analysis · обновляется ежедневно
Назад ко всем статьям

23 июня 2026 года Red Agent, автономный агент безопасности, созданный Wiz, открыл issue в публичном репозитории GitHub, принадлежащем Snowflake, и получил учетные данные. Репозиторием был snowflakedb/snowflake-connector-net, а workflow .github/workflows/jira_issue.yml, в котором была ошибка, предназначен для создания тикета в Jira всякий раз, когда кто-либо открывает issue, — он встраивал заголовок тикета в shell-команду вместо того чтобы передавать его через переменную окружения, поэтому специально составленный заголовок выполнялся как код на раннере GitHub Actions. Токен, полученный Red Agent, аутентифицировался как qa@snowflake.net на snowflakecomputing.atlassian.net и имел доступ на чтение к Jira-проектам Snowflake в области инженерии, соответствия требованиям безопасности и bug-bounty. Уязвимость оставалась активной пять дней. Wiz опубликовала полный анализ 17 августа 2026 года. Этот инцидент внесен в каталог как запись 2026-08-17-snowflake-jira-zhao-dao-can в Orca AI Incident Archive, который содержит полный набор записей с оценками уверенности и указанными источниками.

Интерполяция и защита, которая так и не сработала

Рабочий процесс срабатывал по событию GitHub «issues: opened». Любой пользователь с аккаунтом GitHub мог запустить его, открыв issue в публичном репозитории — без приглашения, без роли мейнтейнера и без форка. Уязвимый шаг считывал заголовок issue в переменную оболочки: TITLE=$(echo '${{ github.event.issue.title }}' | sed ...), с двумя фильтрами, которые должны были экранировать двойные и одинарные кавычки до сборки строки.

Проблема заключается в порядке операций, а не в экранировании. GitHub подставляет содержимое выражения в текст скрипта ещё до того, как bash начнёт его разбор, поэтому одиночная кавычка внутри заголовка issue закрывает заключённую в кавычки строку, которую удерживает открытой echo. Два фильтра sed выполняются внутри того же скрипта после подстановки и не могут исправить кавычки, которые уже изменили разбор строки. В отчёте Wiz прямо указано, что экранирование происходит слишком поздно, чтобы предотвратить выход за пределы, и что заголовок, составленный с закрывающей кавычкой и точкой с запятой, добавляет команды по выбору атакующего к шагу run.

Второй контроль должен был сузить аудиторию, но не сделал этого. В задании имелось условие if, сравнивающее github.event.pull_request.user.login с логином бота whitesource-for-github-com, очевидно, чтобы автоматические пул-реквесты зависимостей не создавали тикеты. В событии issues объекта пул-реквеста вообще нет, поэтому сравнение идёт со свойством со значением null. Собственная документация GitHub, цитируемая The Hacker News, утверждает, что разыменование несуществующего свойства даёт пустую строку; анализ Wiz приходит к тому же результату через значение null. В любом случае условие оказывалось истинным для каждого пользователя, и исключение для бота превратилось в пропуск для всего интернета.

То, что делало эту инъекцию достойной сообщения, а не рутинной, — это её окружение. На том же шаге JIRA_BASE_URL, JIRA_USER_EMAIL и JIRA_API_TOKEN хранились как переменные среды, поэтому при произвольном выполнении команд на раннере учётные данные оказывались под рукой, а не на пустой основе. Ни один выпуск Snowflake Connector for .NET не был признан затронутым: слабость находилась в автоматизации CI/CD репозитория, а не в поставляемом коде коннектора.

Что взял Red Agent и что, по его словам, он выбросил

Рассказ Wiz об эксплуатации — это то, что отличает данный случай от находки сканера. Первая полезная нагрузка использовала символ решётки, который закомментировал закрывающую скобку присваивания TITLE=$(...), и вызвала синтаксическую ошибку bash вместо колбэка. Агент прочитал ошибку и переписал полезную нагрузку так, чтобы закрыть блок shell точкой с запятой и новой кавычкой echo, после чего получил внеполосный вызов. Полезная нагрузка использовала curl для обращения к поддомену oast.me с base64-кодированными значениями трёх переменных учётных данных, а колбэк пришёл с IP-адреса Azure 20.106.182.197, который Wiz приписывает раннеру GitHub Actions.

Возвращённый им токен был API-токеном Jira для qa@snowflake.net, а Wiz описывает его охват как доступ на чтение к проектам engineering, security-compliance и отслеживания bug-bounty в облачном экземпляре Snowflake в Atlassian. Вот в чём вред, который фиксирует архив: не выполнение кода на сборочной машине на протяжении пяти дней, а учётные данные, позволявшие читать очереди тикетов, куда подаются неисправленные инженерные и security-баги — включая, по описанию Wiz, трекер bug-bounty.

A self-built HTML card titled 'What the run step could reach', badged 'snowflakedb/snowflake-connector-net · .github/workflows/jira_issue.yml · 18-23 June 2026'. A left panel lists the environment variables held by the step: JIRA_BASE_URL, JIRA_USER_EMAIL, JIRA_API_TOKEN. A right panel reads: identity qa@snowflake.net; Jira host snowflakecomputing.atlassian.net; access, read across engineering, security-compliance and bug-bounty Jira projects. Three tiles read: trigger 'issues: opened'; account required 'any GitHub user'; token rotated '24 June 2026'. A footer cites Wiz Research, published 17 August 2026, and notes no CVE identifier accompanies the flaw.

Wiz заявляет, что безопасно удалила всё, к чему получила доступ в ходе проверки концепции. Snowflake в заявлении для CSO Online сообщила, что этот отчёт «был незамедлительно расследован и устранён», и что «наше расследование не выявило доказательств несанкционированного доступа»; в блоге Wiz также отмечается, что Snowflake проверила журналы аудита за пятидневный период и не обнаружила использования третьими сторонами помимо собственных тестовых адресов Wiz. Snowflake не опубликовала эти журналы, поэтому вывод об отсутствии других участников опирается на её объяснения и на резюме Wiz по этому поводу, а не на документы, которые может прочитать кто-либо за пределами обеих организаций.

Кто написал уязвимую строку

Этот вопрос породил вторую, более масштабную историю. PR #1218 под названием «SNOW-2069227: Update jira workflows» 18 июня 2026 года был объединён squash-мержем как коммит 4a1b8ce, и в списке соавторов squash-коммита указан Copilot Autofix powered by AI. Оригинальный заголовок Wiz строился именно на этом: уязвимость описывалась как находящаяся в пул-реквесте, сделанном с помощью GitHub Copilot, и ранние отраслевые публикации пошли по той же линии — заголовок SC Media звучал так: «Агент Wiz находит уязвимость в репозитории Snowflake в коде, соавтором которого является GitHub Copilot Autofix».

Позиция GitHub, о которой сообщает The Next Web и которая кратко изложена в исследовательской заметке Cloud Security Alliance, такова: его внутренняя проверка показала, что «человек написал изменения, которые привели к уязвимости», а Copilot Autofix «не проверял их и не участвовал в их создании». Техническое обоснование этого опровержения находится в истории git. Squash-слияние сводит пул-реквест к одному коммиту и переносит все трейлеры соавторов независимо от того, какие строки затрагивал этот автор, поэтому строка соавтора фиксирует участие в пул-реквесте, а не авторство конкретного фрагмента. Коммит с соавторством Copilot в том же пул-реквесте, 6d0e2fa, изменил jira_close.yml — другой workflow, нежели уязвимый jira_issue.yml. Небезопасная интерполяция приписывается коммиту 094038e от 25 августа 2025 года, который GitHub приписывает инженеру Snowflake sfc-gh-hpathak; PR #1218 поместил этот паттерн в ветку по умолчанию в июне 2026 года.

Wiz не отозвал находку и изменил формулировку в день публикации. В блоге Wiz опубликовано обновление от 19:57 UTC 17 августа, в котором Copilot описывается как «соавтор, который проверил объединённый PR и изменение кода» и «признал его полностью безопасным», не заметив уязвимостей, и признаётся, что «неясно, было ли изменение кода сделано с помощью ИИ». В этом изложении ошибка ИИ относится к проверке, а не к авторству: объединённый пул-реквест нёс соавторскую подпись Copilot, и инъекция сохранилась. CSO Online цитирует технического директора Wiz Ами Луттвака, который говорит, что чёткое определение авторства между людьми и ИИ «становится немного сложнее установить» и что «просто смотреть на соавторов PR недостаточно».

A self-built HTML card titled 'Two accounts of one commit', badged 'PR #1218 · SNOW-2069227 · squash-merged 18 June 2026 as commit 4a1b8ce'. A cyan-headed left panel, 'GitHub, internal review', reads: a human wrote the contributions that led to the vulnerability; Copilot Autofix neither reviewed nor contributed to them; Copilot's co-authored commit 6d0e2fa changed jira_close.yml, not the vulnerable file; the unsafe pattern traces to commit 094038e of 25 August 2025; squash-merging carries each co-author trailer forward regardless of which lines that author touched. An amber-headed right panel, 'Wiz, blog update, 17 August 2026, 19:57 UTC', reads: Copilot was a co-author that checked the merged PR and code change; it identified it as all-clear without noticing the vulnerabilities; it is unclear whether the code-change was AI-assisted; Copilot Autofix is listed among the squash commit's co-authors; the post was revised on the evening of publication without retracting the finding. Below, an open-question strip asks whether Copilot reviewed jira_issue.yml inside PR #1218, and a dashed band lists what is not in dispute.

Что объединяет две версии, стоит отделять от того, в чём они расходятся. Обе стороны сходятся в том, что squash-коммит содержит трейлер соавторства Copilot Autofix и что workflow можно было эксплуатировать. Никто не оспаривает, что GitHub Advanced Security сканировал финальную ревизию пул-реквеста, извлёк jira_issue.yml и не выявил инъекцию — Wiz называет это пропуском, а опровержение GitHub касается авторства, а не сканирования. Неразрешённым остаётся вопрос, просматривал ли Copilot уязвимые строки вообще: Wiz утверждает, что проверка выполнялась и пропустила их, GitHub говорит, что инструмент не имел к ним отношения, а логи, которые могли бы разрешить этот вопрос, находятся у одной из двух компаний. В материале приводятся обе версии, а не выбирается одна.

Пять дней — и что изменилось после

Исправление было выполнено быстро после поступления отчёта. Компания Wiz 23 июня отправила отчёт через программу Snowflake на HackerOne под номером #3819931 и в тот же день уведомила службу безопасности Snowflake через Slack; Snowflake приняла PR #1402, коммит 1dc7766, который заменил прямое раскрытие выражений переменными окружения, передаваемыми jq в качестве аргументов. Хронология Wiz относит ротацию токена Jira к 24 июня, и The Hacker News сообщает о том же расхождении. Во введении к отчёту самого Wiz, напротив, говорится, что Snowflake устранила уязвимость и провела ротацию учётных данных «в тот же день» — расхождение внутри первоисточника, которое запись не должна замалчивать. Исправление было внедрено 23 июня; в таблице Wiz ротация датирована 24 июня.

График раскрытия информации определяла Snowflake: Wiz называет крайний срок публичного раскрытия — 25 июля 2026 года, через тридцать дней после решения от 25 июня в соответствии с политикой программы, а анализ был опубликован 17 августа. В заявлении для CSO Online Snowflake сообщила, что компания «ценит ответственный подход Wiz к раскрытию информации» и что она работает с Wiz, чтобы поделиться извлечёнными уроками с отраслью. Ни анализ Wiz, ни материал The Hacker News не содержит идентификатора CVE, оценки CVSS или записи в каталоге известных эксплуатируемых уязвимостей CISA; уязвимость представляла собой конфигурацию рабочего процесса в публичном репозитории, а не поставляемую библиотеку, поэтому она так и не попала в базу данных уязвимостей обычным путём.

A self-built HTML card titled 'Five days live, five weeks to publication', subtitled 'Every date below is day-precision, as reported by Wiz Research and The Hacker News'. A vertical timeline lists six entries: 25 August 2025, commit 094038e introduces the unsafe jira_issue.yml refactor, attributed by GitHub to a Snowflake engineer; 18 June 2026, PR #1218 squash-merged as 4a1b8ce and the flaw goes live on the default branch; 23 June 2026, Wiz reports via HackerOne #3819931 and Snowflake merges fix PR #1402 as 1dc7766; 24 June 2026, Jira API token rotated, the fix having landed a day earlier; 25 July 2026, public disclosure deadline under Snowflake's HackerOne policy; 17 August 2026, Wiz publishes the full analysis, blog updated at 19:57 UTC. An amber band reads 'Exposure window: five days, 18 to 23 June 2026'. A footer notes no CVE identifier, CVSS score or known-exploited entry accompanies the record.

Что подтверждают материалы дела, а что — нет

Архив классифицирует это как исследовательскую находку, а не как событие развёртывания, с высокой критичностью, оценкой уверенности A, подтверждённым участием ИИ и зафиксированным реальным ущербом; его дата — 17 августа 2026 года — день публикации Wiz, а сам инцидент охватывает период с 18 по 23 июня. Установленное ядро узкое и проверяемое: публичный рабочий процесс подставлял в shell-команду текст, контролируемый злоумышленником, его защитное условие сравнивалось со свойством, которое в этом событии равно null, токен Jira находился на том же шаге, автономный агент воспользовался этим, а Snowflake в течение суток внесла исправление через слияние и ротировала учётные данные.

Части, которые являются скорее выводом, чем документальным свидетельством, — именно их неформальные пересказы обычно сглаживают. Copilot Autofix документирован как соавтор пул-реквеста и не документирован как автор уязвимых строк; трактовка «ИИ против ИИ», на которой держался первый новостной цикл, была частично отозвана публикующей организацией в течение нескольких часов и опровергнута GitHub. Проверял ли ИИ-обзор объединённого пул-реквеста файл jira_issue.yml — это всё ещё открытый фактический вопрос, а не решённый. А объём доступа, хотя и подробно описан Wiz, не был независимо проверен: ни разрешения Jira, ни запуск workflow, ни запись аудита не были обнародованы.

Схема исправления — та самая, которую собственные рекомендации GitHub предлагали с июля 2025 года: передавать недоверенные данные issue в переменную окружения и передавать их jq в качестве аргумента, а не разворачивать их внутри блока run. Коммит Snowflake, принятый через слияние, делает именно это, и исправленная обработка остаётся в ветке по умолчанию. Для тех, кто рассматривает эту запись как прецедент, устойчивый вывод касается атрибуции, а не инъекции: когда pull request включает нескольких человек и нескольких агентов, трейлер соавтора не позволяет определить, кто написал строку, и в итоге и Wiz, и GitHub сказали об этом разными словами.