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.
This commit is contained in:
2026-08-14 21:07:05 -05:00
parent df13233a29
commit 4aed9f6c5c
3 changed files with 161 additions and 0 deletions
+45
View File
@@ -260,6 +260,51 @@ jobs:
--exit-code 0 \
"${MANIFEST_FILE}"
# Modo auditoría: sin --error a propósito, Semgrep siempre termina
# con exit 0 aunque reporte hallazgos. Es la primera vuelta — se
# revisan los resultados en conjunto antes de decidir qué reglas
# deberían pasar a bloquear el pipeline más adelante.
- name: Escaneo SAST (Semgrep) — modo auditoría, no bloquea
shell: bash
run: |
set -euo pipefail
SEMGREP_IMAGE="semgrep/semgrep:1.173.0"
docker run --rm \
-v "${{ github.workspace }}:/src" \
-w /src \
"${SEMGREP_IMAGE}" \
semgrep scan \
--config=p/typescript \
--config=p/react \
--config=p/nextjs \
--config=p/security-audit \
--json \
--output=semgrep-report.json \
"${APP_DIR}"
echo "=== Resumen Semgrep ==="
docker run --rm \
-v "${{ github.workspace }}:/src" \
-w /src \
"${SEMGREP_IMAGE}" \
python3 -c "
import json
data = json.load(open('semgrep-report.json'))
results = data.get('results', [])
print(f'Hallazgos: {len(results)}')
for r in results:
print(f\" [{r['extra']['severity']}] {r['check_id']} - {r['path']}:{r['start']['line']}\")
"
- name: Publicar reporte de Semgrep
if: always()
uses: actions/upload-artifact@v3
with:
name: semgrep-report
path: semgrep-report.json
if-no-files-found: ignore
- name: Login en Gitea Registry
uses: docker/login-action@v2
with: