Como integrar análise de código estático em pipelines de CI/CD?

Como integrar análise de código estático em pipelines de CI/CD?

IN-COM 11 de Junho de 2026

Cada linha de código que chega à produção foi escrita por alguém que acreditava estar correta. A análise estática de código é o sistema automatizado que verifica se essa crença estava correta, não executando o código, mas lendo sua estrutura, rastreando seus fluxos de dados e comparando-o com padrões conhecidos de vulnerabilidades, bugs e violações de qualidade. A questão não é se devemos executá-la, mas em que ponto do pipeline executá-la, qual ferramenta usar, quais limites impor e como manter o feedback rápido o suficiente para que os desenvolvedores realmente ajam de acordo com ele, em vez de ignorá-lo.

A diferença entre a análise estática como uma mera formalidade e a análise estática como um verdadeiro mecanismo de controle de qualidade reside na configuração. Um scanner que executa e gera um relatório que ninguém lê é mera encenação. Um mecanismo de controle de qualidade que reprova uma compilação quando novas vulnerabilidades críticas são introduzidas, bloqueia uma mesclagem quando a cobertura cai abaixo do limite e exibe feedback preciso sobre arquivos e linhas na interface de revisão de pull requests é um sistema que altera seu comportamento no momento em que as decisões são tomadas.

Em que etapa do pipeline devo executar a análise estática?

A análise estática deve ser executada em vários pontos, cada um com uma finalidade diferente. Executá-la em apenas um ponto cria pontos cegos; executá-la em todos os pontos igualmente cria fluxos de trabalho lentos que os desenvolvedores precisam contornar.

Estágio do PipelineO que correrOrçamento de LatênciaPropósito
Pré-compromisso (local)Apenas linters rápidos (ESLint, Clippy, Rustfmt)Menos de 5 segundosImpeça que problemas óbvios cheguem ao repositório.
Solicitação de pull request / solicitação de merge requestAnálise completa de linting + SAST + varredura incremental do SonarQubeMenos de 3 minutosFusões de blocos em novas questões críticas
Construção do branch principalAnálise completa, incluindo cobertura, duplicação e dívida técnica.Menos de 10 minutosAcompanhe as tendências de qualidade, atualize os painéis de controle.
Programado todas as noitesAnálises detalhadas, auditoria de dependências, verificações de conformidade.Sem orçamentoIdentificar problemas de produção lenta e riscos na cadeia de suprimentos

A etapa mais valiosa é a do pull request . A análise realizada no push para a branch principal ocorre depois que a decisão de mesclar já foi tomada. A análise realizada no pull request, com a publicação de comentários inline contendo o contexto preciso do arquivo e da linha, ocorre quando o desenvolvedor ainda está trabalhando no código e o custo de correção é o menor possível.

Executar análises pesadas antes de cada commit, a cada salvamento, prejudica a experiência do desenvolvedor. Linters rápidos, que concluem em menos de 5 segundos, devem ser feitos lá. Todo o resto deve ser feito em CI (Integração Contínua).

GitHub Actions: Configuração Completa e Funcional

O GitHub Actions é a plataforma de CI mais utilizada. O fluxo de trabalho a seguir executa um pipeline de qualidade em camadas: verificação de formatação, linting, compilação, verificação de segurança SAST e controle de qualidade SonarCloud.

yaml

# .github/workflows/quality.yml
name: Code Quality

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

jobs:
  # ── Layer 1: Fast checks (under 60 seconds) ─────────────────────────────
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Lint (fail on warnings)
        run: npx eslint src/ --max-warnings 0

      - name: Format check
        run: npx prettier --check src/

      - name: TypeScript type check
        run: npx tsc --noEmit

  # ── Layer 2: Security SAST (Semgrep) ────────────────────────────────────
  sast:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Semgrep SAST scan
        uses: semgrep/semgrep-action@v1
        with:
          config: p/javascript p/nodejs p/owasp-top-ten
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

  # ── Layer 3: SonarCloud quality gate ────────────────────────────────────
  sonar:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history required for blame data

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci

      - name: Run tests with coverage
        run: npm test -- --coverage --coverageReporters=lcov

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

Propriedades

# sonar-project.properties
sonar.projectKey=my-org_my-project
sonar.organization=my-org
sonar.sources=src
sonar.tests=tests
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=**/*.test.js,**/*.spec.js

Principais decisões de configuração neste fluxo de trabalho:

  • --max-warnings 0 No ESLint: qualquer aviso é considerado uma falha na compilação. Equipes que permitem avisos os acumulam até que ninguém os leia.
  • fetch-depth: 0 Ao finalizar a compra: o SonarCloud precisa do histórico completo do Git para atribuir a responsabilidade aos problemas introduzidos e calcular corretamente as métricas de código novo.
  • O Semgrep é executado em paralelo com o Sonar após a verificação de lint: a verificação de segurança e a verificação de qualidade são preocupações independentes; executá-las em paralelo reduz o tempo total do pipeline.
  • Os testes são executados com lcov Saída de cobertura: o SonarCloud lê isso para mostrar a cobertura por arquivo e aplicar limites de cobertura no controle de qualidade.

GitLab CI/CD: Pipeline de Qualidade com Portões de Qualidade

yaml

# .gitlab-ci.yml
stages:
  - lint
  - test
  - analyze
  - security

variables:
  SONAR_HOST_URL: "https://sonarcloud.io"

lint:
  stage: lint
  image: node:20
  cache:
    paths: [node_modules/]
  script:
    - npm ci
    - npx eslint src/ --max-warnings 0
    - npx prettier --check src/
    - npx tsc --noEmit
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm test -- --coverage --coverageReporters=lcov cobertura
  coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

sonarcloud:
  stage: analyze
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"
  cache:
    key: "${CI_JOB_NAME}"
    paths: [.sonar/cache]
  script:
    - sonar-scanner
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

semgrep:
  stage: security
  image: returntocorp/semgrep:latest
  script:
    - semgrep scan --config=p/owasp-top-ten --config=p/javascript
      --sarif --output=semgrep.sarif src/
  artifacts:
    reports:
      sast: semgrep.sarif
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == "main"

O artifacts: reports: sast: O bloco integra a saída SARIF do Semgrep diretamente no Painel de Segurança do GitLab, onde as descobertas aparecem no relatório de segurança da solicitação de merge, em vez de como saída bruta do log de CI.

Jenkins: Pipeline Declarativo com SonarQube

Groovy

// Jenkinsfile
pipeline {
    agent any

    tools {
        nodejs "NodeJS-20"
    }

    environment {
        SONAR_TOKEN = credentials('sonar-token')
    }

    stages {
        stage('Lint') {
            steps {
                sh 'npm ci'
                sh 'npx eslint src/ --max-warnings 0'
                sh 'npx tsc --noEmit'
            }
        }

        stage('Test') {
            steps {
                sh 'npm test -- --coverage --coverageReporters=lcov'
            }
            post {
                always {
                    publishHTML([
                        allowMissing: false,
                        reportDir: 'coverage/lcov-report',
                        reportFiles: 'index.html',
                        reportName: 'Coverage Report'
                    ])
                }
            }
        }

        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('SonarQube') {
                    sh '''
                        npx sonar-scanner \
                          -Dsonar.projectKey=my-project \
                          -Dsonar.sources=src \
                          -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info
                    '''
                }
            }
        }

        stage('Quality Gate') {
            steps {
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
    }

    post {
        failure {
            emailext(
                subject: "Quality Gate FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                body: "Build failed quality gate. Review: ${env.BUILD_URL}",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
    }
}

waitForQualityGate abortPipeline: true é a linha crítica. Ela consulta o SonarQube até que a análise seja concluída e interrompe a compilação se o critério de qualidade não for atendido. Sem isso, o pipeline é concluído enquanto a análise ainda está em andamento e o resultado do critério de qualidade nunca é aplicado.

Configurando Portões de Qualidade que as Equipes Realmente Respeitam

Um controle de qualidade que falha em todas as confirmações de commit porque os limites estão definidos de forma muito rigorosa é desativado. Um controle que nunca falha porque os limites são muito permissivos não tem utilidade. O objetivo é um controle calibrado de acordo com o perfil de risco real do projeto.

Configuração inicial recomendada para um novo controle de qualidade do SonarQube:

métricoCondiçãoLimite
Novos bugsMelhor que0
Novas vulnerabilidadesMelhor que0
Novos pontos críticos de segurança analisadosMenor que100%
Nova cobertura de códigoMenor que80%
Novas linhas duplicadasMelhor que3%
Novo código cheira malMelhor que10

Aplique esses limites apenas ao código novo (o período de "código novo" no SonarQube). Não os aplique a toda a base de código de um projeto legado, pois falhas em todas as compilações devido ao acúmulo de dívida técnica ao longo de cinco anos podem levar as equipes a desativar completamente o recurso. A abordagem de código novo permite estancar a sangria enquanto a dívida existente é tratada separadamente.

Ajuste os limites gradualmente ao longo do tempo. Comece com limites conservadores e, em seguida, torne-os mais rigorosos a cada trimestre, à medida que a equipe reduz o acúmulo de tarefas. Um critério que hoje atinge 75% de cobertura pode ser atualizado para 80% assim que a taxa de aprovação estiver confortavelmente acima desse valor. O ajuste incremental é mais sustentável do que buscar a perfeição a qualquer custo.

Gerenciamento do desempenho da análise estática em bases de código extensas

O motivo mais comum pelo qual as equipes desativam ou contornam a análise estática na CI é que ela torna os pipelines muito lentos. Uma verificação de qualidade de 15 minutos a cada push para um branch de funcionalidade interrompe o fluxo de trabalho do desenvolvedor. Existem diversas estratégias para manter a análise rápida:

Análise incremental (somente código novo). O SonarQube e a maioria das ferramentas de análise estática corporativas suportam a análise apenas dos arquivos alterados em uma solicitação pull, em vez de todo o código-fonte. Configure sonar.pullrequest.base, sonar.pullrequest.branch e sonar.pullrequest.key Parâmetros para habilitar a verificação incremental específica de PRs. A análise completa ainda é executada na mesclagem com a branch principal; a verificação de PRs leva menos de 2 minutos na maioria das bases de código.

Armazenamento em cache. A parte mais custosa de muitas análises estáticas é o download da ferramenta e suas definições de regras. Armazene em cache o binário do scanner, o banco de dados de regras e quaisquer dependências resolvidas entre as execuções:

yaml

# GitHub Actions: cache SonarCloud scanner
- name: Cache SonarCloud packages
  uses: actions/cache@v4
  with:
    path: ~/.sonar/cache
    key: ${{ runner.os }}-sonar
    restore-keys: ${{ runner.os }}-sonar

Paralelismo. A verificação de código (linting), a verificação de segurança e a análise de qualidade são tarefas independentes. Execute-as em paralelo, em vez de sequencialmente. O tempo total do pipeline é determinado pela tarefa mais lenta, não pela soma de todas as tarefas.

Ativação seletiva de ferramentas. Nem todas as ferramentas precisam ser executadas em todas as branches. A verificação completa de segurança e conformidade pode ser executada em pull requests e na branch principal, enquanto as branches de pré-merge executam apenas linters rápidos. Use filtros de branch na configuração de CI para aplicar diferentes perfis de análise.

Análise Estática em Pipelines com Segurança em Primeiro Lugar: SAST e Varredura de Dependências

Os pipelines focados em segurança adicionam duas camadas além da análise de qualidade padrão: SAST (Static Application Security Testing) para vulnerabilidades no código do aplicativo e verificação de dependências para CVEs conhecidas em bibliotecas de terceiros.

As ferramentas SAST (Semgrep, CodeQL, Snyk Code, Checkmarx) analisam como os dados não confiáveis ​​fluem pelo código do aplicativo para alcançar operações sensíveis. Elas encontram injeção de SQL, XSS, injeção de comandos, desserialização insegura e credenciais embutidas no código que os linters de qualidade padrão não conseguem detectar.

yaml

# GitHub Actions: CodeQL analysis for deep vulnerability detection
- name: Initialize CodeQL
  uses: github/codeql-action/init@v3
  with:
    languages: javascript-typescript

- name: Perform CodeQL Analysis
  uses: github/codeql-action/analyze@v3
  with:
    category: "/language:javascript-typescript"

Varredura de dependência cheques package.json, pom.xml, requirements.txte arquivos de manifesto equivalentes em relação a bancos de dados de vulnerabilidades:

yaml

# Dependency audit in the pipeline
- name: Dependency audit
  run: |
    npm audit --audit-level=high
    # Fail if high or critical vulnerabilities are found

O modelo de segurança em camadas aplica uma análise de segurança de domínio (SAST) rápida e baseada em padrões (Semgrep) em cada solicitação de pull request para feedback imediato dos desenvolvedores e uma análise de segurança de domínio semântico (SAST) profunda (CodeQL) de forma agendada para uma cobertura completa. A verificação de dependências é executada em cada build, pois novas vulnerabilidades CVE são publicadas diariamente.

Erros comuns de integração e como evitá-los

Executar a análise apenas na branch principal. O valor da análise estática reside em detectar problemas antes que eles sejam mesclados, não depois. A análise executada somente após a mesclagem serve como um relatório, não como um mecanismo de controle.

Configurar os pontos de controle de qualidade para emitir avisos em vez de reprovar. Um aviso que não bloqueia o fluxo de trabalho é uma notificação que os desenvolvedores aprendem a ignorar. Pontos de controle que não reprovam não contribuem para a construção de uma cultura de qualidade.

Não excluir arquivos de teste dos limites de cobertura. Testar código que testa outro código de teste gera números de cobertura inflados. Exclua diretórios de teste dos cálculos de cobertura para obter medições precisas da cobertura do código de produção.

Ignorar o backlog de análise em bases de código legadas. Ativar o SonarQube pela primeira vez em uma base de código legada com um milhão de linhas e encontrar 40,000 problemas é desmotivador e contraproducente. Use a abordagem de linha de base de novo código: defina uma data de início para o "novo código", aplique os gates apenas ao código escrito após essa data e trate o backlog de problemas existente como um programa separado de redução de dívida técnica.

Não incorporar os resultados nos comentários da solicitação de pull request. Os resultados de análise publicados em um painel separado, que os desenvolvedores precisam acessar, são ignorados. Os resultados de análise publicados como comentários em linha nas linhas específicas que introduziram problemas, dentro da interface de revisão de pull requests, são tratados. Configurar sonar.pullrequest.* Parâmetros para integração com GitHub, GitLab ou Bitbucket, para que os resultados apareçam onde a revisão de código ocorre.

Como SMART TS XL Integra-se com pipelines de CI/CD

A maioria das ferramentas de análise estática em pipelines de CI/CD enxerga uma linguagem por vez. O ESLint enxerga JavaScript. O SonarQube enxerga Java, Python, JavaScript e C#. Nenhuma delas enxerga COBOL, JCL, RPG ou as dependências entre linguagens que conectam um programa em lote de mainframe ao microsserviço Java que consome sua saída, que por sua vez se conecta ao frontend JavaScript que a exibe.

SMART TS XL Integra-se aos pipelines de CI/CD como uma camada de análise estática multilíngue que abrange simultaneamente todas as linguagens do ambiente corporativo. Quando um serviço Java é modificado, SMART TS XL'S análise de impacto O sistema rastreia o grafo de dependências para identificar quais programas COBOL, esquemas de banco de dados e serviços subsequentes são afetados pela alteração, antes que ela seja implementada. Quando um copybook COBOL é modificado, ele identifica todos os programas em todo o portfólio de aplicativos que incluem esse copybook e que precisam de validação.

Essa consciência da dependência entre idiomas é a capacidade que torna SMART TS XLA integração CI/CD do SonarQube é diferente de simplesmente adicionar outra linguagem ao SonarQube. Não se trata apenas de analisar o código no arquivo alterado. Trata-se de entender o que mais no sistema será afetado pela alteração, ou seja, a análise arquitetural que determina o verdadeiro escopo de uma mudança antes que ela seja implementada.

Para equipes de desenvolvimento empresarial que operam em sistemas legados e modernos, SMART TS XL'S Integração DevOps A funcionalidade incorpora essa análise estrutural ao fluxo de trabalho de revisão do pipeline, proporcionando a visibilidade arquitetural que as ferramentas específicas de linguagem não conseguem oferecer. O resultado são portões de qualidade que reforçam os padrões não apenas dentro dos limites de uma linguagem, mas em todo o sistema, garantindo que uma alteração em qualquer componente não introduza falhas inesperadas nos componentes que dependem dele.

Análise Estática como um Elemento Permanente da Logística

As equipes que dominam a análise estática a tratam da mesma forma que tratam os testes: não como uma etapa a ser concluída, mas como uma prática contínua incorporada em cada commit. A análise é executada em cada pull request. Os resultados do Quality Gate são exibidos em conjunto com a revisão de código. Os critérios de avaliação se tornam mais rigorosos à medida que a base de código melhora. Novas categorias de análise são adicionadas conforme a maturidade da equipe em segurança e qualidade aumenta.

Os modelos de configuração de pipeline neste artigo são pontos de partida. As ferramentas específicas, os limites e as condições de aprovação devem ser calibrados de acordo com a pilha de linguagens do projeto, o perfil de risco e a maturidade da equipe. O que não deve variar é o princípio: análises que não definem critérios para a fusão de código não alteram o comportamento, e é justamente o comportamento que os programas de qualidade de código buscam modificar.