Guia Completo de SAST em Pipelines CI: Semgrep, SonarQube e CodeQL na Prática Já leu

O que é SAST e Por Que Implementar em CI/CD SAST (Static Application Security Testing) é uma metodologia de análise de código-fonte sem executá-lo, buscando vulnerabilidades, padrões inseguros e violações de políticas de segurança antes do código chegar à produção. A implementação em pipelines CI garante que cada commit seja analisado automaticamente, bloqueando merges problemáticos e reduzindo custos de correção posterior. A diferença fundamental entre SAST e outras abordagens é que ela trabalha com o código-fonte direto, não com binários ou aplicações rodando. Isso permite detecção precoce de problemas como SQL injection, XSS, credenciais expostas e uso de bibliotecas desatualizadas. Integrar SAST no CI/CD transforma segurança em parte do fluxo de desenvolvimento, não um overhead pós-entrega. Semgrep: Pattern Matching Agnóstico e Customizável Conceito e Arquitetura Semgrep é um motor de busca de padrões que funciona sem dependências complexas e oferece suporte a múltiplas linguagens (Python, JavaScript, Java, Go, C, C++, etc.). Diferente de outras ferramentas, Semgrep usa uma linguagem de

O que é SAST e Por Que Implementar em CI/CD

SAST (Static Application Security Testing) é uma metodologia de análise de código-fonte sem executá-lo, buscando vulnerabilidades, padrões inseguros e violações de políticas de segurança antes do código chegar à produção. A implementação em pipelines CI garante que cada commit seja analisado automaticamente, bloqueando merges problemáticos e reduzindo custos de correção posterior.

A diferença fundamental entre SAST e outras abordagens é que ela trabalha com o código-fonte direto, não com binários ou aplicações rodando. Isso permite detecção precoce de problemas como SQL injection, XSS, credenciais expostas e uso de bibliotecas desatualizadas. Integrar SAST no CI/CD transforma segurança em parte do fluxo de desenvolvimento, não um overhead pós-entrega.

Semgrep: Pattern Matching Agnóstico e Customizável

Conceito e Arquitetura

Semgrep é um motor de busca de padrões que funciona sem dependências complexas e oferece suporte a múltiplas linguagens (Python, JavaScript, Java, Go, C, C++, etc.). Diferente de outras ferramentas, Semgrep usa uma linguagem de padrões simples baseada em YAML, permitindo criar regras customizadas em minutos sem conhecimento profundo em parsing ou AST.

O grande diferencial é a capacidade de escrever regras de negócio específicas da sua organização. Enquanto SonarQube e CodeQL focam em vulnerabilidades genéricas, Semgrep permite detectar padrões anti-pattern próprios, como uso de APIs internas proibidas ou patterns de logging inseguro que sua empresa quer evitar.

Instalação e Configuração Básica

A instalação é trivial comparada a outras ferramentas. No Linux/Mac, basta executar:

brew install semgrep
# ou via pip
pip install semgrep

Para verificar a instalação:

semgrep --version

Usando Semgrep em um Pipeline CI/CD

Vamos implementar Semgrep em um pipeline GitHub Actions. Crie o arquivo .github/workflows/semgrep.yml:

name: Semgrep SAST

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/security-audit
            p/owasp-top-ten
            p/python
          generateSarif: true

      - name: Upload SARIF to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: semgrep.sarif

Este pipeline executa regras pré-definidas de segurança. Para entender o que está acontecendo: p/security-audit e p/owasp-top-ten são packs de regras mantidas pela Semgrep Registry; generateSarif converte o resultado para formato SARIF, padrão aberto que GitHub Code Scanning entende nativamente.

Criando Regras Customizadas

Agora vem o poder real: criar suas próprias regras. Suponha que sua empresa proíbe uso de eval() em Python. Crie rules/custom-rules.yaml:

rules:
  - id: no-eval-allowed
    pattern: eval(...)
    message: "eval() é proibido por política de segurança. Use json.loads() ou ast.literal_eval()"
    languages: [python]
    severity: ERROR

  - id: hardcoded-database-password
    patterns:
      - pattern-either:
          - pattern: "password = '...'"
          - pattern: "password = \"...\""
    message: "Senha hardcoded detectada. Use variáveis de ambiente"
    languages: [python]
    severity: CRITICAL

Execute a regra customizada:

semgrep --config=rules/custom-rules.yaml .

Exemplo Prático: Detectando SQL Injection

Considere este código Python vulnerável (deliberadamente):

def get_user(user_id):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return database.execute(query)

Crie a regra rules/sql-injection.yaml:

rules:
  - id: sql-injection-f-string
    pattern-either:
      - pattern: $DB.execute(f"...{...}...")
      - pattern: $DB.execute("..." + str(...))
      - pattern: $DB.execute("..." % (...))
    message: |
      SQL Injection detectada. Use prepared statements com placeholders
      Correto: cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    languages: [python]
    severity: CRITICAL

Semgrep detectaria a vulnerabilidade imediatamente no pipeline.

SonarQube: Análise Profunda com Métricas de Qualidade

Arquitetura e Diferenças Fundamentais

SonarQube é uma plataforma enterprise de análise estática que combina SAST com métricas de qualidade (duplicação de código, cobertura de testes, complexidade ciclomática). Enquanto Semgrep é rápido e customizável, SonarQube oferece histórico, trends e integração com gestão de projetos.

SonarQube executa análise profunda em AST (Abstract Syntax Tree), permitindo detectar padrões complexos que Semgrep não alcança facilmente. É ideal para organizações grandes que precisam de compliance, relatórios executivos e controle centralizado de qualidade.

Instalação com Docker

A forma mais prática é via Docker:

docker run -d \
  --name sonarqube \
  -p 9000:9000 \
  -e SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonarqube \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=sonar_password \
  sonarqube:latest

Acesse http://localhost:9000 (usuário padrão: admin/admin).

Integração em Pipeline GitLab CI

Crie .gitlab-ci.yml:

stages:
  - scan
  - build

sonarqube_scan:
  stage: scan
  image: sonarsource/sonar-scanner-cli:latest
  script:
    - sonar-scanner \
        -Dsonar.projectKey=my-python-app \
        -Dsonar.sources=src \
        -Dsonar.host.url=http://sonarqube-server:9000 \
        -Dsonar.login=$SONAR_TOKEN \
        -Dsonar.python.coverage.reportPath=coverage.xml
  artifacts:
    reports:
      sast: sonarqube-report.json
  allow_failure: false

O -Dsonar.python.coverage.reportPath integra cobertura de testes; sem isso, SonarQube não consegue calcular a métrica de cobertura.

Exemplo: Detectando Code Smells e Vulnerabilidades

Considere esta classe Java com problemas:

public class UserService {
    private String dbPassword = "admin123"; // Hardcoded password

    public User getUser(int id) {
        String query = "SELECT * FROM users WHERE id = " + id; // SQL Injection
        return database.execute(query);
    }

    public void logUser(User user) {
        System.out.println("User: " + user); // Println in production code
    }

    public String processData(String input) {
        if (input != null) {
            if (input.length() > 0) {
                if (input.equals("admin")) { // Deep nesting
                    return "admin";
                }
            }
        }
        return "";
    }
}

SonarQube detectará:
- CRITICAL: Senha hardcoded
- CRITICAL: SQL Injection
- MAJOR: System.out.println em código de produção
- MAJOR: Complexidade ciclomática excessiva
- MINOR: Return statement redundante

Configuração de Quality Gates

Quality Gates definem quando um projeto "passa" ou "falha". Configure em SonarQube:

Condições:
- Segurança: 0 vulnerabilidades críticas
- Confiabilidade: Máximo 5 bugs
- Manutenibilidade: Nota >= C
- Cobertura: >= 80%
- Duplicação: <= 3%

No pipeline, a análise falha se alguma condição não for atendida:

script:
  - sonar-scanner [...]
  - curl -s "http://sonarqube:9000/api/qualitygates/project_status?projectKey=my-app" \
      | jq '.projectStatus.status' | grep -q "OK" || exit 1

CodeQL: Análise Semântica com Banco de Dados de Vulnerabilidades

Conceito: Consultas SQL em Código-Fonte

CodeQL, desenvolvido pela GitHub, transforma código-fonte em um banco de dados consultável via uma linguagem similar a SQL. Isso permite detectar vulnerabilidades através de consultas semânticas, não apenas pattern matching. Se Semgrep é "busca por regex evoluída" e SonarQube é "análise com métricas", CodeQL é "análise lógica programática".

Um exemplo: para detectar SQL Injection, você não apenas procura por f"SELECT...", mas rastreia o fluxo de dados desde a entrada do usuário até a execução SQL, detectando quando dados não-sanitizados chegam lá.

Instalação e Setup Inicial

Instale a CLI de CodeQL:

# macOS
brew install codeql

# Linux - download manual
wget https://github.com/github/codeql-cli-bundle/releases/download/v2.14.0/codeql-linux64.zip
unzip codeql-linux64.zip
export PATH=$PWD/codeql:$PATH

Verifique:

codeql --version

GitHub Actions com CodeQL

O jeito mais prático é usar GitHub Actions (CodeQL vem integrado ao GitHub):

name: CodeQL Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * 1'  # Segunda-feira às 2am

jobs:
  analyze:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        language: [python, javascript]

    steps:
      - uses: actions/checkout@v3

      - uses: github/codeql-action/init@v2
        with:
          languages: ${{ matrix.language }}
          queries: security-and-quality

      - uses: github/codeql-action/autobuild@v2

      - uses: github/codeql-action/analyze@v2
        with:
          category: "/language:${{ matrix.language }}"

O autobuild detecta automaticamente como compilar o projeto (para linguagens compiladas como C/C++/Java). Para linguagens interpretadas, esse passo é desnecessário.

Criando Consultas Customizadas CodeQL

CodeQL permite criar queries customizadas em sua linguagem própria (QL). Crie queries/custom-command-injection.ql:

import javascript

from CallExpression call, Identifier func
where
  func.getName() = "exec" and
  call.getCallee() = func and
  call.getArgument(0).getType() = StringType
select
  call,
  "Possível command injection: 'exec' com string. Use array com argumentos separados"

Execute contra sua codebase:

codeql database create /tmp/codeql-db --language=javascript --source-root=.
codeql query run queries/custom-command-injection.ql --database=/tmp/codeql-db

Exemplo Prático: Detectando XSS em Node.js/Express

Considere este código vulnerável:

const express = require('express');
const app = express();

app.get('/profile/:name', (req, res) => {
    const name = req.params.name;
    res.send(`<h1>Welcome ${name}</h1>`); // XSS vulnerability
});

app.post('/comment', (req, res) => {
    const comment = req.body.text;
    database.save(`<p>${comment}</p>`); // XSS stored
    res.json({ success: true });
});

CodeQL detectaria isso com a query padrão js/xss. Para criar uma regra customizada que detecta especificamente template strings inseguras:

import javascript

from TemplateLiteral template, DataFlowNode source
where
  source.asExpr() = template.getElement(_) and
  source.getVariable().getName().matches("req\\..*")
select
  template,
  "Template string com user input não sanitizado: " + source.getVariable().getName()

CodeQL rastreia que req.params.name ou req.body.text (entrada do usuário) flui diretamente para HTML, sem sanitização.

Diferença Chave: Data Flow Analysis

CodeQL excele em análise de fluxo de dados. Ela entende isso:

# CodeQL rastreia o fluxo:
user_input = request.args.get('id')        # Source: entrada do usuário
processed = f"SELECT * FROM users WHERE id = {user_input}"  # Sink: SQL execution
database.execute(processed)

# Mesmo que o código esteja em arquivos diferentes,
# CodeQL descobre que user_input chegou em database.execute()

Semgrep encontraria o padrão de f-string, mas CodeQL prova que existe um caminho de dados inseguro.

Integrando as Três Ferramentas em um Pipeline Real

Estratégia: Layers de Análise

Cada ferramenta tem seu propósito:

  1. Semgrep (rápido, primária): Executa em ~30s, bloqueia problemas óbvios
  2. SonarQube (completo, gateway): Análise profunda + métricas, bloqueia se Quality Gate falhar
  3. CodeQL (semântico, backup): Detecta vulnerabilidades complexas de fluxo

O pipeline completo em GitHub Actions:

name: Complete Security Pipeline

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      # Layer 1: Semgrep (rápido)
      - name: Semgrep Fast Scan
        uses: returntocorp/semgrep-action@v1
        with:
          config: p/owasp-top-ten
          generateSarif: true
        continue-on-error: true

      # Layer 2: CodeQL (profundo)
      - uses: github/codeql-action/init@v2
        with:
          languages: [python, javascript]

      - uses: github/codeql-action/autobuild@v2

      - uses: github/codeql-action/analyze@v2

      # Layer 3: SonarQube (métricas)
      - name: SonarQube Scan
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: |
          docker run \
            --rm \
            -e SONAR_HOST_URL=https://sonarqube.company.com \
            -e SONAR_LOGIN=$SONAR_TOKEN \
            -v $(pwd):/src \
            sonarsource/sonar-scanner-cli \
            -Dsonar.sources=/src \
            -Dsonar.projectKey=my-project

      # Upload all results
      - uses: github/codeql-action/upload-sarif@v2
        if: always()
        with:
          sarif_file: semgrep.sarif
          category: semgrep

Configuração de Merge Checks

Configure GitHub para exigir que todos os scans passem:

Settings > Branches > Branch protection rule:
- Require CodeQL checks to pass ✓
- Require Semgrep checks to pass ✓
- Require status checks to pass before merging ✓

Agora, nenhum pull request pode ser merged sem passar em TODAS as ferramentas.

Exemplo: Projeto Python Real

Suponha um projeto Flask com múltiplos problemas:

# app.py
from flask import Flask, request
import sqlite3

app = Flask(__name__)
DB_PASSWORD = "root123"  # Problema 1: senha hardcoded

@app.route('/search')
def search():
    query = request.args.get('q')
    # Problema 2: SQL injection
    results = sqlite3.execute(f"SELECT * FROM items WHERE name = '{query}'")
    return results

@app.route('/eval-code', methods=['POST'])
def eval_endpoint():
    code = request.json.get('code')
    # Problema 3: eval() remoto
    result = eval(code)
    return str(result)

Semgrep detecta em ~30s:

Arquivo: app.py
- Hardcoded password
- eval() usage (regra customizada)

CodeQL detecta em ~2min:

Arquivo: app.py
- SQL injection (rastreia: request.args.get → f-string → sqlite3.execute)
- Remote code execution via eval()

SonarQube detecta em ~3min:

- Hardcoded credentials (CRITICAL)
- Code injection (CRITICAL)
- Missing input validation (MAJOR)
- Complexity issues
- Sem testes unitários (coverage 0%)

Quality Gate falha porque segurança crítica não passou. PR é bloqueado.

Conclusão

Dominar SAST em pipelines CI exige entender que cada ferramenta resolve um problema diferente. Semgrep é velocidade e customização; SonarQube é visibilidade e compliance; CodeQL é precisão semântica. A combinação das três cria uma defesa em camadas que pega desde problemas óbvios até vulnerabilidades sutis de fluxo de dados.

O principal aprendizado prático é que segurança integrada no CI é mais eficaz e barata que segurança testada depois. Um bug de segurança detectado no pull request custa horas para o developer; o mesmo bug encontrado em produção custa horas para o SRE, danos à reputação e potencial brecha de dados.

Por fim, lembre-se que ferramentas não substituem design seguro. SAST é um detector de problemas conhecidos; um desenvolvedor com mentalidade security-first ainda é o melhor preventivo.

Referências


Artigos relacionados