# 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. ```mermaid flowchart LR A[push / pull_request] --> B[gitleaks] B -- "sin hallazgos" --> C[build] B -- "secreto detectado" --> X["❌ pipeline detenido
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í: ```json { "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.yaml` — **no** 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): ```toml [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.