Skip to main content

Usando a análise incremental com a CLI do CodeQL

Obtenha resultados mais rápidos CodeQL em solicitações de pull analisando apenas o que mudou. A análise incremental pode reduzir os tempos de varredura em até 10x quando você executa o CodeQL CLI no seu próprio sistema de CI/CD.

Quem pode usar esse recurso?

O CodeQL está disponível para os seguintes tipos de repositórios:

Sobre a análise incremental

As verificações completas CodeQL em cada solicitação de pull podem ser lentas, especialmente em grandes bases de código. Se você executar o CodeQL CLI no seu próprio sistema de CI/CD, a análise incremental oferece duas maneiras de acelerar o processo:

  • A análise informada por Diff relata apenas alertas em linhas que você adicionou ou alterou, portanto, as consultas são executadas mais rapidamente e os resultados são mais relevantes.
  • A análise de sobreposição reutiliza um banco de dados armazenado em cache do branch padrão em vez de criar um do zero, reduzindo drasticamente o tempo de criação e avaliação de consulta do banco de dados.

Você pode usar esses recursos de forma independente ou em conjunto. Para a maioria das equipes que analisam solicitações de pull em bases de código estabelecidas, é recomendável usar ambos: análise de sobreposição para criação rápida de banco de dados e avaliação de consulta e análise informada por diferenciação para resultados focados e relevantes.

Se você usar code scanning configuração padrão ou o codeql-action em GitHub, a análise incremental já é tratada automaticamente. Este artigo destina-se às equipes que executam diretamente CodeQL CLI em sua própria infraestrutura de CI/CD.

Pré-requisitos

Antes de configurar a análise incremental, verifique se você atende aos seguintes requisitos:

  • CodeQL CLI versão do pacote: 2.21.0 ou posterior para análise informada por diferenciação; 2.23.8 ou posterior para análise de sobreposição (com mínimos por idioma, consulte versões mínimas do pacote da CLI)
  • A raiz de origem deve estar dentro de um repositório Git
  • Git versão 2.38.0 ou posterior (necessária para análise de sobreposição, especificamente a opção --format usada por git ls-files)
  • Todos os arquivos de interesse devem ser acompanhados pelo Git (não em .gitignore)
  • O índice Git deve refletir com precisão a árvore de origem que está sendo analisada
  • Modo de build: A análise de sobreposição oferece suporte apenas a build-mode: none (builds rastreados não são compatíveis). O Go funciona com a análise de sobreposição, apesar de não dar suporte explicitamente a esse modo.

Escolhendo uma abordagem

ScenarioBaseado em diffSobreposição
Push para a ramificação padrãoNão (não é uma PR)modo base de sobreposição
Análise de PR (primeira vez, sem cache)YesNão (executar análise completa)
Análise de PR (com base em cache)Yesmodo de sobreposição
Ramificação sem pull request e não padrãoNoNo

Para obter exemplos completos e funcionais em vários sistemas de CI, consulte o repositório de configurações de exemplo de pipeline do CodeQL.

Análise informada por difusão

A análise informada por Diff é uma otimização para análise de solicitação de pull. Em vez de informar todos os alertas encontrados na base de código, ele informa apenas os alertas nas linhas que foram adicionadas ou modificadas no diff do pull request.

Etapa 1: identificar os intervalos de diferenciação

Você precisa dos intervalos de linhas adicionadas ou modificadas no diff da pull request. A entrada pode vir de qualquer origem (git diffa API da plataforma de CI ou outro mecanismo).

Para cada arquivo alterado, produza uma lista de intervalos com a seguinte estrutura:

  • path: caminho de arquivo absoluto (sempre use barras para frente)
  • startLine: linha inicial inclusiva baseada em 1
  • endLine: linha final inclusiva e baseada em 1

Por exemplo, dada essa diferença unificada (gerada por git diff):

--- a/src/utils.ts
+++ b/src/utils.ts
@@ -2,7 +2,6 @@ import { helper } from './helper';
 
 function existing() {
   const x = 1;
-  const unused = 2;
   return x;
 }
 
@@ -14,6 +13,8 @@ function validate(input: string) {
 function process(input: string) {
   // validate
   if (!input) return;
+  const sanitized = input.trim();
+  console.log(sanitized);
   return input;
 }
 
@@ -23,5 +24,5 @@ function format(value: number) {
 
 function render(data: object) {
   const output = JSON.stringify(data);
-  return output;
+  return `<div>${output}</div>`;
 }

Os intervalos de diff resultantes para src/utils.ts seriam:

  • ["/path/to/repo/src/utils.ts", 16, 17] (as duas linhas inseridas no segundo pedaço)
  • ["/path/to/repo/src/utils.ts", 27, 27] (a linha modificada no terceiro pedaço)

O primeiro pedaço contém apenas uma exclusão, portanto, ele não produz nenhum intervalo. Observe que os intervalos usam os números de linha "para" (novo arquivo), não os números "de" (arquivo antigo).

Casos especiais:

  • Arquivos binários ou diffs muito grandes (sem conteúdo de patch disponível): use o intervalo sentinela {path, startLine: 0, endLine: 0} para indicar "o arquivo inteiro".
  • Arquivos renomeados sem alterações de conteúdo: retornar uma matriz vazia (sem intervalos).
  • Difusões truncadas: se a fonte de diferenciação estiver incompleta para solicitações de pull grandes (por exemplo, uma API que limita o número de arquivos alterados), você deve ignorar a análise informada por diferenciação e executar a análise completa para essa execução.

Para uma implementação de referência da análise de diff, consulte getDiffRanges() no código-fonte codeql-action.

Etapa 2: Criar um pacote de extensão de dados

Crie um diretório temporário contendo dois arquivos. Esse pacote de extensão se integra ao predicado extensível restrictAlertsTo definido na biblioteca padrão CodeQL.

** qlpack.yml:**

name: my-ci/pr-diff-range
version: 0.0.0
library: true
extensionTargets:
  codeql/util: '*'  # Target the codeql/util pack where restrictAlertsTo is defined
dataExtensions:
  - pr-diff-range.yml

** pr-diff-range.yml:**

extensions:
  - addsTo:
      pack: codeql/util
      extensible: restrictAlertsTo
      checkPresence: false  # Don't error if the predicate doesn't exist in older CLI versions
    data:
      # Each row: [filePath, startLine, endLine]
      - ["/path/to/repo/src/utils.ts", 16, 17]
      - ["/path/to/repo/src/utils.ts", 27, 27]

Cada linha de dados é [filePath, lineStart, lineEnd]. Os números de linha são baseados em 1. O caso especial lineStart = 0, lineEnd = 0 indica uma correspondência em todo o arquivo.

Importante

Se o diff tiver zero linhas adicionadas ou modificadas (por exemplo, apenas exclusões), você ainda deve fornecer uma extensão de dados não vazia com uma entrada sentinela ["", 0, 0]. Uma seção data vazia deixaria o predicado restrictAlertsTo inativo, o que significa que todos os alertas seriam gerados — o oposto do comportamento desejado.

Etapa 3: passar o pacote de extensão para o CodeQL CLI

Ao executar consultas, adicione os seguintes sinalizadores a codeql database run-queries:

codeql database run-queries \
  --additional-packs=PATH_TO_EXTENSION_PACK \
  --extension-packs=my-ci/pr-diff-range \
  PATH_TO_DATABASE \
  QUERIES
  • --additional-packs informa CodeQL onde encontrar o pacote no disco. Para obter mais informações, consulte executar consultas no banco de dados.
  • --extension-packs instrui CodeQL a carregar o pacote de extensões especificado.

Etapa 4: Excluir consultas de diagnóstico

Ao usar a análise informada por difusão, você deve excluir consultas marcadas com exclude-from-incremental. Essas consultas de diagnóstico não produzem alertas (por exemplo, métricas ou cobertura de código), portanto, não fornecem valor em um contexto incremental, mas ainda consomem recursos.

Você pode adicionar isso ao arquivo de configuração de verificação de código:

query-filters:
  - exclude:
      tags: exclude-from-incremental

Como alternativa, crie um arquivo do conjunto de consultas (.qls) que exclua essas consultas:

- description: Pull request queries for Java
- import: codeql-suites/java-code-scanning.qls
  from: codeql/java-queries
- exclude:
    tags contain: exclude-from-incremental

Para obter mais informações, consulte Opções de configuração de fluxo de trabalho para verificação de código.

Etapa 5: Filtrar a saída SARIF

Depois CodeQL de gerar o arquivo SARIF, você deve filtrar a saída no lado da CI para remover resultados cujos locais estão fora dos intervalos de diferenciação.

Para cada resultado no SARIF, verifique se algum deles locations ou relatedLocations se cruza com um intervalo de diferenciação para esse arquivo. Uma posição intersecta um intervalo quando range.startLine <= location.endLine e location.startLine <= range.endLine. O caso especial range.startLine == range.endLine == 0 corresponde a qualquer localização no arquivo. Verifique se os locais do artefato SARIF são resolvidos para o mesmo formato de caminho absoluto usado nos intervalos de diferenciação antes da comparação.

O predicado restrictAlertsTo permite, mas não garante, que as consultas omitam alertas fora do intervalo, portanto a filtragem no lado da CI é necessária para obter resultados estáveis.

Para ver uma implementação de referência para filtragem SARIF, consulte filterAlertsByDiffRange() no código-fonte codeql-action.

Resumo dos sinalizadores da CLI para análise informada por diferenciação

Comando da CLIFlagPurpose
codeql database init--codescanning-config=FILEArquivo de configuração de verificação de código (para filtro de consulta)
codeql database run-queries--additional-packs=DIRLocal do pacote de extensões
codeql database run-queries--extension-packs=my-ci/pr-diff-rangeNome do pacote de extensão a ser carregado
codeql database interpret-results--sarif-run-property=incrementalMode=diff-informed(Opcional) Marcar SARIF com metadados baseados em diff

Análise de sobreposição

A análise em sobreposição acelera CodeQL a criação de bancos de dados e a avaliação de consultas para pull requests, com base em um banco de dados "base" pré-existente:

  1. Na ramificação padrão: Construa um banco de dados "overlay-base" (um banco de dados completo com resultados intermediários em cache). Essa pode ser qualquer branch de longa duração para a qual as pull requests são direcionadas.
  2. Em solicitações de pull: Baixe o banco de dados base de sobreposição em cache e crie um banco de dados leve de "sobreposição" que processa apenas os arquivos alterados.

Modo de base de sobreposição (ramificação padrão)

Execute o modo overlay-base na sua branch de destino padrão ou de longa duração após cada mesclagem para gerar e armazenar em cache um banco de dados base.

1. Inicializar o banco de dados com --overlay-base

codeql database init \
  --overlay-base \
  --db-cluster \
  PATH_TO_DATABASE \
  --source-root=PATH_TO_SOURCE \
  --language=LANGUAGE

A opção --overlay-base indica a CodeQL que crie um banco de dados que possa servir como base para futuras análises de sobreposição.

2. Compilar e extrair normalmente

Execute as etapas de build e a extração como faria normalmente para seu projeto.

3. Registrar OIDs de arquivo

Após a conclusão da extração, registre os IDs de objeto do Git (OIDs) de todos os arquivos rastreados sob a raiz de origem. Execute este comando no diretório raiz de origem (PATH_TO_SOURCE). Esse instantâneo é usado posteriormente para determinar quais arquivos foram alterados.

cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'

Analise essa saída em um mapa { "relative/path": "git-oid" } JSON e armazene-a junto com o banco de dados. A saída inclui arquivos em submódulos do Git, o que exige que a análise de sobreposição rastreie com precisão todas as alterações nos arquivos entre a base e a sobreposição.

4. Executar consultas e preservar o cache

Ao executar consultas em um banco de dados de base de sobreposição, não passe --expect-discarded-cache. Os resultados intermediários armazenados em cache são o que torna as compilações de solicitação de pull rápidas. Descartá-los forçaria a reavaliação completa em cada PR.

5. Limpar e armazenar em cache o banco de dados

Após a análise, limpe o banco de dados usando o nível de limpeza overlay:

codeql database cleanup PATH_TO_DATABASE --cache-cleanup=overlay

O overlay nível de limpeza preserva mais dados armazenados em cache do que o nível padrão clear . O modo de sobreposição reutiliza esses dados armazenados em cache para uma avaliação de consulta eficiente em solicitações de pull, portanto, descartá-los eliminaria o benefício de desempenho.

Em seguida, armazene o banco de dados (incluindo o arquivo OIDs) em seu sistema de cache para recuperação posterior por builds de solicitação de pull.

Modo de sobreposição (solicitações de pull)

Execute o modo de sobreposição em builds de solicitação de pull para criar um banco de dados leve na parte superior da base armazenada em cache. Se nenhum banco de dados base de sobreposição compatível estiver disponível no cache (por exemplo, na primeira execução ou após uma CodeQL CLI atualização de versão), ignore --overlay-changes e execute uma análise completa normal. As chaves de cache devem incluir pelo menos a versão e o CodeQL CLI conjunto de idiomas para evitar bancos de dados base incompatíveis.

1. Baixe o banco de dados overlay-base armazenado em cache

Recupere o banco de dados base de sobreposição mais recente do cache. O banco de dados deve incluir o arquivo de OIDs registrado durante o modo overlay-base.

2. Calcular arquivos alterados

Compare os OIDs registrados no banco de dados base com o estado git atual. Execute este comando no mesmo diretório raiz de origem (PATH_TO_SOURCE) usado durante o modo base de sobreposição:

cd PATH_TO_SOURCE && git ls-files --recurse-submodules --format='%(objectname)_%(path)'

Compare os dois mapas para localizar arquivos que foram adicionados, removidos ou modificados (OID diferente). Escreva o resultado como um arquivo JSON:

{
  "changes": ["src/modified-file.ts", "src/new-file.ts", "src/deleted-file.ts"]
}

Os caminhos de arquivo devem ser relativos à raiz de origem.

3. Inicializar o banco de dados com --overlay-changes

Execute codeql database init no diretório do banco de dados overlay-base restaurado. O PATH_TO_DATABASE deve apontar para o banco de dados de base de sobreposição em cache restaurado, não um novo diretório vazio– o comando estende a base existente para a análise de solicitação de pull.

codeql database init \
  --overlay-changes=PATH_TO_OVERLAY_CHANGES_JSON \
  --db-cluster \
  PATH_TO_DATABASE \
  --source-root=PATH_TO_SOURCE \
  --language=LANGUAGE

Importante

No modo de sobreposição, não passe --overwrite ou --force-overwrite. Você está desenvolvendo sobre o banco de dados base em cache existente, não o substituindo.

4. Criar, extrair e executar consultas normalmente

Prossiga com a execução de build, extração e consulta normalmente. Você pode adicionar o sinalizador --sarif-run-property ao seu comando codeql database interpret-results existente para marcar a saída SARIF com metadados de sobreposição:

codeql database interpret-results \
  --format=sarif-latest \
  --output=results.sarif \
  --sarif-run-property=incrementalMode=overlay \
  PATH_TO_DATABASE \
  QUERIES_OR_SUITES

Se tanto a sobreposição quanto a análise baseada em diff estiverem ativas, use incrementalMode=overlay,diff-informed.

Os alertas da análise incremental aparecem nos resultados da varredura de código do pull request da mesma forma que os alertas das varreduras completas. Qualquer banco de dados base de sobreposição funcionará independentemente da idade, mas bases mais recentes produzem resultados mais rápidos e precisos.

Assim como na análise baseada em diff, exclua as consultas marcadas com exclude-from-incremental ao usar o modo de sobreposição. Para obter detalhes, consulte a Etapa 4: Excluir consultas de diagnóstico.

Resumo dos sinalizadores da CLI para análise de sobreposição

Comando da CLIFlagModePurpose
codeql database init--codescanning-config=FILEsobreposiçãoArquivo de configuração de verificação de código (para filtro de consulta)
codeql database init--overlay-baseoverlay-baseCriar um banco de dados básico para uso futuro em sobreposições
codeql database init--overlay-changes=FILEsobreposiçãoCriar banco de dados de sobreposição usando apenas arquivos alterados
codeql database init
(não --overwrite)sobreposiçãoNão substitua o banco de dados base armazenado em cache
codeql database run-queries
(não --expect-discarded-cache)overlay-basePreservar resultados intermediários armazenados em cache
codeql database cleanup--cache-cleanup=overlayoverlay-baseUsar o nível de limpeza específico da sobreposição
codeql database interpret-results--sarif-run-property=incrementalMode=overlaysobreposiçãoMarcar SARIF com metadados de sobreposição

Versões mínimas do pacote da CLI

A versão mínima base para análise de sobreposição é 2.23.8. Alguns idiomas exigem versões mínimas mais altas:

LinguagemVersão mínima CodeQL CLI do pacote
C/C++2.25.0
C#2.24.1
Go2.24.2
Java2.23.8
JavaScript2.23.9
Python2.23.9
Ruby2.23.9