1

CI/CD Seguro: Dependabot, SAST e DAST no GitHub

Introdução#

Vulnerabilidades não aparecem apenas em infraestrutura. Elas vêm de código inseguro, dependências desatualizadas e containers mal configurados. Enquanto você defende contra DDoS, aplicações vulneráveis podem ser exploradas diretamente por atacantes.

Neste artigo, você aprenderá a implementar 5 camadas de segurança automática em seu pipeline CI/CD usando ferramentas gratuitas do GitHub e serviços open source. Cada uma detecta um tipo diferente de risco: dependências vulneráveis, código inseguro, falhas lógicas e imagens de container comprometidas.

O melhor: tudo funciona sem custos adicionais se você usa GitHub Public ou temos opções gratuitas para repos privados.

⚠️ Atenção: Estas ferramentas detectam e alertam sobre problemas, mas não corrigem tudo automaticamente. Você ainda precisa revisar e aprovar cada correção. Segurança é um processo contínuo, não um checkbox.

Pré-requisitos#

Para implementar as medidas deste artigo, você precisa de:

  • Repositório no GitHub (público ou privado)
  • GitHub Actions habilitado (padrão em repos novos)
  • Conhecimento básico de YAML (workflows GitHub Actions)
  • Aplicação em Docker ou código C#/.NET (exemplos são específicos, mas conceitos aplicam a qualquer linguagem)
  • Conta SonarCloud (gratuita para repos públicos e privados)
    Repos privados com plano GitHub Pro/Team têm acesso a algumas ferramentas gratuitas de segurança. Repos públicos têm tudo gratuito.

1. Dependabot: Detectar Dependências Vulneráveis#

Dependabot é a primeira linha de defesa. Ele verifica automaticamente suas dependências contra bancos de dados de vulnerabilidades conhecidas (CVE) e abre pull requests com atualizações de segurança.

Exemplo real: Um pacote que você usa há 6 meses descobre uma falha crítica. Dependabot abre um PR automaticamente dentro de horas. Você revisa, testa e merge.

Ativar Dependabot no GitHub#

  • Acesse SettingsCode security and analysis
  • Ative Dependabot alerts e Dependabot security updates
  • Pronto — Dependabot já está monitorando

Configuração Avançada: dependabot.yml#

Para controlar frequência de atualizações e agrupamento de PRs, crie .github/dependabot.yml:

version: 2
updates:
  # Dependências do .NET (NuGet)
  - package-ecosystem: "nuget"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "03:00"
    allow:
      - dependency-type: "all"
    pull-request-branch-name:
      separator: "/"
    reviewers:
      - "seu-usuario"
    assignees:
      - "seu-usuario"
    open-pull-requests-limit: 10  # Máximo 10 PRs abertos ao mesmo tempo

  # Docker images
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "04:00"
    open-pull-requests-limit: 5

  # GitHub Actions
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "05:00"

  # Python (se aplicável)
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "03:00"
    allow:
      - dependency-type: "direct"  # Apenas dependências diretas, não transitivas

Automação: Auto-merge de Updates Seguras#

  • Conta SonarCloud (gratuita para repos públicos e privados)
    Se você quer que Dependabot merge automaticamente certas atualizações (patch versions, por exemplo):
name: Auto-merge Dependabot updates

on: pull_request

jobs:
  auto-merge:
    if: github.actor == 'dependabot[bot]'
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: write
    steps:
      - uses: actions/checkout@v4

      - name: Enable auto-merge para patches de segurança
        run: |
          gh pr merge --auto --squash "${{ github.event.pull_request.number }}"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Apenas merge automaticamente se o update é MINOR ou PATCH, não MAJOR

ℹ️ Informação: Dependabot detecta vulnerabilidades em tempo real. Em 2024, 42% de todos os PRs de segurança são abertos por ferramentas automáticas. Sem automação, você fica para trás.

2. GitHub Code Scanning: Análise Estática com CodeQL#

GitHub Code Scanning usa CodeQL (linguagem de query estática) para encontrar bugs de segurança e code smells em seu código. Funciona em repositórios públicos e privados (com GitHub Pro).

O que detecta: SQL injection, XSS, command injection, use-after-free, variáveis não inicializadas, e 300+ outros padrões inseguros.

Ativar Code Scanning#

  • Acesse SettingsCode security and analysis
  • Ative GitHub Advanced Security (se disponível na sua conta)
  • Code Scanning é ativado automaticamente

Workflow Automático com CodeQL#

GitHub gera automaticamente um workflow .github/workflows/github-code-scanning.yml. Customize se necessário:

name: CodeQL Analysis

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]
  schedule:
    - cron: '0 3 * * 0'  # Toda segunda-feira às 03:00 UTC

jobs:
  analyze:
    name: CodeQL Analyze
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'csharp', 'python', 'javascript' ]  # Ajuste conforme seu projeto

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v2
        with:
          languages: ${{ matrix.language }}
          queries: security-extended  # Usar queries de segurança estendidas

      - name: Autobuild (C#)
        if: matrix.language == 'csharp'
        run: |
          dotnet build --configuration Release

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

Interpretar Resultados#

Resultados aparecem em SecurityCode scanning alerts:

  • Critical: Falhas que afetam segurança (SQL injection, RCE)
  • High: Bugs que podem ser explorados
  • Medium/Low: Práticas ruins ou technical debt

Desabilitar Alertas de Forma Segura#

Nem todo alerta é actionable. Se você confirma que um alerta é false positive:

# No código comentado:
// lgtm [cs/sql-injection]  validamos o input antes de usar em query
var query = $"SELECT * FROM users WHERE id = {userId}";

📝 Exemplo: CodeQL encontrou um SQL injection potencial em um relatório. Resultado: descobriu que você estava concatenando IDs de usuário sem validação. Depois de adicionar parametrização, o alerta desapareceu.

3. SonarCloud: Análise SAST Profunda e Code Quality#

SonarCloud vai além de segurança — mede qualidade de código, cobertura de testes, duplicação e code smells. É gratuito para repos públicos e privados (com limitações).

O que detecta além de segurança: bugs, code smells, code coverage, technical debt, falhas de design.

Setup: Conectar GitHub a SonarCloud#

  • Acesse SonarCloud.io
  • Faça login com sua conta GitHub
  • Selecione os repositórios a analisar
  • Gere um token em My AccountSecurityGenerate Tokens

Workflow com SonarCloud#

Adicione .github/workflows/sonarcloud.yml:

name: SonarCloud Analysis

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

jobs:
  sonarcloud:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Necessário para análise de qualidade

      - name: Set up .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Restore NuGet packages
        run: dotnet restore

      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}  # Gere em SonarCloud

      - name: Upload coverage to Codecov (opcional)
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage.xml

Arquivo sonar-project.properties#

Crie na raiz do seu projeto:

sonar.projectKey=seu-usuario_seu-projeto
sonar.organization=seu-usuario

# C#
sonar.sources=src
sonar.exclusions=**/bin/**,**/obj/**,**/tests/**
sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml

# Testes
sonar.tests=tests
sonar.test.inclusions=**/Tests/**

# Code Coverage
sonar.coverage.exclusions=**/Tests/**

Badges de Qualidade#

Adicione ao README para mostrar status:

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=seu-usuario_seu-projeto&metric=alert_status)](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)
[![Code Coverage](https://sonarcloud.io/api/project_badges/measure?project=seu-usuario_seu-projeto&metric=coverage)](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=seu-usuario_seu-projeto&metric=security_rating)](https://sonarcloud.io/dashboard?id=seu-usuario_seu-projeto)

ℹ️ Informação: SonarCloud analisa Pull Requests automaticamente e coloca comentários no código com sugestões. Desenvolvedores recebem feedback em tempo real, não no final do sprint.

4. OWASP ZAP: Testes Dinâmicos de Segurança (DAST)#

OWASP ZAP (Zed Attack Proxy) é um teste dinâmico — ele acessa sua aplicação como um atacante e procura vulnerabilidades em tempo de execução.

Diferença SAST vs DAST:

  • SAST (Code Scanning, SonarCloud): analisa código-fonte sem executar
  • DAST (ZAP): executa a aplicação e ataca como um hacker real

Workflow com OWASP ZAP#

Adicione .github/workflows/owasp-zap.yml:

name: OWASP ZAP Scan

on:
  push:
    branches: [ main, develop ]
  schedule:
    - cron: '0 2 * * 0'  # Domingo às 2 AM

jobs:
  zap-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t seu-app:latest .

      - name: Start application
        run: |
          docker run -d --name app -p 8080:80 seu-app:latest
          sleep 10  # Aguarde app iniciar

      - name: Run OWASP ZAP scan
        uses: zaproxy/[email protected]
        with:
          target: 'http://localhost:8080'
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a'  # Análise completa
          issue_title: 'OWASP ZAP: Vulnerabilidades encontradas'

      - name: Upload ZAP report
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: zap-report
          path: report_html.html

      - name: Comment on PR (se DAST falhar)
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚨 OWASP ZAP encontrou vulnerabilidades. Veja o relatório em Artifacts.'
            })

      - name: Cleanup
        if: always()
        run: docker stop app && docker rm app

Configuração de Regras (.zap/rules.tsv)#

Customize quais vulnerabilidades ZAP deve blocar:

# rule_id  alert_title  threshold  enabled
10000  Information  IGNORE  true
10001  Informational  IGNORE  true
40014  Cross Site Request Forgery  HIGH  true
40016  Cross Site Scripting (Reflected)  MEDIUM  true
40017  Cross Site Scripting (Stored)  HIGH  true
90024  SQL Injection  CRITICAL  true
90025  LDAP Injection  HIGH  true

Rodando ZAP Localmente para Teste#

Antes de colocar em CI/CD, teste localmente:

# Instalar ZAP
docker pull owasp/zap2docker-stable

# Executar baseline scan
docker run -t owasp/zap2docker-stable zap-baseline.py \
  -t http://seu-app.local:80 \
  -r report.html

⚠️ Atenção: DAST só funciona se sua aplicação está rodando. Certifique-se de que o Docker build e start sejam rápidos. Scans podem levar 5-10 minutos.

5. Trivy: Scanning de Container e Dependências#

Trivy verifica imagens Docker e arquivos de dependência em busca de vulnerabilidades conhecidas. É rápido (segundos), agnóstico a linguagem e integra bem em CI/CD.

O que escaneia: imagens Docker, arquivos requirements.txt, package.json, *.csproj, etc.

Workflow com Trivy#

Adicione .github/workflows/trivy.yml:

name: Trivy Vulnerability Scan

on:
  push:
    branches: [ main, develop ]
    paths: [ 'Dockerfile', '*.csproj', 'requirements.txt', 'package.json' ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 1 * * 0'  # Domingo à 1 AM

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy vulnerability scan (filesystem)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'  # Filesystem scan
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'HIGH,CRITICAL'

      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Build Docker image (se houver Dockerfile)
        if: hashFiles('Dockerfile') != ''
        run: docker build -t seu-app:${{ github.sha }} .

      - name: Scan Docker image with Trivy
        if: hashFiles('Dockerfile') != ''
        uses: aquasecurity/trivy-action@master
        with:
          input: 'seu-app:${{ github.sha }}'
          format: 'table'
          exit-code: '1'  # Fail if vulnerabilities found
          severity: 'CRITICAL'  # Apenas critical, não warning

Executar Trivy Localmente#

# Instalar Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# Escanear arquivo local
trivy fs .

# Escanear imagem Docker
trivy image seu-app:latest

# Apenas vulnerabilidades críticas
trivy image --severity CRITICAL seu-app:latest

Interpretando Saída Trivy#

vuln-id       severity  package
CVE-2024-1234 CRITICAL  openssl/1.0.2
CVE-2024-5678 HIGH      curl/7.32.0

Cada CVE tem um link para detalhes. Se for crítico, você deve atualizar o pacote imediatamente.

📝 Exemplo: Trivy encontrou CVE-2024-1234 em uma imagem base Ubuntu. Você atualiza a imagem, reconstrói e o scan passa. Tudo em menos de 1 minuto.

6. Exemplo Prático: Workflow Completo em CI/CD#

Aqui está um workflow que roda todas as 5 camadas de segurança em cada push:

name: Segurança Completa

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

jobs:
  # 1. Dependabot já roda automaticamente (integrado no GitHub)
  
  # 2. CodeQL
  codeql:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v2
        with:
          languages: csharp
      - run: dotnet build --configuration Release
      - uses: github/codeql-action/analyze@v2

  # 3. SonarCloud
  sonarcloud:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
      - run: dotnet restore
      - uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

  # 4. Trivy (filesystem + Docker image)
  trivy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          format: 'sarif'
          output: 'trivy-results.sarif'
      - uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  # 5. OWASP ZAP (apenas em main)
  dast:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t seu-app:latest .
      - run: docker run -d --name app -p 8080:80 seu-app:latest && sleep 10
      - uses: zaproxy/[email protected]
        with:
          target: 'http://localhost:8080'
          rules_file_name: '.zap/rules.tsv'
      - if: always()
        run: docker stop app

Tempo Total de Execução#

  • CodeQL: ~3 minutos
  • SonarCloud: ~4 minutos
  • Trivy: ~30 segundos
  • OWASP ZAP: ~8 minutos (apenas em main)
    Total: ~10-15 minutos por push (sem paralelização). Com paralelização no GitHub Actions, rodam em paralelo.

📖 Artigo completo com exemplos de código: CI/CD Seguro: Dependabot, SAST e DAST no GitHub

Carregando publicação patrocinada...