Semgrep
Herramienta de análisis estático (SAST) que busca patrones en código fuente. A diferencia de herramientas tradicionales, Semgrep usa patrones similares al código real, lo que facilita escribir y entender reglas personalizadas.
Instalación
# macOS
brew install semgrep
# pip
python3 -m pip install semgrep
# Docker
docker run --rm -v "${PWD}:/src" semgrep/semgrep semgrep --config=auto /src
# Verificar
semgrep --version
Escaneo rápido
# Usar reglas recomendadas de la comunidad
semgrep --config=auto .
# Usar un ruleset específico del registry
semgrep --config=p/owasp-top-ten .
# Escanear solo ciertos lenguajes
semgrep --config=auto --lang=python,javascript src/
# Escanear un archivo o directorio específico
semgrep --config=auto src/api/
Rulesets útiles del registry
| Ruleset | Descripción |
|---|---|
p/owasp-top-ten | OWASP Top 10 |
p/security-audit | Auditoría general de seguridad |
p/secrets | Secretos hardcodeados (tokens, passwords, API keys) |
p/ci | Reglas optimizadas para CI (bajo ruido) |
p/python | Seguridad + buenas prácticas Python |
p/javascript | Seguridad + buenas prácticas JavaScript |
p/golang | Seguridad + buenas prácticas Go |
p/docker | Mejores prácticas en Dockerfiles |
p/kubernetes | Configuraciones inseguras en manifiestos K8s |
# Combinar varios rulesets
semgrep --config=p/owasp-top-ten --config=p/secrets --config=p/docker .
Formatos de salida
# Tabla en terminal (por defecto)
semgrep --config=auto .
# JSON para post-proceso
semgrep --config=auto --json -o report.json .
# SARIF para GitHub Code Scanning
semgrep --config=auto --sarif -o report.sarif .
# Solo mostrar findings de severidad alta
semgrep --config=auto --severity=ERROR .
Flags útiles
--severity=ERROR— solo findings críticos--exclude='tests/**'— excluir directorios--include='*.py'— solo ciertos archivos--max-target-bytes=1000000— limitar tamaño de archivos--no-git-ignore— incluir archivos en.gitignore--verbose— detalle de reglas ejecutadas--debug— troubleshooting de reglas
Reglas personalizadas
La principal ventaja de Semgrep es lo fácil que es escribir reglas propias. Los patrones se parecen al código real, usando ... como comodín.
Estructura básica
rules:
- id: mi-regla
pattern: <patrón>
message: <mensaje para el desarrollador>
languages: [python]
severity: WARNING # INFO | WARNING | ERROR
metadata:
category: security
cwe: "CWE-89: SQL Injection"
Patrones comunes
Detectar inyección SQL
rules:
- id: python-sqli-format
patterns:
- pattern: cursor.execute(f"...")
message: >
Posible SQL injection — usa consultas parametrizadas
con cursor.execute("SELECT ... WHERE id = %s", (id,))
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-89"
Detectar uso de eval
rules:
- id: no-eval
pattern: eval(...)
message: Evita eval() — usa alternativas seguras como ast.literal_eval()
languages: [python]
severity: ERROR
Detectar secretos hardcodeados
rules:
- id: hardcoded-password
patterns:
- pattern: $VAR = "..."
- metavariable-regex:
metavariable: $VAR
regex: (?i)(password|secret|token|api_key)
message: Posible secreto hardcodeado en $VAR — usa variables de entorno
languages: [python, javascript, typescript]
severity: ERROR
Detectar HTTP sin TLS
rules:
- id: no-http-requests
pattern: requests.get("http://...")
message: Usa HTTPS en vez de HTTP
languages: [python]
severity: WARNING
Operadores de patrones
| Operador | Uso |
|---|---|
pattern | Coincidencia directa |
patterns | AND — todos deben coincidir |
pattern-either | OR — cualquiera coincide |
pattern-not | Excluir coincidencias |
pattern-inside | Buscar dentro de otro patrón |
pattern-not-inside | Excluir si está dentro de un patrón |
metavariable-regex | Filtrar metavariables con regex |
metavariable-comparison | Comparar valores de metavariables |
Ejemplo avanzado: combinando operadores
rules:
- id: flask-open-redirect
patterns:
- pattern: redirect($URL)
- pattern-not: redirect("/...")
- pattern-inside: |
@app.route(...)
def $FUNC(...):
...
message: >
Posible open redirect — valida $URL antes de redirigir
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-601"
Ejecutar reglas locales
# Archivo de reglas individual
semgrep --config=rules/my-rules.yaml src/
# Directorio de reglas
semgrep --config=rules/ src/
# Combinar reglas locales con registry
semgrep --config=rules/ --config=p/owasp-top-ten src/
Integración CI
GitHub Actions
name: Semgrep
on:
pull_request:
push: { branches: [ main ] }
jobs:
scan:
runs-on: ubuntu-latest
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@v4
- name: Scan
run: |
mkdir -p reports
semgrep --config=auto --sarif -o reports/semgrep.sarif . || true
semgrep --config=auto --json -o reports/semgrep.json . || true
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: reports/semgrep.sarif }
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with: { name: semgrep-reports, path: reports/ }
Política de fallo
# Bloquear PR si hay findings de severidad ERROR
semgrep --config=auto --severity=ERROR --error .
El flag --error hace que Semgrep retorne exit code 1 si encuentra findings, útil para fallar el pipeline.
GitLab CI
semgrep:
image: semgrep/semgrep
script:
- semgrep --config=auto --sarif -o semgrep.sarif .
artifacts:
reports:
sast: semgrep.sarif
Ignorar findings
En código (inline)
# nosemgrep: mi-regla
result = eval(user_input)
// nosemgrep
const token = "hardcoded-for-testing";
Con .semgrepignore
Crear .semgrepignore en la raíz del repo (sintaxis similar a .gitignore):
# Ignorar tests
tests/
*_test.py
*.test.js
# Ignorar vendors / dependencias
vendor/
node_modules/
third_party/
# Ignorar archivos generados
*.generated.go
*.pb.go
Errores comunes
- Demasiados false positives — empieza con
p/cique tiene reglas de bajo ruido, luego añade rulesets según necesites - Escaneo lento — usa
--excludepara ignorar directorios grandes (vendors, node_modules) y--max-target-bytespara archivos pesados - Regla no encuentra nada — verifica el lenguaje con
--langy prueba el patrón en semgrep.dev/playground --config=autorequiere login — en versiones recientes necesitassemgrep logino usar rulesets específicos como--config=p/ci
Referencias
- https://semgrep.dev/docs
- https://semgrep.dev/r — Registry de reglas
- https://semgrep.dev/playground — Playground para probar patrones