Segurança no CI: scan de imagens Docker e dependências

    Cada imagem Docker que você faz deploy pode conter vulnerabilidades conhecidas. Integrar scanners de segurança no pipeline de CI detecta CVEs críticos antes que cheguem à produção — sem custo adicional, usando ferramentas open-source que rodam diretamente no GitHub Actions.

    Trivy: scanner de imagens Docker no CI

    Detectar CVEs em imagens Docker com Trivy no GitHub Actions:

    yaml
    # .github/workflows/security.yml
    name: Security Scan
    
    on:
      push:
        branches: [main]
      pull_request:
        branches: [main]
      schedule:
        - cron: '0 6 * * 1'  # scan semanal às 6h toda segunda
    
    jobs:
      scan-docker:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
    
          - name: Build imagem para scan
            run: docker build -t scan-target:latest .
    
          - name: Scan com Trivy
            uses: aquasecurity/trivy-action@master
            with:
              image-ref: scan-target:latest
              format: 'sarif'        # formato para GitHub Security tab
              output: trivy-results.sarif
              severity: 'CRITICAL,HIGH'
              exit-code: '1'         # falhar CI se encontrar CRITICAL ou HIGH
    
          - name: Upload resultados para GitHub Security
            uses: github/codeql-action/upload-sarif@v3
            if: always()
            with:
              sarif_file: trivy-results.sarif
    
          # Relatório no PR:
          - name: Scan com saída em tabela (para comentário)
            if: github.event_name == 'pull_request'
            uses: aquasecurity/trivy-action@master
            with:
              image-ref: scan-target:latest
              format: 'table'
              severity: 'CRITICAL,HIGH,MEDIUM'
              exit-code: '0'  # não falhar neste step (já falhou acima)

    Scan de dependências npm com npm audit

    Detectar vulnerabilidades em dependências antes do deploy:

    yaml
    # .github/workflows/security.yml — adicionar job:
      scan-npm:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with:
              node-version: '22'
    
          - run: npm ci
    
          # npm audit nativo — falhar para vulnerabilidades críticas:
          - name: npm audit
            run: npm audit --audit-level=high
            # --audit-level: low, moderate, high, critical
    
          # Relatório detalhado:
          - name: npm audit report
            if: failure()
            run: npm audit --json | jq '.vulnerabilities | keys[] as $k | .[$k] | select(.severity == "high" or .severity == "critical") | {name: $k, severity: .severity, via: .via[0]}'
    
          # Para ignorar vulnerabilidades conhecidas sem correção:
          # npm audit --audit-level=high --production
          # (ignora devDependencies)
    
          # Alternativa mais rica: audit-ci
          # npm install -g audit-ci
          - name: audit-ci (com allowlist)
            run: |
              npx audit-ci --high \
                --allowlist GHSA-xxxx-yyyy-zzzz  # ignorar CVE específico com justificativa

    Dependabot: atualizações automáticas de segurança

    Configurar Dependabot para criar PRs de atualização automáticos:

    yaml
    # .github/dependabot.yml — na raiz do repositório:
    version: 2
    updates:
      # Dependências npm:
      - package-ecosystem: "npm"
        directory: "/"
        schedule:
          interval: "weekly"
          day: "monday"
          time: "06:00"
          timezone: "America/Sao_Paulo"
        # Criar PRs separados para cada dependência (não agrupar):
        open-pull-requests-limit: 10
        # Reviewer automático:
        reviewers:
          - "meu-usuario"
        # Labels para fácil filtragem:
        labels:
          - "dependabot"
          - "security"
        # Agrupar atualizações de patch (menos PRs):
        groups:
          patch-updates:
            update-types:
              - "patch"
    
      # Dependências do GitHub Actions:
      - package-ecosystem: "github-actions"
        directory: "/.github/workflows"
        schedule:
          interval: "weekly"
        labels:
          - "ci"
    
      # Dockerfile base images:
      - package-ecosystem: "docker"
        directory: "/"
        schedule:
          interval: "weekly"

    Scan de secrets no código com Gitleaks

    Detectar senhas e tokens acidentalmente commitados:

    yaml
    # .github/workflows/security.yml — scan de secrets:
      scan-secrets:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0  # histórico completo para escanear todos os commits
    
          - name: Scan de secrets com Gitleaks
            uses: gitleaks/gitleaks-action@v2
            env:
              GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}  # gratuito para repositórios públicos
    
    # .gitleaks.toml — configuração de regras customizadas:
    title = "Gitleaks Config"
    
    [extend]
    useDefault = true
    
    [[rules]]
    description = "Variável customizada de produção"
    id = "prod-db-url"
    regex = '''DATABASE_URL=postgresql://[^s]+'''
    tags = ["secret", "database"]
    
    [[allowlist]]
    description = "Ignorar arquivos de teste"
    paths = ['''test/.*''', '''.test.ts
    #x27;''] [[allowlist]] description = "Ignorar exemplos de código" regexes = ['''exemplo|placeholder|your-''']

    Policy as code com OPA/Conftest

    Validar que o Dockerfile segue políticas de segurança:

    rego
    # policy/docker.rego — políticas de segurança para Dockerfiles:
    package main
    
    # Proibir rodar como root:
    deny[msg] {
        input[i].Cmd == "cmd"
        # Sem USER definido = root por padrão:
        not any([c | c := input[_]; c.Cmd == "user"])
        msg := "Dockerfile não define USER não-root"
    }
    
    # Exigir HEALTHCHECK:
    deny[msg] {
        not any([c | c := input[_]; c.Cmd == "healthcheck"])
        msg := "Dockerfile não tem HEALTHCHECK definido"
    }
    
    # Exigir versão pinada na imagem base (não :latest):
    deny[msg] {
        input[i].Cmd == "from"
        image := input[i].Value[0]
        endswith(image, ":latest")
        msg := sprintf("Imagem base usa :latest: %s", [image])
    }
    
    # .github/workflows/security.yml — validar com Conftest:
      check-dockerfile:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Instalar Conftest
            run: |
              curl -L https://github.com/open-policy-agent/conftest/releases/download/v0.46.0/conftest_0.46.0_Linux_x86_64.tar.gz | tar xz
              mv conftest /usr/local/bin/
          - name: Validar Dockerfile
            run: conftest test Dockerfile --policy policy/
    Dica
    Conftest e OPA são ferramentas avançadas. Para começar: use apenas Trivy e Dependabot — já cobrem 80% dos riscos mais comuns com zero configuração.

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o VPS para Docker em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes