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:
@@ -349,6 +349,40 @@ for r in results:
|
|||||||
--ignore-unfixed \
|
--ignore-unfixed \
|
||||||
"${IMAGE_NAME}:${{ steps.vars.outputs.VERSION }}"
|
"${IMAGE_NAME}:${{ steps.vars.outputs.VERSION }}"
|
||||||
|
|
||||||
|
# SBOM de la imagen que ya pasó el gate de CRITICAL — describe
|
||||||
|
# exactamente qué paquetes (y en qué versión) quedaron adentro.
|
||||||
|
# CycloneDX: formato más usado por herramientas de consulta/alertas
|
||||||
|
# de CVEs (ej. cruzar el SBOM contra un aviso nuevo tipo log4shell).
|
||||||
|
- name: Instalar Syft
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
SYFT_VERSION="1.51.0"
|
||||||
|
curl -sSfL \
|
||||||
|
"https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" \
|
||||||
|
-o /tmp/syft.tar.gz
|
||||||
|
tar -xzf /tmp/syft.tar.gz -C /tmp syft
|
||||||
|
chmod +x /tmp/syft
|
||||||
|
/tmp/syft version
|
||||||
|
|
||||||
|
- name: Generar SBOM (Syft)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
/tmp/syft "${IMAGE_NAME}:${{ steps.vars.outputs.VERSION }}" \
|
||||||
|
-o cyclonedx-json=sbom.cdx.json \
|
||||||
|
-o spdx-json=sbom.spdx.json
|
||||||
|
|
||||||
|
- name: Publicar SBOM
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: sbom-${{ steps.vars.outputs.VERSION }}
|
||||||
|
path: |
|
||||||
|
sbom.cdx.json
|
||||||
|
sbom.spdx.json
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
# Login ya se hizo arriba; recién acá se sube, después de que la
|
# Login ya se hizo arriba; recién acá se sube, después de que la
|
||||||
# imagen pasó el gate de CRITICAL.
|
# imagen pasó el gate de CRITICAL.
|
||||||
- name: Subir Imagen al Registry
|
- name: Subir Imagen al Registry
|
||||||
|
|||||||
@@ -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
|
- Gitleaks (secretos): devsecops/gitleaks.md
|
||||||
- Trivy (imagen + IaC): devsecops/trivy.md
|
- Trivy (imagen + IaC): devsecops/trivy.md
|
||||||
- SAST (Semgrep): devsecops/sast.md
|
- SAST (Semgrep): devsecops/sast.md
|
||||||
|
- SBOM (Syft): devsecops/sbom.md
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
social:
|
social:
|
||||||
|
|||||||
Reference in New Issue
Block a user