Como usar o Semgrep para análise estática focada em segurança
1. Introdução ao Semgrep e análise estática de segurança
A análise estática de segurança (SAST — Static Application Security Testing) é uma técnica essencial para identificar vulnerabilidades no código-fonte antes da execução. Diferente de ferramentas tradicionais como SonarQube (focada em qualidade geral) ou Bandit (específica para Python), o Semgrep se destaca por sua flexibilidade e capacidade de combinar padrões sintáticos com análise de fluxo de dados.
O Semgrep permite que desenvolvedores e times de segurança criem regras personalizadas para detectar vulnerabilidades como injeção SQL, Cross-Site Scripting (XSS), vazamento de secrets e uso inseguro de APIs. Sua principal vantagem é a sintaxe intuitiva baseada em padrões de código, sem a necessidade de compilar ou executar a aplicação.
Casos de uso típicos incluem:
- Bloquear eval() em JavaScript
- Identificar concatenação de strings em queries SQL
- Detectar senhas hardcoded em variáveis de ambiente
- Prevenir uso de funções criptográficas inseguras
2. Instalação e configuração inicial do Semgrep
A instalação pode ser feita via pip (recomendado para ambientes de desenvolvimento):
pip install semgrep
Para ambientes conteinerizados, use Docker:
docker pull returntocorp/semgrep:latest
docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep --config=auto /src
Em sistemas Linux, também é possível instalar via gerenciador de pacotes:
# Ubuntu/Debian
sudo apt-get install semgrep
# macOS (Homebrew)
brew install semgrep
Estrutura básica de um projeto Semgrep:
meu-projeto/
├── .semgrep/
│ └── rules/
│ ├── security/
│ │ ├── sql-injection.yaml
│ │ └── xss.yaml
│ └── best-practices/
├── src/
└── .semgrepignore
Para executar seu primeiro scan:
semgrep --config=auto .
O modo --config=auto baixa automaticamente as regras da comunidade e as aplica ao diretório atual.
3. Estrutura de uma regra Semgrep para segurança
Uma regra Semgrep possui componentes essenciais:
rules:
- id: no-eval-js
pattern: eval(...)
message: "Uso de eval() detectado. Risco de execução de código malicioso."
severity: ERROR
languages:
- javascript
- typescript
Explicação dos campos:
- id: Identificador único da regra
- pattern: Padrão de busca (pode usar metavariables como $X)
- message: Mensagem exibida ao encontrar o padrão
- severity: Pode ser ERROR, WARNING ou INFO
- languages: Linguagens alvo da análise
Padrões avançados incluem:
- pattern-inside: Busca dentro de um contexto específico
- pattern-not: Exclui padrões específicos
- metavariables: Capturam partes do código para reuso
Exemplo com pattern-inside:
rules:
- id: unsafe-query-param
patterns:
- pattern-inside: |
function $FUNC($REQ, $RES) {
...
}
- pattern: $REQ.query.$PARAM
message: "Parâmetro de query não validado"
severity: WARNING
languages: [javascript]
4. Criação de regras personalizadas para vulnerabilidades comuns
SQL Injection em Python
rules:
- id: sql-injection-concat
pattern: |
cursor.execute("SELECT * FROM users WHERE id = " + $USER_INPUT)
message: "SQL Injection detectado. Use parâmetros parametrizados."
severity: ERROR
languages: [python]
metadata:
cwe: "CWE-89"
XSS Refletido em JavaScript
rules:
- id: reflected-xss
patterns:
- pattern: document.write($USER_INPUT)
- pattern-not: document.write(escapeHTML($USER_INPUT))
message: "XSS Refletido. Sanitize a entrada do usuário."
severity: ERROR
languages: [javascript]
metadata:
cwe: "CWE-79"
Hardcoded Secrets
rules:
- id: hardcoded-password
pattern: |
$VAR = "..."
patterns:
- pattern: $VAR = "..."
- metavariable-regex:
metavariable: $VAR
regex: (password|secret|token|api_key)
message: "Credencial hardcoded detectada. Use variáveis de ambiente."
severity: ERROR
languages: [python, javascript, java]
5. Integração do Semgrep no pipeline de CI/CD
Exemplo de workflow para GitHub Actions:
name: Semgrep Security Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: p/default
severity: ERROR
- name: Upload results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: semgrep.sarif
Para usar regras pré-definidas da comunidade:
semgrep --config=p/default .
semgrep --config=p/security-audit .
semgrep --config=p/owasp-top-ten .
Para ignorar falsos positivos, use comentários nosemgrep:
# nosemgrep: rules.sql-injection-concat
cursor.execute("SELECT * FROM users WHERE id = " + safe_input)
6. Análise avançada: dataflow e taint tracking
O Semgrep suporta análise de fluxo de dados com o modo taint:
rules:
- id: taint-example
mode: taint
pattern-sources:
- pattern: request.GET.get(...)
- pattern: request.POST.get(...)
pattern-sinks:
- pattern: eval(...)
- pattern: exec(...)
pattern-sanitizers:
- pattern: escape(...)
- pattern: sanitize(...)
message: "Entrada do usuário atingindo função perigosa"
severity: ERROR
languages: [python]
Este modo rastreia automaticamente como dados não confiáveis (sources) fluem até funções perigosas (sinks), considerando sanitizers no caminho.
Exemplo prático de rastreamento:
# Código vulnerável
user_input = request.GET.get('cmd')
result = eval(user_input) # Semgrep detecta o fluxo
# Código seguro
user_input = request.GET.get('cmd')
sanitized = escape(user_input)
result = eval(sanitized) # Sanitizer interrompe o fluxo
7. Boas práticas e manutenção de regras de segurança
Versionamento de regras com Git:
# Estrutura de repositório de regras
regras-seguranca/
├── rules/
│ ├── sql/
│ ├── xss/
│ └── crypto/
├── tests/
│ ├── positive/
│ └── negative/
└── .github/workflows/test-rules.yml
Testes de regras com exemplos positivos e negativos:
# Teste positivo (deve detectar)
# test-positive.py
user_input = "1 OR 1=1"
cursor.execute("SELECT * FROM users WHERE id = " + user_input)
# Teste negativo (não deve detectar)
# test-negative.py
user_input = "1"
cursor.execute("SELECT * FROM users WHERE id = %s", (user_input,))
Para validar regras:
semgrep --test --config=rules/ tests/
Atualização contínua: acompanhe CVEs e novos padrões de ataque. O repositório oficial do Semgrep (p/default) é atualizado semanalmente com novas regras.
8. Limitações e complementos ao Semgrep
O Semgrep não substitui:
- DAST (Dynamic Analysis): Não testa aplicações em execução
- Análise de lógica de negócio: Não entende regras de negócio complexas
- Análise de dependências: Não verifica bibliotecas de terceiros (use Snyk ou Dependabot)
Para uma estratégia completa de segurança, combine com:
- CSP (Content Security Policy): Mitigação de XSS no frontend
- SRI (Subresource Integrity): Verificação de integridade de scripts
- DAST: Testes dinâmicos com OWASP ZAP ou Burp Suite
Próximo passo: implementar um guardrail de segurança que bloqueie PRs com vulnerabilidades críticas, usando Semgrep como gatekeeper no pipeline.
Referências
- Semgrep Documentation — Documentação oficial completa, incluindo guias de instalação, criação de regras e integração CI/CD
- Semgrep Registry — Repositório oficial com milhares de regras de segurança pré-definidas para diversas linguagens
- OWASP Semgrep Rules — Conjunto de regras OWASP para detecção de vulnerabilidades do Top 10
- Semgrep GitHub Action — Action oficial para integrar Semgrep em workflows do GitHub Actions
- Semgrep Taint Mode Tutorial — Guia detalhado sobre configuração de análise de fluxo de dados com taint tracking
- Semgrep vs Bandit: Comparison — Artigo comparando Semgrep com Bandit para análise de segurança em Python
- CWE Top 25 with Semgrep — Mapeamento das 25 fraquezas de software mais perigosas com regras Semgrep