feat(devsecops): generar SBOM (Syft) de la imagen del frontend
Corre después del gate de CRITICAL de Trivy, sobre la imagen ya construida. Genera CycloneDX y SPDX, publicados como artifact sbom-v1.0.X en cada build. Validado contra la imagen real: 3528 componentes CycloneDX / 228 paquetes SPDX. Documenta en docs/devsecops/sbom.md el propósito (trazabilidad tipo "log4shell"), diferencia entre formatos, y un hallazgo real (yarn empaquetado sin usarse, mismo patrón que el npm sacado en el fix de Trivy).
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# SBOM (Syft) — inventario de software
|
||||
|
||||
!!! info "Qué es un SBOM"
|
||||
SBOM = *Software Bill of Materials* — literalmente, una "lista de
|
||||
materiales" del software, igual que la lista de ingredientes de un
|
||||
producto. Es un documento (JSON, en este caso) que enumera **cada
|
||||
paquete que terminó dentro de la imagen final**: nombre, versión
|
||||
exacta, de dónde viene (`npm`, `deb`, etc.) y, cuando aplica, su
|
||||
licencia.
|
||||
|
||||
[Syft](https://github.com/anchore/syft) genera ese inventario
|
||||
inspeccionando la imagen Docker ya construida — no necesita acceso al
|
||||
código fuente ni a `package.json`, lee directamente lo que quedó
|
||||
instalado en los layers de la imagen.
|
||||
|
||||
## Por qué importa: el escenario "log4shell"
|
||||
|
||||
En diciembre de 2021 apareció una vulnerabilidad crítica en Log4j (una
|
||||
librería de logging de Java) usada, directa o indirectamente, en una
|
||||
cantidad enorme de software. La pregunta que todo equipo tuvo que
|
||||
responder en horas, no en días, fue: **"¿nosotros usamos esto, en algún
|
||||
lugar, aunque sea una dependencia de una dependencia?"**
|
||||
|
||||
Sin un SBOM, esa respuesta implica revisar manualmente cada
|
||||
`package.json`, cada imagen Docker, cada servicio — y confiar en que no
|
||||
se te escapó una dependencia transitiva de tres niveles de profundidad.
|
||||
Con un SBOM generado en cada build y guardado como artifact, la respuesta
|
||||
es una búsqueda de texto sobre un archivo:
|
||||
|
||||
```bash
|
||||
grep -i "nombre-del-paquete-afectado" sbom.cdx.json
|
||||
```
|
||||
|
||||
Si aparece, sabés exactamente en qué versión, y podés cruzarlo contra el
|
||||
aviso de seguridad para saber si tu versión específica está afectada —
|
||||
en minutos, no en una auditoría manual del repo completo.
|
||||
|
||||
## Qué genera este pipeline
|
||||
|
||||
Un step de Syft corre sobre la imagen ya construida (la misma que pasó
|
||||
el gate de CRITICAL de Trivy) y produce **dos formatos** del mismo
|
||||
inventario, publicados como artifact del pipeline:
|
||||
|
||||
- `sbom.cdx.json` — [CycloneDX](https://cyclonedx.org/), el formato con
|
||||
mejor soporte en herramientas de consulta/alertas automáticas de CVEs.
|
||||
- `sbom.spdx.json` — [SPDX](https://spdx.dev/), el estándar ISO, más
|
||||
orientado a cumplimiento de licencias y trazabilidad legal.
|
||||
|
||||
No hay una razón fuerte para elegir solo uno en esta etapa — generar
|
||||
ambos cuesta segundos y cada formato es mejor para un caso de uso
|
||||
distinto, así que se publican los dos.
|
||||
|
||||
## Resultado real de este build
|
||||
|
||||
Corriendo Syft contra la imagen real de `workloads/ecommerce`:
|
||||
|
||||
| Formato | Paquetes listados |
|
||||
|---|---|
|
||||
| CycloneDX | 3528 componentes |
|
||||
| SPDX | 228 paquetes |
|
||||
|
||||
!!! tip "¿Por qué el número es tan distinto entre formatos?"
|
||||
No es un error — cada formato tiene un nivel de detalle distinto.
|
||||
CycloneDX de Syft incluye entradas más granulares (variantes,
|
||||
sub-paquetes, entradas sin versión resuelta marcadas `UNKNOWN`)
|
||||
mientras que SPDX agrupa a un nivel más alto. Para "¿tengo este
|
||||
paquete, sí o no?" cualquiera de los dos sirve; para conteos exactos,
|
||||
hay que saber cuál se está mirando.
|
||||
|
||||
Ejemplo de una entrada real (`sbom.cdx.json`, recortado):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "next",
|
||||
"version": "15.5.10",
|
||||
"licenses": [{ "license": { "id": "MIT" } }],
|
||||
"purl": "pkg:npm/[email protected]",
|
||||
"properties": [
|
||||
{ "name": "syft:package:language", "value": "javascript" },
|
||||
{ "name": "syft:package:type", "value": "npm" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
El campo `purl` (*Package URL*) es el identificador estándar que usan
|
||||
Trivy, Syft, GitHub Advisories y la mayoría de las bases de datos de
|
||||
CVEs para referirse a "este paquete, en este ecosistema, en esta
|
||||
versión" — es lo que hace posible cruzar un SBOM contra un aviso de
|
||||
seguridad de forma automática, sin depender de que el nombre coincida
|
||||
exactamente en texto libre.
|
||||
|
||||
!!! example "El SBOM también sirve para encontrar cosas que sobran"
|
||||
Revisando el inventario real apareció `[email protected]` — viene
|
||||
empaquetado en la imagen base de Node igual que el `npm` que ya se
|
||||
sacó del stage final en el fix de [Trivy](trivy.md). No es una
|
||||
vulnerabilidad activa hoy, pero es exactamente el tipo de hallazgo
|
||||
que un SBOM hace visible para revisar después: herramientas que
|
||||
viajan en la imagen de producción sin que el runtime las necesite.
|
||||
|
||||
## Cómo se consulta
|
||||
|
||||
El SBOM se publica como artifact del pipeline (`sbom-v1.0.X`, con ambos
|
||||
archivos) en cada build. Para consultarlo:
|
||||
|
||||
1. Descargar el artifact de la corrida del pipeline que te interesa
|
||||
(o del último build de `main`, para saber qué corre en producción
|
||||
ahora mismo).
|
||||
2. Buscar el paquete en cuestión:
|
||||
```bash
|
||||
python3 -c "
|
||||
import json
|
||||
d = json.load(open('sbom.cdx.json'))
|
||||
for c in d['components']:
|
||||
if c['name'] == 'next':
|
||||
print(c['name'], c['version'])
|
||||
"
|
||||
```
|
||||
(o `grep -A3 '"name": "next"' sbom.cdx.json` si no hay Python a mano).
|
||||
3. Si el paquete aparece, confirmar la versión contra el aviso de
|
||||
seguridad para saber si aplica.
|
||||
|
||||
No hace falta memorizar el formato — el punto de tener el SBOM ya
|
||||
generado es no depender de reconstruir esta información bajo presión
|
||||
el día que aparezca la próxima CVE grande.
|
||||
@@ -102,6 +102,7 @@ nav:
|
||||
- Gitleaks (secretos): devsecops/gitleaks.md
|
||||
- Trivy (imagen + IaC): devsecops/trivy.md
|
||||
- SAST (Semgrep): devsecops/sast.md
|
||||
- SBOM (Syft): devsecops/sbom.md
|
||||
|
||||
extra:
|
||||
social:
|
||||
|
||||
Reference in New Issue
Block a user