Правка, которая должна была улучшить воркфлоу, открыла доступ к секрету
Сюжет умещается в несколько строк. В открытый репозиторий snowflakedb/snowflake-connector-net 18 июня 2026 года приняли пулл-реквест #1218 — с виду безобидный, «правим Jira-воркфлоу, код не трогаем». Среди соавторов коммита числится Copilot Autofix powered by AI. Спустя пять суток команда Wiz вытащила оттуда действующий API-ключ Jira: достаточно было завести в трекере issue с подходящим заголовком.
Ниже — реконструкция по коммитам. Всё лежит публично, так что каждый шаг проверяется вручную.
Как разворачивались события
Схема на основе публикации Wiz Research и коммитов Snowflake
Пять суток — ровно столько воркфлоу, у которого в окружении лежал секрет JIRA_API_TOKEN, отзывался на issue, открытый кем угодно из интернета.
Каким был payload
Судя по описанию Wiz, с наскока не получилось: попытка закомментировать хвост через # не давала нужного закрытия конструкции. Сработал другой приём — закрыть кавычку, вставить свою команду и снова открыть кавычку, чтобы остаток строки не поломал разбор:
Ключ уехал на сторонний домен в кодировке base64 — приём out-of-band-подтверждения. Далее он проходил аутентификацию в snowflakecomputing.atlassian.net под учёткой qa@snowflake.net и открывал чтение инженерных проектов, комплаенса и bug bounty.
Заплатку выкатили в тот же день, 23 июня. Коммит 1dc7766 в составе PR #1402 вернул переменные ISSUE_TITLE, ISSUE_BODY и ISSUE_URL в секцию env:, а формирование JSON — на jq -n --arg. Днём позже ключ отозвали. Идентификатор CVE не выдавался: перед нами не дефект продукта, а ошибка в настройке CI отдельно взятого репозитория.
Что стояло в файле до и после
Раньше файл .github/workflows/jira_issue.yml вёл себя корректно: значение заголовка ехало в переменную окружения, а jq собирал payload через --arg, беря экранирование на себя.
run: TITLE=$(echo '${{ github.event.issue.title }}' | sed ...)
Внешне мелочь, по сути — перелом. Запись ${{ … }} в GitHub Actions относится не к шеллу, а к шаблонизатору, который вклеивает значение в текст скрипта ещё до запуска. Когда очередь доходит до sed, заголовок уже стал частью команды, и экранировать нечего: одиночная кавычка в заголовке завершает echo, а всё написанное автором issue дальше воспринимается как продолжение команды.
⛔ Так делать нельзя
Прямая вставка ${{ github.event.* }} в тело run: недопустима. Пользовательские данные — заголовки и тексты issue, имена веток, комментарии, названия PR — передаются исключительно через env: и читаются потом как обычная шелл-переменная, взятая в кавычки.
Страховка, которой не было
Формально в воркфлоу присутствовала ограничивающая проверка — условие по github.event.pull_request.user.login. Беда в том, что запуск происходил по событию issues: opened, а у него объекта pull_request попросту не существует. Чтение поля у отсутствующего объекта возвращает пустоту, условие превращается в истину, шаг отрабатывает всегда.
Типовая ошибка: защита сочинялась под один триггер, а переехала на другой без разбора.
⚠️ Осторожно
Про природу источника. Red Agent — фирменная разработка самой Wiz, и материал в жанре «наш агент самостоятельно обнаружил и проэксплуатировал» одновременно продаёт продукт. Что здесь поддаётся проверке — даты, хеши коммитов и содержимое диффов; они открыты, и я их сверил по репозиторию. Тезисы об автономности агента остаются на совести вендора.
Уроки для репозитория с агентом в контуре
Занятна тут не инъекция как таковая — классу багов полтора десятка лет, — а вектор правки. Инструмент, созданный для укрепления безопасности, поменял верную конструкцию на ошибочную, а ревьюер, глядя на описание «CI yml adjustment, no code changes», пропустил это мимо.
Изменения в воркфлоу — это код, у которого есть секреты. Одна строчка в .github/workflows/ способна обойтись дороже сотни строк прикладной логики. Привычку «PR по CI смотрим по диагонали» стоит выбросить.
Смотреть надо на вектор, а не только на итог. Разумный вопрос к любому автофиксу: что стояло раньше и по какой причине. Переход с jq --arg на sed — движение к худшему, и заметно оно лишь при взгляде на исходное состояние.
Класс проблемы закрывается автоматикой. Подстановки через ${{ }} отлично ловятся статическим анализом. Как минимум запустите в CI actionlint и zizmor, чтобы о таком сообщал робот, а не сторонний исследователь.
✅ Как правильно
Пробежка по собственным воркфлоу за десять минут:
выполнить grep -rn '\${{ github.event' .github/workflows/ и просмотреть каждое совпадение внутри run:;
считать недоверенным вводом всё, что приходит с триггеров issues, issue_comment, pull_request_target, discussion;
не пускать секреты в шаги, где обрабатывается пользовательский текст;
сверить каждое if: с фактическим триггером — поля чужого события молча отдают пустоту;
урезать права интеграционных токенов и ротировать их по календарю, а не по факту утечки.
❗ Главное
Сгенерированный моделью автофикс стоит воспринимать как заявку, а не как патч из рук эксперта. Вес ей придаёт ревью — и только то ревью, где сравнивают «было» со «стало», а не оценивают, выглядит ли результат правдоподобно.
У кого включён Copilot Autofix или его аналог: разбираете ли вы такие правки с той же придирчивостью, что и правки живого коллеги, или на практике мержите быстрее? И запускает ли кто-нибудь actionlint либо zizmor по своим воркфлоу — попадалось ли им что-то настоящее?
Вы можете написать сейчас и зарегистрироваться позже.
Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.
Сюжет умещается в несколько строк. В открытый репозиторий
snowflakedb/snowflake-connector-net18 июня 2026 года приняли пулл-реквест #1218 — с виду безобидный, «правим Jira-воркфлоу, код не трогаем». Среди соавторов коммита числится Copilot Autofix powered by AI. Спустя пять суток команда Wiz вытащила оттуда действующий API-ключ Jira: достаточно было завести в трекере issue с подходящим заголовком.Ниже — реконструкция по коммитам. Всё лежит публично, так что каждый шаг проверяется вручную.
Как разворачивались события
Пять суток — ровно столько воркфлоу, у которого в окружении лежал секрет
JIRA_API_TOKEN, отзывался на issue, открытый кем угодно из интернета.Каким был payload
Судя по описанию Wiz, с наскока не получилось: попытка закомментировать хвост через
#не давала нужного закрытия конструкции. Сработал другой приём — закрыть кавычку, вставить свою команду и снова открыть кавычку, чтобы остаток строки не поломал разбор:Ключ уехал на сторонний домен в кодировке base64 — приём out-of-band-подтверждения. Далее он проходил аутентификацию в
snowflakecomputing.atlassian.netпод учёткойqa@snowflake.netи открывал чтение инженерных проектов, комплаенса и bug bounty.Заплатку выкатили в тот же день, 23 июня. Коммит
1dc7766в составе PR #1402 вернул переменныеISSUE_TITLE,ISSUE_BODYиISSUE_URLв секциюenv:, а формирование JSON — наjq -n --arg. Днём позже ключ отозвали. Идентификатор CVE не выдавался: перед нами не дефект продукта, а ошибка в настройке CI отдельно взятого репозитория.Что стояло в файле до и после
Раньше файл
.github/workflows/jira_issue.ymlвёл себя корректно: значение заголовка ехало в переменную окружения, аjqсобирал payload через--arg, беря экранирование на себя.Правка превратила это вот в такое:
Внешне мелочь, по сути — перелом. Запись
${{ … }}в GitHub Actions относится не к шеллу, а к шаблонизатору, который вклеивает значение в текст скрипта ещё до запуска. Когда очередь доходит доsed, заголовок уже стал частью команды, и экранировать нечего: одиночная кавычка в заголовке завершаетecho, а всё написанное автором issue дальше воспринимается как продолжение команды.⛔ Так делать нельзя
Прямая вставка
${{ github.event.* }}в телоrun:недопустима. Пользовательские данные — заголовки и тексты issue, имена веток, комментарии, названия PR — передаются исключительно черезenv:и читаются потом как обычная шелл-переменная, взятая в кавычки.Страховка, которой не было
Формально в воркфлоу присутствовала ограничивающая проверка — условие по
github.event.pull_request.user.login. Беда в том, что запуск происходил по событиюissues: opened, а у него объектаpull_requestпопросту не существует. Чтение поля у отсутствующего объекта возвращает пустоту, условие превращается в истину, шаг отрабатывает всегда.Типовая ошибка: защита сочинялась под один триггер, а переехала на другой без разбора.
⚠️ Осторожно
Про природу источника. Red Agent — фирменная разработка самой Wiz, и материал в жанре «наш агент самостоятельно обнаружил и проэксплуатировал» одновременно продаёт продукт. Что здесь поддаётся проверке — даты, хеши коммитов и содержимое диффов; они открыты, и я их сверил по репозиторию. Тезисы об автономности агента остаются на совести вендора.
Уроки для репозитория с агентом в контуре
Занятна тут не инъекция как таковая — классу багов полтора десятка лет, — а вектор правки. Инструмент, созданный для укрепления безопасности, поменял верную конструкцию на ошибочную, а ревьюер, глядя на описание «CI yml adjustment, no code changes», пропустил это мимо.
Изменения в воркфлоу — это код, у которого есть секреты. Одна строчка в
.github/workflows/способна обойтись дороже сотни строк прикладной логики. Привычку «PR по CI смотрим по диагонали» стоит выбросить.Смотреть надо на вектор, а не только на итог. Разумный вопрос к любому автофиксу: что стояло раньше и по какой причине. Переход с
jq --argнаsed— движение к худшему, и заметно оно лишь при взгляде на исходное состояние.Класс проблемы закрывается автоматикой. Подстановки через
${{ }}отлично ловятся статическим анализом. Как минимум запустите в CIactionlintиzizmor, чтобы о таком сообщал робот, а не сторонний исследователь.✅ Как правильно
Пробежка по собственным воркфлоу за десять минут:
grep -rn '\${{ github.event' .github/workflows/и просмотреть каждое совпадение внутриrun:;issues,issue_comment,pull_request_target,discussion;if:с фактическим триггером — поля чужого события молча отдают пустоту;❗ Главное
Сгенерированный моделью автофикс стоит воспринимать как заявку, а не как патч из рук эксперта. Вес ей придаёт ревью — и только то ревью, где сравнивают «было» со «стало», а не оценивают, выглядит ли результат правдоподобно.
Источники
💬 Вопрос к сообществу
У кого включён Copilot Autofix или его аналог: разбираете ли вы такие правки с той же придирчивостью, что и правки живого коллеги, или на практике мержите быстрее? И запускает ли кто-нибудь
actionlintлибоzizmorпо своим воркфлоу — попадалось ли им что-то настоящее?TOP HOSTERS: KAMATERA (30 дней бесплатного теста!)
Универсальный хостер №1 - 4VPS.su (2Гб\с сервера) - 10% скидка на первый заказ или 15% бонус на первое пополнение