flawopen.com/Teardowns/cve-2026-84973-jupyterlab-snapshot-checkout-toctou
CVE-2026-84973: TOCTOU-гонка в snapshot workflow JupyterLab
Проверка времени с точностью до секунды могла позволить snapshot workflow получить и выполнить неавторизованный commit pull request с правами задания. Обновите update-snapshots-checkout до версии 1.0.0 или новее.
Рецензент одобряет конкретную посылку, но на пункте выдачи сверяют только секунду на часах. Если посылку подменить в ту же секунду, замена выглядит одобренной. Action сравнивала время с точностью до секунды и принимала равные значения, поэтому обновление pull request могло пройти одновременно с комментарием сопровождающего.
Ключевые понятия и термины
TOCTOU- Time-of-check to time-of-use: решение о безопасности принимается для ресурса, который изменяется до его использования.
Head SHA pull request- Неизменяемый идентификатор commit на вершине ветки pull request. Workflow должен выполнять только точно одобренный commit.
Права токена GitHub Actions- Операции, разрешённые GITHUB_TOKEN задания, например чтение или изменение содержимого репозитория и pull request.
CWE-367 / CWE-829- В advisory гонка времени отнесена к
CWE-367, а выполнение функциональности, контролируемой pull request, — кCWE-829.
Связи со стандартами
Анализ первопричины
До версии 1.0.0 update-snapshots-checkout получала pushed_at pull request и created_at запускающего комментария, переводила их в секунды Unix и отклоняла запуск только если pushed_at строго больше. Оба значения API имеют точность до секунды. Поэтому отправка изменений и разрешённый комментарий в одну секунду могли дать равные значения и пройти проверку. Затем action сохраняла текущий head SHA и проверяла его после checkout; это подтверждало лишь соответствие checkout только что полученному SHA, но не то, что сопровождающий разрешил именно этот commit. В исправлении upstream -gt заменён на -ge, чтобы отклонять равенство и более поздние отправки. Риск возникает при использовании уязвимой версии action и последующем выполнении содержимого pull request; последствия ограничены правами и учётными данными workflow.
Пошаговый сценарий атаки
Доверенный сопровождающий запускает workflow
Создаётся pull request, а OWNER, COLLABORATOR или MEMBER публикует комментарий, запускающий workflow обновления snapshots.
Изменение атакующего совпадает по времени с комментарием
Участник обновляет ветку в ту же секунду. Из-за секундной точности API значения времени Unix могут совпасть.
Строгое сравнение принимает равные значения
Уязвимая action отклоняет запуск только когда pushed_at больше created_at. Равенство проходит условие, и сохраняется новый head SHA.
Workflow получает этот commit и может его выполнить
Checkout совпадает с сохранённым SHA и проходит последующую проверку. Если следующие шаги запускают actions или скрипты из pull request, они получают права и учётные данные задания.
Обновите action и ограничьте права
Обновите до 1.0.0 или новее. Закрепляйте actions за неизменяемыми SHA, выдавайте GITHUB_TOKEN только необходимые права и не передавайте секреты заданиям с недоверенным кодом pull request.
Исходный код: Уязвимый vs Защищённый вариант
Это точное однострочное исправление upstream: -ge отклоняет равенство при доступной точности API. Оно закрывает описанный обход в ту же секунду. Тем не менее разрешение следует связывать с неизменяемым SHA и минимизировать права: порядок времени не заменяет неизменяемую проверку авторизации. В upstream diff диагностическое сообщение не меняется: в нём сказано «после», хотя теперь отклоняется и равенство.
if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
echo "Updating is not allowed because the PR was pushed to (at $pushed_at) after the triggering comment was issued (at $COMMENT_AT)"
exit 1
fi
if [[ $(date -d "$pushed_at" +%s) -ge $(date -d "$COMMENT_AT" +%s) ]]; then
echo "Updating is not allowed because the PR was pushed to (at $pushed_at) after the triggering comment was issued (at $COMMENT_AT)"
exit 1
fi
Чек-лист по защите системы для инженеров
- ✓Обновите все использования update-snapshots-checkout до версии 1.0.0 или новее; найдите также скопированный и форкнутый код.
- ✓Закрепляйте actions за проверенными неизменяемыми SHA и анализируйте изменения перед обновлением ссылок.
- ✓Не разрешайте изменяемую ветку pull request только по временным меткам; привяжите одобрение к точному head SHA.
- ✓Задайте минимальные явные права каждого задания и не выдавайте contents: write и pull-requests: write без необходимости.
- ✓Не передавайте секреты и привилегированные данные развёртывания заданиям, выполняющим код внешних pull request.
- ✓Добавьте регрессионные тесты, отклоняющие одинаковые и более поздние метки времени.