Files
devops 4aed9f6c5c feat(devsecops): agregar Semgrep SAST en modo auditoría al pipeline
Escanea workloads/ecommerce con rulesets públicos del registro de
Semgrep (p/typescript, p/react, p/nextjs, p/security-audit) vía la
imagen oficial semgrep/semgrep:1.173.0. Sin --error a propósito: siempre
termina en exit 0, solo reporta — primera vuelta para revisar juntos qué
reglas deberían pasar a bloquear más adelante.

Validado contra el código real: 0 hallazgos en 91 reglas / 72 archivos.
Documenta la herramienta en docs/devsecops/sast.md, incluyendo la
diferencia con gitleaks/trivy y qué significa (y qué no) un scan limpio.
2026-08-14 21:07:05 -05:00

116 lines
5.3 KiB
Markdown

# SAST (Semgrep) — análisis estático de código
!!! info "Qué problema resuelve"
SAST significa *Static Application Security Testing*: analizar el
**código fuente** en busca de patrones de programación inseguros, sin
ejecutar la aplicación. Semgrep lee cada archivo `.ts`/`.tsx` y lo
compara contra un catálogo de reglas — cada regla describe una forma
de escribir código que suele terminar en una vulnerabilidad conocida
(inyección, XSS, uso inseguro de una API, etc.).
## En qué se diferencia de Gitleaks y Trivy
Las tres herramientas ya integradas a este pipeline analizan cosas
completamente distintas — vale la pena tenerlo claro porque a primera
vista "escaneo de seguridad" suena como una sola categoría:
| Herramienta | Qué mira | Pregunta que responde |
|---|---|---|
| [Gitleaks](gitleaks.md) | El texto de los archivos y el historial de git | "¿Hay una credencial commiteada?" |
| [Trivy](trivy.md) | Paquetes instalados (imagen) y configuración (YAML) | "¿Alguna dependencia tiene una CVE conocida? ¿El manifiesto de Kubernetes es inseguro?" |
| **Semgrep (SAST)** | La **lógica** del código que escribimos nosotros | "¿Esta función, tal como está escrita, abre una vulnerabilidad?" |
Ninguna de las tres reemplaza a las otras. Una dependencia puede estar
100% al día (sin CVEs, Trivy contento) y aun así el código propio puede
construir una URL con un string sin sanitizar y quedar abierto a SSRF —
eso solo lo detecta un análisis de la lógica del código, que es
exactamente lo que hace Semgrep.
## Qué tipo de bugs detecta (ejemplos genéricos)
Los rulesets usados (`p/typescript`, `p/react`, `p/nextjs`,
`p/security-audit`) cubren, entre otras cosas:
- **Inyección**: construir queries, comandos de shell o URLs concatenando
strings con datos que vienen del usuario, en vez de usar una API
parametrizada.
- **XSS en React/Next.js**: usar `dangerouslySetInnerHTML` con contenido
que no pasó por un sanitizador.
- **SSRF**: hacer un `fetch()`/request server-side hacia una URL que
construye el propio usuario, sin validar el host de destino.
- **Criptografía insegura**: algoritmos de hash débiles (`md5`, `sha1`)
usados para contraseñas o tokens, en vez de un KDF diseñado para eso.
- **`eval` / `new Function()`** sobre datos no confiables.
- **ReDoS**: expresiones regulares con backtracking exponencial que un
input malicioso puede usar para colgar el proceso.
- **Prototype pollution**: merges/asignaciones dinámicas de objetos que
permiten sobrescribir `__proto__`.
## Resultado real de esta primera corrida
```text
Scanning 72 files tracked by git with 292 Code rules:
ts 87 rules 39 files
js 81 rules 1 file
json 1 rule 6 files
Ran 91 rules on 72 files: 0 findings.
```
`workloads/ecommerce` salió limpio con estos rulesets: **0 hallazgos**.
!!! warning "0 hallazgos no significa 'código perfecto'"
Significa que ningún patrón conocido de estos 91 rules hizo match —
no es una garantía de ausencia de bugs, solo de ausencia de *estos*
patrones específicos. SAST tiene falsos negativos por naturaleza (un
bug de lógica de negocio nuevo, específico de esta app, no está en
ningún ruleset genérico). El valor de correr esto en cada build no es
"una vez limpio, siempre limpio" — es que si alguien introduce a
futuro uno de estos patrones conocidos (por ejemplo, un
`dangerouslySetInnerHTML` sin sanitizar en un componente nuevo), el
pipeline lo va a marcar en ese mismo push.
## Por qué modo auditoría (no bloquea) en esta primera integración
El step corre **sin** la flag `--error` a propósito — Semgrep siempre
termina con `exit 0`, reporta lo que encuentra pero nunca frena el
pipeline. Es la primera vez que esta herramienta corre sobre el repo: la
idea es revisar juntos qué reglas de los 91 activos generan ruido (falsos
positivos específicos de este código) antes de decidir cuáles deberían
pasar a bloquear.
```mermaid
flowchart LR
A[Semgrep scan] --> B{hallazgos?}
B -- "sí" --> C["se reportan en el log<br/>+ artifact JSON"]
B -- "no" --> C
C --> D["✅ pipeline sigue<br/>(exit 0 siempre)"]
```
Una vez que se decida qué reglas son suficientemente confiables para
esta app, el paso natural es agregar `--error` **con un subconjunto**
de reglas (no las 91 completas) usando `--config` más específico o
`.semgrepignore` / reglas individuales marcadas como bloqueantes —
todavía no se hizo ese recorte.
## Los rulesets elegidos
- `p/typescript` — patrones generales de TypeScript/JavaScript.
- `p/react` — específico de componentes React (hooks mal usados, XSS vía
props/render, etc.).
- `p/nextjs` — patrones propios del framework (App Router, API routes,
middlewares).
- `p/security-audit` — catálogo transversal de seguridad (inyección,
criptografía débil, deserialización insegura) sin atarse a un
framework específico.
Son rulesets **públicos y gratuitos** del registro de Semgrep — no
requieren cuenta ni login (`semgrep login` solo hace falta para acceder a
reglas adicionales de pago, que no se usan acá).
## Dónde ver el resultado
El JSON completo (`semgrep-report.json`) se publica como artifact del
pipeline (`semgrep-report`) en cada corrida, tenga o no hallazgos —
mismo patrón que Gitleaks y Trivy.