FlawOpen

flawopen.com/Teardowns/cve-2026-84973-jupyterlab-snapshot-checkout-toctou

● CVE-2026-84973 · Alta
Investigación · FlawOpen

CVE-2026-84973: TOCTOU en el flujo de snapshots de JupyterLab

Una comprobación de tiempo con precisión de un segundo podía permitir que el flujo de snapshots descargara y ejecutara un commit no aprobado de una solicitud de cambios con los permisos del trabajo. Actualice update-snapshots-checkout a la versión 1.0.0 o posterior.

💡 Explicación en Lenguaje Sencillo (ELI5)

Un revisor aprueba un paquete concreto, pero el mostrador de entrega solo comprueba el segundo del reloj. Si alguien cambia el paquete durante ese mismo segundo, el sustituto parece aprobado. La action comparaba marcas de tiempo con precisión de un segundo y aceptaba valores iguales, por lo que una actualización de la solicitud podía pasar junto al comentario del mantenedor.

Conceptos Clave y Términos

TOCTOU
Time-of-check to time-of-use: se toma una decisión de seguridad sobre un recurso que cambia antes de utilizarse.
SHA head de la solicitud de cambios
Identificador inmutable del commit situado en la punta de la rama de la solicitud. El flujo solo debe ejecutar el commit exacto autorizado.
Permisos del token de GitHub Actions
Operaciones que el GITHUB_TOKEN del trabajo puede realizar, como leer o escribir contenido y solicitudes de cambios.
CWE-367 / CWE-829
El advisory clasifica la carrera temporal como CWE-367 y la ejecución de contenido controlado por la solicitud como CWE-829.

Mapeos de marcos

  • Referencia CWE para la debilidad descrita en esta guía: CWE-367 CWE-829

Análisis de Causa Raíz

Antes de la versión 1.0.0, update-snapshots-checkout obtenía pushed_at de la solicitud y created_at del comentario que activaba el flujo, convertía ambos valores a segundos Unix y rechazaba la ejecución solo cuando pushed_at era estrictamente mayor. Ambas marcas de tiempo de la API tienen precisión de un segundo. Por eso, una actualización y un comentario autorizado en el mismo segundo podían dar valores iguales y superar la comprobación. Después, la action guardaba el SHA actual y lo comprobaba tras el checkout; esa comparación solo demostraba que se descargó el SHA recién obtenido, no que el mantenedor lo hubiera autorizado. La corrección upstream cambia -gt por -ge y rechaza valores iguales o posteriores. El riesgo requiere una versión vulnerable y que pasos posteriores ejecuten contenido de la solicitud; el impacto depende de los permisos y credenciales del flujo.

Flujo de Ataque Paso a Paso

Paso 1

Un mantenedor de confianza activa el flujo

Se abre una solicitud de cambios y un OWNER, COLLABORATOR o MEMBER publica el comentario que activa el flujo de actualización de snapshots.

Paso 2

Una actualización controlada por el atacante coincide con el comentario

Un colaborador actualiza la rama en el mismo segundo. La API solo ofrece precisión de un segundo, por lo que los valores Unix pueden coincidir.

Paso 3

La comparación estricta acepta la igualdad

La action vulnerable rechaza únicamente cuando pushed_at es mayor que created_at. La igualdad no cumple esa condición y se registra el nuevo SHA de la rama.

Paso 4

El flujo descarga y puede ejecutar ese commit

El checkout coincide con el SHA recién registrado y supera la comprobación posterior. Si los pasos siguientes ejecutan actions o scripts de la solicitud, utilizan los permisos y credenciales del trabajo.

Paso 5

Actualice y reduzca los privilegios del flujo

Actualice a 1.0.0 o posterior. Fije las actions a SHAs inmutables, conceda al GITHUB_TOKEN solo permisos necesarios y mantenga el código de solicitudes no confiables lejos de secretos y trabajos privilegiados.

Código Fuente: Vulnerable vs. Seguro

Este es el cambio upstream de una línea: -ge rechaza la igualdad con la precisión temporal de la API. Cierra el bypass documentado del mismo segundo. Aun así, vincule la autorización a un SHA inmutable y reduzca permisos: el orden temporal no sustituye una autorización inmutable. El diff upstream conserva el mensaje de diagnóstico; dice «después», aunque ahora también se rechaza la igualdad.

IMPLEMENTACIÓN VULNERABLE
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
PARCHE SEGURO Y ROBUSTO
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 Verificación de Seguridad para Ingeniería

Fuentes