flawopen.com/Teardowns/cve-2026-84973-jupyterlab-snapshot-checkout-toctou
CVE-2026-84973: condição TOCTOU no fluxo de snapshots do JupyterLab
Uma verificação de horário com precisão de um segundo podia permitir que o fluxo de snapshots baixasse e executasse um commit não aprovado de pull request com as permissões do job. Atualize update-snapshots-checkout para a versão 1.0.0 ou posterior.
Uma pessoa aprova uma encomenda específica, mas o balcão de entrega confere apenas o segundo do relógio. Se alguém trocar a encomenda naquele mesmo segundo, a substituta parece aprovada. A action comparava horários com precisão de um segundo e aceitava valores iguais, permitindo que uma atualização do pull request passasse junto do comentário do mantenedor.
Conceitos Centrais e Termos
TOCTOU- Time-of-check to time-of-use: a decisão de segurança é tomada sobre um recurso que muda antes de ser utilizado.
SHA do head do pull request- Identificador imutável do commit no topo do branch do pull request. O fluxo deve executar apenas o commit exato autorizado.
Permissões do token do GitHub Actions- Operações permitidas ao GITHUB_TOKEN do job, como ler ou gravar conteúdo e pull requests.
CWE-367 / CWE-829- O advisory associa a condição de corrida temporal à
CWE-367e a execução de conteúdo controlado pelo pull request àCWE-829.
Mapeamentos de frameworks
Análise de Causa Raiz
Antes da versão 1.0.0, update-snapshots-checkout obtinha os horários pushed_at do pull request e created_at do comentário que acionou o fluxo, convertia ambos para segundos desde a época e rejeitava a execução apenas quando pushed_at era estritamente maior. Os dois horários da API têm precisão de um segundo. Assim, um push e um comentário autorizado no mesmo segundo podiam ser iguais e passar pela verificação. Em seguida, a action salvava o SHA atual do head e fazia checkout; a comparação posterior apenas provava que o checkout correspondia ao SHA recém-obtido, não que o mantenedor havia autorizado aquele commit. A correção upstream troca -gt por -ge e rejeita horários iguais ou posteriores. O risco exige uma versão vulnerável e etapas posteriores que executem conteúdo do pull request; o impacto depende das permissões e credenciais do workflow.
Fluxo de Ataque Passo a Paso
Um mantenedor autorizado aciona o fluxo
Um pull request é aberto e um OWNER, COLLABORATOR ou MEMBER publica o comentário que inicia o fluxo de atualização de snapshots.
Uma atualização controlada pelo atacante corre contra o comentário
Um colaborador atualiza o branch do pull request no mesmo segundo. Como os horários da API têm precisão de um segundo, os valores convertidos podem ser iguais.
A comparação estrita aceita a igualdade
A action vulnerável rejeita apenas quando pushed_at é maior que created_at. Valores iguais não atendem à condição, então o novo SHA do head é registrado.
O workflow baixa e pode executar esse commit
O checkout corresponde ao SHA registrado e passa na comparação posterior. Se etapas seguintes executarem actions ou scripts controlados pelo pull request, elas terão as permissões e credenciais configuradas no job.
Atualize e limite a autoridade do workflow
Atualize para 1.0.0 ou posterior. Fixe as actions em SHAs imutáveis, conceda somente as permissões necessárias ao GITHUB_TOKEN e mantenha código de pull requests não confiáveis longe de segredos e jobs privilegiados.
Código-Fonte: Vulnerável vs. Seguro
Este trecho corresponde à correção upstream de uma linha: -ge rejeita igualdade na precisão de tempo fornecida pela API. Ele fecha o bypass documentado no mesmo segundo. Ainda prefira autorizar um SHA imutável e reduza permissões, pois a ordem temporal não substitui uma decisão de autorização imutável. O diff upstream mantém a mensagem de diagnóstico existente; ela diz “depois”, embora a igualdade agora também seja rejeitada.
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
Lista de Verificação de Segurança para Engenharia
- ✓Atualize todas as utilizações de update-snapshots-checkout para 1.0.0 ou posterior e procure código copiado ou derivado.
- ✓Fixe actions de terceiros e da organização em SHAs imutáveis revisados; avalie as mudanças antes de atualizar os SHAs.
- ✓Não autorize um branch móvel de pull request apenas por horários; vincule a aprovação ao SHA exato do head e falhe se ele mudar.
- ✓Defina permissões mínimas explícitas para cada job e omita contents: write e pull-requests: write quando não forem necessários.
- ✓Não disponibilize segredos ou credenciais privilegiadas de implantação a jobs que executam código de pull requests externos.
- ✓Adicione teste de regressão que rejeite horários iguais e também um push posterior.
Fontes
- Advisory GitHub Security Lab GHSL-2026-203
- Advisory GHSA-wwhg-p79f-vfvm
- Commit da correção upstream 6a2505f
- MITRE CWE-367: condição de corrida time-of-check/time-of-use
- MITRE CWE-829: inclusão de funcionalidade de esfera de controle não confiável
- Caso de workflow relacionado (distinto): Marimo GHSL-2026-226