
Laboratorio aislado de regresión y control de seguridad para CVE-2026-59891 en @sigstore/oci
# Laboratorio de Regresión y Control CVE-2026-59891
Proyecto de laboratorio aislado y auditoría de solo lectura que compara las versiones vulnerable y corregida reales de `@sigstore/oci` con la misma entrada y comprueba las condiciones de exposición según el entorno.
> El CVE Reporter de este proyecto es
> [gyubin02](https://github.com/gyubin02).
> Resumen para contratación y entrevistas:
> [Caso de automatización de gestión de vulnerabilidades CVE-2026-59891](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/docs/portfolio-case-study.ko.md)
## Qué se verifica
El núcleo de CVE-2026-59891 es la confusión de hostname que se produce al elegir las credenciales del Registry.
- `0.7.0`: puede seleccionar las credenciales de `ghcr.io` porque `cr.io` está contenido en la cadena `ghcr.io`.
- `0.7.1`: tras normalizar el hostname, solo selecciona las credenciales que coinciden exactamente.
Este repositorio verifica automáticamente dos escenarios de selección de credenciales.
1. **collision**: solo existen credenciales de `ghcr.io`, pero el destino es `cr.io`
2. **exact**: existen credenciales de `cr.io` que coinciden exactamente con el destino
La versión vulnerable debe seleccionar incorrectamente las credenciales en el primer escenario, y la versión corregida debe rechazarlas. Ambas versiones deben superar el segundo escenario normal.
Además, se ha añadido una comparación dinámica que utiliza un mock Registry desechable en `127.0.0.1` en lugar de un Registry externo real. La versión vulnerable 0.7.0 realiza 2 solicitudes locales y, tras el challenge de autenticación, se observa un header Authorization sintético en la segunda solicitud. La versión corregida 0.7.1 es rechazada en la fase de selección de credenciales, por lo que realiza 0 solicitudes. En los resultados solo quedan la observación (sí/no) y el número de solicitudes, no el valor del header.
## CLI de verificación de exposición del proyecto
`cve-2026-59891-audit` lee únicamente los **metadatos** del `package-lock.json` del proyecto objetivo y del Docker config especificado explícitamente, y distingue lo siguiente:
- La ubicación y la versión de las instalaciones afectadas de `@sigstore/oci`
- La existencia de la versión vulnerable y si se cumplen las condiciones reales de exposición
- El conflicto de subcadenas del Registry y el destino realmente seleccionado según el orden de las claves JSON
- La prioridad según el entorno, las medidas correctivas y el procedimiento de nueva prueba
- Evidencia JSON y Markdown con el hostname del Registry y las rutas absolutas eliminados
Ejecución inmediata con un fixture sintético:
```bash
npm run audit:demo
```
Verificación de otro proyecto Node.js:
```bash
npm run audit:project -- \
--project ../target-project \
--docker-config ../review-copy/config.json \
--image cr.io/example/demo \
--destination-trust unknown \
--format markdown \
--output reports/cve-2026-59891.md
```
`--destination-trust` debe especificar obligatoriamente uno de los siguientes valores:
- `trusted`: confirma que el destination está restringido mediante código y allowlist
- `untrusted`: entradas externas o entradas del workflow afectan al destination
- `unknown`: aún no se ha podido confirmar
El veredicto se divide en `exposure_conditions_met`, `potential_exposure`, `affected_component_only`, `not_detected` e `indeterminate`. `not_detected` es solo el resultado para el alcance de entradas registrado; no significa «seguro» ni «cumplimiento normativo».
## Salvaguardas
- El laboratorio de regresión no lee ni modifica el `~/.docker/config.json` real.
- Utiliza únicamente un `HOME` dedicado y aislado y valores ficticios sin valor: `lab-user` / `LAB_ONLY_FAKE_TOKEN`.
- Solo invoca el paquete cuando `HOME`, el marcador del laboratorio y los valores ficticios son todos correctos.
- Los probes de regresión de selección existentes bloquean mediante tripwire todas las llamadas HTTP(S), TCP, TLS, DNS y `fetch`.
- En la superficie de red de Node que usan los probes dinámicos, solo se permiten solicitudes HTTP al puerto exacto `127.0.0.1` asignado por el SO. Se bloquean el HTTP externo o hacia otros puertos, el resolver DNS, TLS, UDP, el `fetch` global y WebSocket.
- No hereda a los procesos hijos los tokens ni las variables de entorno de Docker de la sesión actual.
- Se verifica que stdout/stderr y los resultados de auditoría no conservan ni siquiera headers falsos, dejando solo booleanos y el número de solicitudes.
- La CLI de verificación no permite omitir `--docker-config` y no infiere archivos desde HOME ni `DOCKER_CONFIG`.
- La CLI no decodifica, aplica hash ni imprime valores de credenciales, y no ejecuta paquetes del proyecto objetivo, Docker ni credential helpers, ni se conecta a la red.
- Si se detectan symlinks, claves JSON duplicadas, lockfiles no compatibles o un `npm-shrinkwrap.json` de mayor prioridad, falla sin afirmar que sea seguro.
Dado que se instala intencionadamente el paquete vulnerable, la distribución del paquete npm está bloqueada con `"private": true`. No debe utilizarse como dependencia de código de producción.
Por ello, que `npm audit` reporte 0.7.0 es un resultado esperado; el alcance y el motivo de la excepción están documentados en [`SECURITY.md`](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/SECURITY.md).
## Ejecución
Requisitos: Node.js 22.22.2+, 24.15.0+ o 26 o superior. La versión recomendada está fijada en `.nvmrc`.
```bash
npm ci --ignore-scripts
npm test
npm run demo
npm run dynamic:demo
npm run audit:fixture
npm run audit:demo
npm run evidence
```
Resultados esperados clave de `npm run demo`:
```text
0.7.0 collision credential-selected
0.7.1 collision credential-rejected
0.7.0 exact credential-selected
0.7.1 exact credential-selected
Regression result: PASS
```
Resultados esperados clave de `npm run dynamic:demo`:
```json
{
"vulnerable": {
"packageVersion": "0.7.0",
"credentialSelection": "credential-selected",
"requestCount": 2,
"authorizationObserved": true,
"digestVerified": true,
"networkPolicy": "exact-loopback-only"
},
"fixed": {
"packageVersion": "0.7.1",
"credentialSelection": "credential-rejected",
"requestCount": 0,
"authorizationObserved": false,
"digestVerified": false,
"networkPolicy": "exact-loopback-only"
},
"regressionResult": "PASS"
}
```
Hay 34 pruebas automatizadas en total. `npm run evidence` genera 7 archivos en total: los 6 artefactos sujetos a inspección más `SHA256SUMS`, que verifica su integridad.
## Capacidades que demuestra este proyecto
- Explica a nivel de código las condiciones de aparición de un CVE real y lo reproduce de forma segura
- Diseña pruebas de regresión que comparan la versión vulnerable y la corregida en las mismas condiciones
- Diseña pruebas loopback-only que comparan dinámicamente si las credenciales sintéticas se transmiten o no localmente
- Evalúa las precondiciones reales de ataque en el entorno, al margen del CVSS público
- Identifica estáticamente package-lock v1, v2 y v3, alias y dependencias anidadas
- Genera automáticamente evidencia de auditoría JSON y Markdown sin información secreta
- Gestión de vulnerabilidades: detección → análisis de impacto → verificación de la corrección → control
- Pruebas de seguridad a prueba de fallos que no tocan credenciales reales
## Fuentes y alcance
- [GitHub Security Advisory GHSA-pf56-329r-95rw](https://github.com/sigstore/sigstore-js/security/advisories/GHSA-pf56-329r-95rw)
- [NVD CVE-2026-59891](https://nvd.nist.gov/vuln/detail/CVE-2026-59891)
Este proyecto es una evaluación controlada y limitada con fines educativos. Los resultados loopback solo demuestran diferencias de comportamiento en un entorno sintético; no demuestran la exposición o el compromiso de credenciales ni la eficacia de los controles en un entorno empresarial real. No implica la certificación CISA, la certificación ISMS-P ni el cumplimiento normativo de ninguna organización específica. El Node API guard es una capa de defensa para las rutas de ejecución observadas de las dependencias bloqueadas, no un namespace de red ni un firewall a nivel de SO.
La documentación detallada del alcance, las pruebas, los riesgos y los controles está en [`docs/`](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/docs), y `npm run evidence` genera un registro de ejecución reproducible y un manifest SHA-256.