Generación de SBOMs

Objetivo

Este notebook explica el proceso para generar Software Bill of Materials (SBOMs) para un conjunto de repositorios de código y cómo analizar los resultados de manera centralizada. Utilizaremos la herramienta Syft para la generación y la librería Pandas en Python para el análisis.

El flujo de trabajo completo está diseñado para ser reproducible y se basa en las siguientes etapas:

  1. Descubrimiento de Repositorios: Identificar los proyectos a los que se les generará un SBOM.
  2. Generación de SBOMs: Ejecutar Syft para analizar cada repositorio y crear un SBOM en formato JSON.
  3. Análisis de Resultados: Cargar todos los SBOMs generados en un DataFrame de Pandas para su inspección y análisis.

Estructura de Directorios

El proyecto sigue una estructura organizada para separar los datos de entrada, los scripts y los resultados:

ciberseguridad_2026/
├── data/
│   ├── repos/      # Directorio para los repositorios a analizar
│   └── results/    # Directorio para los SBOMs generados en JSON
├── nbs/            # Notebooks de Jupyter
└── scripts/        # Scripts de automatización
    └── generate_sboms.py

Paso 1: Generación de SBOMs

El primer paso es ejecutar el script generate_sboms.py, que se encarga de orquestar la generación de los SBOMs.

Este script realiza las siguientes acciones:

  1. Busca repositorios: Escanea el directorio data/repos en busca de subdirectorios que contengan código fuente.
  2. Ejecuta Syft: Para cada repositorio encontrado, invoca a syft para analizar su contenido.
  3. Guarda los resultados: El SBOM generado para cada repositorio se guarda como un archivo JSON en el directorio data/results.
!python ../../scripts/generate_sboms.py
INFO | Usando Syft CLI: /usr/local/bin/syft
INFO | [1/3] Procesando repositorio data/repos/claude-code-action
INFO | SBOM guardado en data/results/claude-code-action-sbom.json
INFO | [2/3] Procesando repositorio data/repos/genai-code-review
INFO | SBOM guardado en data/results/genai-code-review-sbom.json
INFO | [3/3] Procesando repositorio data/repos/opencode
INFO | SBOM guardado en data/results/opencode-sbom.json
INFO | Resumen final | total_repos=3 | repos_generados=3 | archivos_generados=3 | omitidos=0 | errores=0

Paso 2: Análisis de los SBOMs con Pandas

Una vez que los SBOMs han sido generados, podemos cargarlos en un DataFrame de Pandas para analizarlos. Esto nos permite tener una visión consolidada de todas las dependencias y artefactos encontrados en los diferentes repositorios.

El siguiente código se encarga de:

  1. Localizar los archivos JSON: Busca todos los archivos .json en el directorio data/results.
  2. Cargar y normalizar los datos: Para cada archivo JSON, lo carga y utiliza pd.json_normalize para aplanar la estructura anidada de los artefactos.
  3. Añadir información del repositorio: Agrega una columna repo para identificar a qué repositorio pertenece cada artefacto.
  4. Concatenar los resultados: Une todos los DataFrames individuales en uno solo.
import pandas as pd
from pathlib import Path
import json

path = Path("../../data/results")

df_all = pd.concat(
    [
        pd.json_normalize(json.load(open(file))["artifacts"]).assign(repo=file.stem)
        for file in path.glob("*-sbom.json")
    ],
    ignore_index=True
)

df_all.head()
id name version type foundBy locations licenses language cpes purl ... metadata.dependencies.@actions/core metadata.dependencies.shell-quote metadata.comment metadata.dependencies.@fastify/busboy repo metadata.name metadata.versionConstraint metadata.version metadata.source metadata.checksum
0 3dde6d26a8867975 ./.github/workflows/ci.yml UNKNOWN github-action-workflow github-action-workflow-usage-cataloger [{'path': '/.github/workflows/ci-all.yml', 'ac... [] [{'cpe': 'cpe:2.3:a:.\/.github\/workflows\/ci.... ... NaN NaN NaN NaN claude-code-action-sbom NaN NaN NaN NaN NaN
1 9166fd4b121e55f3 ./.github/workflows/test-base-action.yml UNKNOWN github-action-workflow github-action-workflow-usage-cataloger [{'path': '/.github/workflows/ci-all.yml', 'ac... [] [{'cpe': 'cpe:2.3:a:.\/.github\/workflows\/tes... ... NaN NaN NaN NaN claude-code-action-sbom NaN NaN NaN NaN NaN
2 95afc7fec8addb9b ./.github/workflows/test-custom-executables.yml UNKNOWN github-action-workflow github-action-workflow-usage-cataloger [{'path': '/.github/workflows/ci-all.yml', 'ac... [] [{'cpe': 'cpe:2.3:a:.\/.github\/workflows\/tes... ... NaN NaN NaN NaN claude-code-action-sbom NaN NaN NaN NaN NaN
3 f535590207532289 ./.github/workflows/test-mcp-servers.yml UNKNOWN github-action-workflow github-action-workflow-usage-cataloger [{'path': '/.github/workflows/ci-all.yml', 'ac... [] [{'cpe': 'cpe:2.3:a:.\/.github\/workflows\/tes... ... NaN NaN NaN NaN claude-code-action-sbom NaN NaN NaN NaN NaN
4 bdedac1ea6288e33 ./.github/workflows/test-settings.yml UNKNOWN github-action-workflow github-action-workflow-usage-cataloger [{'path': '/.github/workflows/ci-all.yml', 'ac... [] [{'cpe': 'cpe:2.3:a:.\/.github\/workflows\/tes... ... NaN NaN NaN NaN claude-code-action-sbom NaN NaN NaN NaN NaN

5 rows × 30 columns

Exploración de los Resultados

Con los datos en un DataFrame, podemos realizar diversas consultas y análisis. Por ejemplo, podemos ver cuántos artefactos se encontraron por cada repositorio.

df_all['repo'].value_counts()
repo
opencode-sbom              775
claude-code-action-sbom     37
genai-code-review-sbom       5
Name: count, dtype: int64

También podemos ver las licencias más comunes encontradas en todas las dependencias.

df_all['licenses'].explode().value_counts().head(10)
licenses
{'value': 'MIT', 'spdxExpression': 'MIT', 'type': 'declared', 'urls': [], 'locations': [{'path': '/base-action/package-lock.json', 'accessPath': '/base-action/package-lock.json', 'annotations': {'evidence': 'primary'}}]}    8
Name: count, dtype: int64

Cómo Añadir Nuevos Repositorios al Análisis

El proyecto está diseñado para que sea sencillo añadir nuevos repositorios al proceso de generación de SBOMs. El sistema utiliza Git submodules para gestionar los repositorios externos.

1. Edita el archivo data/repos.json

Abre data/repos.json y agrega nuevos repositorios con esta estructura:

{
  "url": "URL_DEL_REPOSITORIO_EN_GITHUB.git",
  "path": "data/repos/NOMBRE_DEL_REPOSITORIO"
}
  • url: La URL .git del repositorio
  • path: Ruta local donde se clonará (usa data/repos/{nombre})

2. Ejecuta en la terminal (esto clona los repositorios y los agrega como submódulos):

uv run scripts/add_submodules.py && git submodule update --init --recursive

3. Ejecuta la generación de SBOMs nuevamente:

uv run scripts/generate_sboms.py

O desde el notebook: - Reinicia el kernel - Ejecuta la celda con !python ../../scripts/generate_sboms.py