---
name: devsecops-pipeline-security
description: Integración de gates de seguridad en el pipeline CI/CD. Cubre SAST (CodeQL, Semgrep con reglas custom, output SARIF), DAST (OWASP ZAP baseline + autenticado), secret scanning (gitleaks pre-commit + CI), container scanning (Trivy para imágenes y SBOM), supply chain (dependency review, license checks), y diseño de quality gates en PR (bloquear por severidad, no por hallazgos totales). Consolida los 5 controles defensivos que deben existir en todo pipeline productivo. Cargar al diseñar CI/CD para un proyecto nuevo, al auditar seguridad del pipeline existente, o al reforzar una release con controles antes de producción.
version: "1.0.0"
herramientasPermitidas: [Read, Bash, Grep]
evolvable: true  # default para skill estandar
nist_csf: [ID.RA-01, GV.SC-07, PR.PS-01, PR.PS-06, DE.CM-09, RS.MI-01]
nist_ai_rmf: [MAP-1.6]
attack_techniques: [T1195.002, T1190, T1552.001]
exclusiones:
  - "No cargar para revisión manual de código buscando vulnerabilidades (OWASP Top 10 en un endpoint concreto) — para revisión dirigida cargar `checklist-seguridad` o el skill de revisión del lenguaje."
  - "No cargar para hardening de runtime de aplicaciones (WAF, rate limiting, configuración TLS en load balancer) — este skill cubre gates pre-deploy, no runtime en producción. Para runtime ver skills de cloud-aws/azure-cloud/gcp-cloud."
  - "No cargar para respuesta a incidentes o análisis forense post-breach — este skill es preventivo en el pipeline, no investigación. Ese dominio está fuera del scope de swl-ses."
  - "No cargar para pentesting ofensivo o red teaming — el foco es defensa automatizada del pipeline, no evaluación adversaria activa con autorización formal."
---

# DevSecOps — Gates de Seguridad en Pipeline CI/CD

Integración sistemática de controles de seguridad antes del despliegue. Cinco
gates que toda aplicación productiva debe tener, con el trade-off de que
implementarlos mal genera ruido y fatiga de alertas — y los equipos los
desactivan en lugar de arreglarlos.

## Cuándo cargar

- Al diseñar el CI/CD para un proyecto nuevo (fase de scaffolding)
- Al auditar la seguridad del pipeline existente y encontrar gaps (ej: sin SAST, sin scanning de contenedores)
- Al preparar una release con requerimientos de compliance (SOC 2, ISO 27001, PCI-DSS exigen la mayoría de estos controles)
- Al configurar gates de PR que bloqueen merge por severidad de hallazgos
- Al incorporar un proyecto externo al sistema y validar que su pipeline cumple el estándar mínimo

## Cuándo NO cargar

- La tarea es revisión manual de código para un endpoint específico: usar `checklist-seguridad` o el skill del lenguaje
- La tarea es hardening de runtime en producción (WAF, TLS en load balancer): usar skills de cloud
- La tarea es respuesta a incidentes o forensia: fuera del scope de swl-ses
- La tarea es pentesting ofensivo con autorización formal: fuera del scope (dominio red team)

## Los 5 gates obligatorios

El pipeline productivo mínimo tiene estos 5 gates en el orden siguiente:

| # | Gate | Momento | Herramienta recomendada | Bloqueo |
|---|------|---------|-------------------------|---------|
| 1 | Secrets scanning | Pre-commit + CI | gitleaks | Sí — cualquier secret detectado |
| 2 | SAST | PR en CI | Semgrep + CodeQL | Condicional — solo CRITICAL/HIGH |
| 3 | Dependency audit | PR en CI | pip-audit / npm audit / osv-scanner | Condicional — CVE ≥ HIGH sin fix |
| 4 | Container scanning | Post-build | Trivy | Condicional — CRITICAL sin workaround |
| 5 | DAST (baseline) | Post-deploy a staging | OWASP ZAP | Condicional — new HIGH findings |

## Gate 1 — Secret scanning

### Herramienta base: gitleaks

Detección de credenciales hardcodeadas, API keys, tokens, claves privadas.

**Pre-commit hook** (ejecuta localmente antes del push):

```bash
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
```

**CI gate** (GitHub Actions):

```yaml
- name: Secret scan (gitleaks)
  uses: gitleaks/gitleaks-action@v2
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
```

**Configuración**: `.gitleaks.toml` con reglas custom del dominio (ej: detectar patrones de tokens internos que no están en el default de gitleaks).

**Decisión de bloqueo**: SÍ — cualquier secret detectado. Sin excepción. Si aparece un falso positivo, documentarlo en `.gitleaksignore` con justificación comentada.

## Gate 2 — SAST (Static Application Security Testing)

### Herramienta base: Semgrep + CodeQL (complementarios)

- **Semgrep**: rápido, reglas custom en YAML, cobertura amplia de lenguajes, comunidad activa con rule packs (r2c, OWASP)
- **CodeQL** (GitHub): análisis profundo, data flow taint tracking, mejor para C/C++/Java. Viene gratis con GitHub public repos.

**Workflow recomendado** (GitHub Actions):

```yaml
jobs:
  sast:
    runs-on: ubuntu-latest
    permissions:
      security-events: write  # para SARIF upload
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep
        uses: semgrep/semgrep-action@v1
        with:
          config: >-
            p/security-audit
            p/owasp-top-ten
            p/secrets
            .semgrep/custom-rules/
          generateSarif: true

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif

      - name: CodeQL init
        uses: github/codeql-action/init@v3
      - name: CodeQL analyze
        uses: github/codeql-action/analyze@v3
```

**Reglas custom**: mantener en `.semgrep/custom-rules/` con reglas específicas del dominio (ej: prohibir `eval()` en Node, prohibir `shell=True` en Python subprocess, detectar patrones de inyección propios).

**Decisión de bloqueo**: CRITICAL y HIGH → bloquear PR. MEDIUM y LOW → comentario en PR pero no bloquean. INFO → solo dashboard.

## Gate 3 — Dependency audit (supply chain)

### Herramientas por ecosistema

| Lenguaje | Herramienta | Comando |
|----------|-------------|---------|
| Python | pip-audit | `pip-audit --format json --output audit.json` |
| Node.js | npm audit / osv-scanner | `npm audit --audit-level=high --json` |
| Java | OWASP Dependency-Check | Maven/Gradle plugin |
| Go | govulncheck | `govulncheck ./...` |
| Rust | cargo-audit | `cargo audit --format json` |
| Cross-lang | osv-scanner (Google) | `osv-scanner --lockfile=X -r .` |

**Política**: dependencia con CVE HIGH/CRITICAL sin fix disponible → bloquear
merge hasta mitigación documentada; con fix disponible → autofix por Dependabot/Renovate.

**License compliance**: complementar con checker de licencias. Incompatibles
con el uso del proyecto (GPL en código cerrado) → bloquear.

**Decisión de bloqueo**: CVE HIGH/CRITICAL sin workaround documentado →
bloquear. Licencia incompatible → bloquear sin excepción.

## Gate 4 — Container scanning

### Herramienta base: Trivy

Escáner gratuito de contenedores con cobertura OS packages + dependencies +
IaC + secrets.

**Integración en build** (GitHub Actions):

```yaml
- name: Build image
  run: docker build -t app:$GITHUB_SHA .

- name: Trivy scan
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: app:${{ github.sha }}
    format: sarif
    output: trivy-results.sarif
    severity: CRITICAL,HIGH
    exit-code: 1  # falla el job si encuentra CRITICAL/HIGH

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif
```

**SBOM generation**: Trivy también genera SBOM (SPDX o CycloneDX) útil para
compliance:

```bash
trivy image --format cyclonedx --output sbom.json app:latest
```

**Política**: CRITICAL sin workaround → bloquear push a registry. HIGH con
workaround documentado en issue → permitir con TODO.

## Gate 5 — DAST en staging

### Herramienta base: OWASP ZAP

Análisis dinámico sobre la aplicación desplegada en staging (no producción).

**Baseline scan** (pasivo, rápido):

```yaml
- name: ZAP Baseline Scan
  uses: zaproxy/action-baseline@v0.10.0
  with:
    target: 'https://staging.example.com'
    fail_action: true
    cmd_options: '-a -j -l WARN'
```

**Full scan** (activo, lento — solo en release):

```yaml
- name: ZAP Full Scan
  uses: zaproxy/action-full-scan@v0.10.0
  with:
    target: 'https://staging.example.com'
    cmd_options: '-z "-config api.key=${{ secrets.ZAP_API_KEY }}"'
```

**Autenticación**: configurar ZAP con session token o flujo de auth para
scanear endpoints protegidos. Sin auth configurada, solo se cubre la capa
pública.

**Decisión de bloqueo**: NEW HIGH findings (no regresiones conocidas) →
bloquear promoción a producción. Los hallazgos conocidos deben tener
excepciones documentadas con fecha de revisión.

## Diseño de quality gates en PR

### Principio: bloquear por severidad, no por cantidad

Un PR con 50 hallazgos INFO y 0 HIGH es aceptable. Un PR con 1 CRITICAL es
bloqueante.

### Thresholds recomendados

```yaml
# .github/workflows/security-gates.yml
- name: Check severity
  run: |
    critical=$(jq '[.runs[].results[] | select(.level=="error" and .properties.severity=="critical")] | length' results.sarif)
    if [ "$critical" -gt 0 ]; then
      echo "FAIL: $critical critical findings"
      exit 1
    fi
```

### Fatigue avoidance

- Suppresiones deben tener fecha de revisión y justificación en commit message
- Dashboard de tendencias de hallazgos (no solo gates) para visibilidad a largo plazo
- Alertas solo en hallazgos NEW, no en los ya conocidos; los conocidos van a backlog
- Rotación periódica de reglas personalizadas para eliminar las que nunca disparan o siempre lo hacen en falso positivo

## Integración incremental para un proyecto existente

Adoptar los 5 gates de golpe genera parálisis. Orden sugerido:

1. **Semana 1** — Gate 1 (secrets): pre-commit + CI con gitleaks. Alto ROI, bajo esfuerzo.
2. **Semana 2** — Gate 3 (deps): auto-audit del ecosistema del proyecto. Dependabot/Renovate.
3. **Semana 3-4** — Gate 2 (SAST): Semgrep con reglas comunitarias; reglas custom más tarde.
4. **Semana 5-6** — Gate 4 (containers): Trivy solo en imágenes de producción.
5. **Semana 8+** — Gate 5 (DAST): ZAP baseline una vez que staging es estable.

Cada gate activado en modo "observación" (no bloquea) 2-3 semanas antes de
volverse bloqueante. Durante observación, limpiar falsos positivos.

## Gotchas / Errores comunes no obvios

- **Activar todos los gates como bloqueantes desde día 1**: genera fatiga de alertas masiva, suppresiones indiscriminadas y eventualmente desactivación del pipeline. Causa: sin dataset de falsos positivos del dominio específico, las reglas genéricas generan ruido insoportable. Solución: cada gate arranca en modo observación (no bloquea); calibrar 2-3 semanas; luego volverse bloqueante. Si un gate siempre genera más de 20 hallazgos por PR, la señal está rota.
- **Suppresiones sin fecha de revisión**: agregar `# nosemgrep` o `.gitleaksignore` permanente sin revisarlo convierte el gate en teatro. Causa: las suppresiones acumuladas dejan de ser excepciones y se vuelven la regla. Solución: exigir comentario en toda suppresión con fecha de revisión (ej: `# nosemgrep: justificado 2026-04-20, revisar 2026-10-20`); script mensual que lista suppresiones vencidas.
- **DAST sin autenticación configurada**: scanear solo la capa pública no detecta vulnerabilidades en endpoints autenticados — donde está la mayoría de datos sensibles. Causa: configurar auth en ZAP requiere trabajo adicional y se salta. Solución: no declarar DAST "hecho" hasta que cubra endpoints autenticados; si el proyecto no tiene auth, documentarlo explícitamente como excepción.
- **Confiar en SARIF upload como sustituto de revisión humana**: SARIF al GitHub Security tab es útil para visibilidad pero no genera acción si nadie lo revisa. Causa: los dashboards aumentan la sensación de cobertura sin necesariamente mejorar la respuesta. Solución: complementar SARIF con alertas activas a un canal (Slack/email) para hallazgos NEW de severidad alta; no confundir "upload exitoso" con "hallazgo atendido".
- **Escanear contenedores solo en prod, no en dev**: los entornos de desarrollo tienen dependencias más permisivas y sirven de superficie de ataque si se despliegan accidentalmente. Causa: los equipos asumen que "solo prod importa" y pasan por alto staging/dev. Solución: aplicar Trivy a todas las imágenes que se suben a un registry — dev, staging, prod. El criterio de bloqueo puede ser más laxo en dev, pero el gate debe existir.
- **Dependabot/Renovate sin merge automático bajo condiciones**: los PRs de bump acumulan sin merge, y cuando se necesitan todos a la vez, romper cosas se vuelve inevitable. Causa: revisar cada bump manualmente es alta fricción; sin automatización los PRs se estancan. Solución: auto-merge bump de patch + tests pasando; bump de minor/major requiere revisión; bump de security → auto-merge si CVE HIGH/CRITICAL independiente del tipo.
- **Reglas custom de Semgrep sin tests**: escribir una regla custom sin fixtures positivas y negativas genera detecciones incorrectas. Causa: los patrones AST son sutiles; un regex mal calibrado atrapa casos irrelevantes. Solución: toda regla custom incluye archivo `rule-test.yml` con casos positivos (debe detectar) y negativos (no debe detectar); Semgrep tiene `semgrep --test` para validación automática.
- **Reportar DAST findings como críticos sin verificar explotabilidad**: ZAP marca "High" por patrones que pueden no ser explotables en el contexto real (ej: header missing que la app no usa). Causa: los scanners son conservadores por diseño. Solución: triage de findings HIGH con verificación de explotabilidad antes de bloquear; los hallazgos con "Information Disclosure" raramente justifican bloqueo de release sin contexto adicional.
- **Validator con centinela string hardcodeado divergente del `.env.example` real** [CONFIRMADO 2026-05-20, SIGAF]: un validator de config tipo `_CENTINELA = "CAMBIAR_POR_CLAVE_SEGURA_EN_PRODUCCION"` rechaza solo ese string exacto. Si el `.env.example` del repo usa una variante (`"CAMBIAR_POR_CLAVE_SEGURA_256_BITS_EN_PRODUCCION"` con sufijo `_256_BITS_`), el validator NO la matchea — la app arranca en cualquier ambiente con el secret trivial conocido públicamente en el repo. Causa: un refactor del `.env.example` para clarificar el placeholder abre bypass silencioso del validator. El test de regresión solo cubre el centinela exacto, no las variantes. Solución: en validators de placeholder/centinela, usar `frozenset[str]` con TODAS las variantes conocidas + regex catch-all de prefijos típicos (`^(CAMBIAR_POR|GENERAR_|REEMPLAZAR_|CHANGE_ME|TODO|FIXME|XXX_|<.*>)`). Defensa en profundidad doble: si alguien introduce una variante futura no enumerada, el regex la atrapa. Tests de regresión deben cubrir explícitamente el placeholder real del `.env.example` actual.
- **Step de upload SARIF sin `continue-on-error: true` mata el workflow en repos privados sin GHAS** [CONFIRMADO 2026-05-20, SIGAF]: repos PRIVATE en organización requieren GitHub Advanced Security (GHAS) licenciado para aceptar uploads SARIF third-party. Sin GHAS la API responde 403 `"Code Security must be enabled for this repository to use code scanning"` y el step falla. CodeQL aparece success cosmético porque su action `github/codeql-action/analyze` embebe el upload con `continue-on-error` interno; Semgrep tiene scan y upload como steps separados — solo el scan tiene `continue-on-error: true`, el upload mata el workflow. Causa: el skill cubre el "happy path" (repo público o con GHAS) sin advertir el caso edge común "repo privado de organización sin GHAS". Solución: en cualquier workflow con upload SARIF al Security tab, el step de upload DEBE tener `continue-on-error: true` para resiliencia ante 3 escenarios — (a) GHAS no habilitado, (b) API GitHub caída, (c) PR desde fork sin secrets. SIEMPRE adjuntar el SARIF también como artefacto del run con `actions/upload-artifact` — los hallazgos quedan accesibles independientemente del estado de GHAS. Cuando GHAS se habilite, basta eliminar el `continue-on-error` para tener integración completa con el Security tab.
- **`upload-artifact` espera archivo no generado por step previo, con `if-no-files-found: warn` enmascara fallo silenciosamente** [CONFIRMADO 2026-05-20, SIGAF]: workflow de gitleaks con step `gitleaks detect --verbose` (sin `--report-format json --report-path X`) seguido de `actions/upload-artifact` apuntando a `path: gitleaks-report.json` que nunca se genera. `if-no-files-found: warn` (default) emite warning pero el job pasa exitoso → debugging imposible cuando aparezcan hallazgos en el futuro. Mismo patrón aplica a cualquier scanner que NO emite reporte por defecto (Trivy, Semgrep sin `--output`, OWASP ZAP). Causa: dos suposiciones no validadas — (1) el scanner emite el reporte automáticamente (FALSO en muchos casos), (2) `warn` es defecto seguro (FALSO si el archivo se espera). Solución: cuando un step de CI consume archivo producido por step previo, verificar (a) el comando genera ese archivo (leer docs del CLI, no asumir), (b) `if-no-files-found: error` (no `warn`) en uploads críticos donde la ausencia del archivo indica fallo real, (c) si el archivo es opcional (ej. cobertura), `warn` es correcto pero documentar por qué. Verificación pre-push: descargar el CLI del scanner localmente y ejecutarlo contra el repo para confirmar que el flag de output funciona como se espera (patrón validado: detectó 12 CVEs reales antes del push en sesión SIGAF 2026-05-20).

## Integración con skills SWL existentes

| Skill SWL | Relación con este skill |
|-----------|-------------------------|
| `checklist-seguridad` | Revisión manual; este skill automatiza parte del checklist en CI |
| `dependencias-auditoria` | Gate 3; este skill lo integra en pipeline con política de bloqueo |
| `iam-secretos` | Gate 1; este skill implementa detección automatizada de secrets |
| `ci-cd-pipelines` | Este skill complementa con capa de seguridad sobre el pipeline genérico |
| `contenedores-docker` | Gate 4; este skill agrega capa de scanning |
| `ai-runtime-security` | Skill hermano — runtime security (post-deploy) vs pipeline security (pre-deploy) |

## Referencias

- NIST CSF 2.0 PR.PS (Platform Security): https://www.nist.gov/cyberframework
- OWASP DevSecOps Maturity Model: https://owasp.org/www-project-devsecops-maturity-model/
- Semgrep rule registry: https://semgrep.dev/explore
- CodeQL (GitHub): https://codeql.github.com/
- OWASP ZAP: https://www.zaproxy.org/
- Trivy (Aqua Security): https://aquasecurity.github.io/trivy/
- gitleaks: https://github.com/gitleaks/gitleaks
- Skills origen (Apache 2.0) en `temp/Anthropic-Cybersecurity-Skills-main/skills/`:
  - `integrating-sast-into-github-actions-pipeline/`
  - `implementing-semgrep-for-custom-sast-rules/`
  - `integrating-dast-with-owasp-zap-in-pipeline/`
  - `implementing-secret-scanning-with-gitleaks/`
  - `scanning-containers-with-trivy-in-cicd/`

## Checklist de verificación

- [ ] Gate 1 (secrets) activo en pre-commit + CI
- [ ] Gate 2 (SAST) con Semgrep + CodeQL, SARIF upload a GitHub Security
- [ ] Gate 3 (deps) con scanner del ecosistema + Dependabot/Renovate + license check
- [ ] Gate 4 (containers) con Trivy en todas las imágenes (dev/staging/prod)
- [ ] Gate 5 (DAST) con ZAP baseline en staging, autenticación configurada
- [ ] Quality gates bloquean por severidad (CRITICAL/HIGH), no por cantidad total
- [ ] Reglas custom de Semgrep con fixtures positivas y negativas verificables con `semgrep --test`
- [ ] Suppresiones con fecha de revisión y justificación
- [ ] Dashboard de tendencias (no solo gates) para visibilidad a largo plazo
- [ ] Alertas activas de hallazgos NEW (no solo dashboards pasivos)
