Files
apps-registry/workloads/docs-portal/docs/devsecops/gitleaks.md
T
devops 422e9d257d feat(devsecops): agregar Gitleaks al pipeline del frontend
Corre en push y pull_request, antes del job build (needs: gitleaks).
Si detecta un secreto, exit-code=1 detiene el pipeline antes de
construir la imagen. Scan histórico completo del repo (249 commits)
confirmado limpio, corrido aparte de forma manual.

Documenta el step en docs/devsecops/gitleaks.md: qué es secret
scanning, por qué corre antes del build, cómo leer un hallazgo y
cómo manejar falsos positivos con allowlist.
2026-08-14 19:48:44 -05:00

5.7 KiB

Gitleaks — detección de secretos

!!! info "Qué problema resuelve" Gitleaks busca patrones de credenciales (API keys, tokens, contraseñas, llaves privadas) dentro del código fuente. No sabe si una credencial es "real" — detecta formas que parecen credenciales (una API key de AWS siempre empieza con AKIA, una llave privada siempre tiene el encabezado -----BEGIN PRIVATE KEY-----, etc.) y también cadenas con entropía alta (aleatoriedad), que suelen ser tokens generados.

Por qué esto importa

Un secreto commiteado a git nunca deja de estar ahí, aunque lo borres en el siguiente commit. Sigue existiendo en el historial, en cualquier fork, en cualquier clon local que alguien ya haya hecho. La única manera real de "revocar" un secreto filtrado es rotarlo (generar uno nuevo e invalidar el viejo) — borrar el commit no alcanza.

Por eso el objetivo de gitleaks no es "arreglar" el secreto después de que se filtró, sino evitar que el commit con el secreto llegue a existir en el repo remoto.

Por qué corre antes del build

En .gitea/workflows/build.yaml, el job gitleaks corre antes que el job build (que compila la imagen Docker y la sube al registry). El job build tiene needs: gitleaks — si el scan falla, build ni siquiera arranca.

flowchart LR
    A[push / pull_request] --> B[gitleaks]
    B -- "sin hallazgos" --> C[build]
    B -- "secreto detectado" --> X["❌ pipeline detenido<br/>build no corre"]

La lógica es simple: no tiene sentido gastar tiempo de build y minutos de runner compilando una imagen a partir de un commit que de todas formas hay que rechazar. Fallar rápido, fallar barato.

También corre en pull request, no solo en push a main — así un secreto se detecta antes de que el PR se mergee, que es el punto donde todavía es más fácil corregirlo (basta con un git commit --amend o un nuevo commit en la misma rama, sin tocar main).

Alcance de este scan

El step de CI escanea el árbol de archivos ya checked out del commit (gitleaks detect --no-git), no el historial completo — el checkout del pipeline es superficial (fetch-depth: 1, solo el último commit), así que no hay historial que recorrer en ese punto.

Antes de integrar esto al pipeline corrimos un scan histórico completo del repo apps-registry (los 249 commits, con gitleaks detect en modo git normal, sin --no-git) para confirmar que no había secretos ya commiteados en el pasado. Resultado: sin hallazgos. Ese scan histórico es una tarea puntual, no algo que corra en cada push — si alguna vez se sospecha una filtración vieja, se repite manualmente.

Cómo leer un hallazgo

Un hallazgo de gitleaks (en el gitleaks-report.json que el pipeline publica como artifact) se ve así:

{
  "Description": "AWS Access Key",
  "StartLine": 14,
  "File": "workloads/ecommerce/lib/config.ts",
  "Match": "REDACTED",
  "Secret": "REDACTED",
  "RuleID": "aws-access-token",
  "Commit": "a1b2c3d"
}

Campos clave:

Campo Qué significa
RuleID Qué tipo de secreto detectó (la regla que hizo match)
File / StartLine Dónde está, exactamente
Match / Secret El valor detectado — el pipeline usa --redact, así que en el reporte real aparece censurado, no en texto plano
Commit En qué commit se introdujo (solo aplica al scan histórico, no al scan --no-git del pipeline)

!!! danger "Si el hallazgo es real" 1. No lo borres del código y listo — el secreto sigue "filtrado" aunque ya no esté en el archivo actual. 2. Rota la credencial primero en el sistema que la emitió (AWS, Gitea, Medusa, lo que sea). Un secreto que ya se vio en un log de CI o en un diff de PR se trata como comprometido. 3. Después de rotarla, sí, saca el valor viejo del código y usa una variable de entorno / secret de Gitea Actions en su lugar. 4. Si el secreto llegó a estar en main (no solo en una rama de PR), avisa antes de reescribir historial — reescribir historial en un repo compartido tiene sus propios riesgos y hay que decidirlo con calma, no como reacción automática del pipeline.

Falsos positivos: cómo hacer allowlist

Gitleaks detecta formas, no intención. Cosas que típicamente generan falsos positivos en este proyecto:

  • Placeholders como CAMBIAR en commerce/secrets.template.yamlno deberían disparar nada porque no tienen la forma de un secreto real (baja entropía, texto plano legible), pero si algún día se usa un placeholder con más pinta de secreto real (ej. un UUID de ejemplo), sí puede hacer match.
  • Hashes largos o IDs opacos que no son secretos (ej. MEDUSA_REGION_ID en frontend.yaml), si tienen entropía suficientemente alta.

Cuando gitleaks marca algo que no es un secreto real, se agrega una regla de allowlist en un archivo .gitleaks.toml en la raíz del repo (todavía no existe — se crea la primera vez que haga falta):

[allowlist]
description = "Falsos positivos conocidos del lab"
regexes = [
  '''MEDUSA_REGION_ID''',
]
paths = [
  '''workloads/ecommerce/commerce/secrets\.template\.yaml''',
]

!!! warning "No es una vía rápida para ignorar hallazgos reales" Cada entrada de allowlist debe quedar documentada (por qué es un falso positivo, no solo "molestaba") y revisada antes de mergear, porque una allowlist mal escrita (una regex demasiado amplia) puede silenciar un secreto real futuro sin que nadie se dé cuenta.

Dónde ver el resultado

El job gitleaks publica el reporte JSON como artifact del pipeline (gitleaks-report) en cada ejecución, tenga o no hallazgos — así queda disponible para inspección incluso cuando el scan pasa limpio.