Skip to main content

Enterprise Server 3.21 в настоящее время доступен в качестве кандидата на выпуск.

Использование инкрементального анализа с помощью CLI CodeQL

Получайте более CodeQL быстрые результаты по pull requests, анализируя только то, что изменилось. Инкрементальный анализ может сократить время сканирования до 10 раз, если вы запускаете CodeQL CLI его в собственной системе CI/CD.

Кто может использовать эту функцию?

CodeQL доступен для следующих типов репозитория:

О инкрементальном анализе

Полные CodeQL сканирования каждого pull-запроса могут быть медленными, особенно в больших кодовых базах. Если вы запускаете их CodeQL CLI в собственной системе CI/CD, инкрементальный анализ даёт два способа ускорить процесс:

  • Анализ с дифференцированной информацией сообщает только о добавленных или изменённых строках, поэтому запросы идут быстрее, а результаты более релевантны.
  • Анализ наложения повторно использует кэшированную базу данных из вашей стандартной ветки вместо создания её с нуля, что значительно сокращает время создания базы данных и оценки запросов.

Вы можете использовать эти функции как самостоятельно, так и вместе. Для большинства команд, анализирующих pull requests в устоявшихся кодовых базах, мы рекомендуем использовать оверлейный анализ для быстрого создания баз данных и оценки запросов, а также анализ с дифференциальным уровнем для целенаправленных и релевантных результатов.

Если вы используете code scanning настройки по умолчанию или codeql-action включение GitHub, инкрементальный анализ уже выполняется автоматически. Эта статья предназначена для команд, которые запускают CodeQL CLI их непосредственно в собственной CI/CD инфраструктуре.

Необходимые условия

Перед установкой инкрементального анализа убедитесь, что вы соответствуете следующим требованиям:

  • CodeQL CLI версия пакета: 2.21.0 или более позднее для анализа с дифференциальной информированностью; 2.23.8 или позже для анализа наложения (с минимальными требованиями по языкам, см. Минимальные версии пучков CLI)
  • Исходный корень должен находиться внутри репозитория Git
  • Git версии 2.38.0 или более поздней (требуется для анализа оверлея, в частности для --format опции, используемой )git ls-files
  • Все интересующие файлы должны отслеживаться через Git (не в .gitignore)
  • Индекс Git должен точно отражать анализируемое исходное дерево
  • Режим строительства: Поддержка только build-mode: none анализа оверлея (трассируемые сборки не поддерживаются). Go работает с анализом оверлея, хотя явно не поддерживает этот режим.

Выбор подхода

ScenarioDiff-informedOverlay
Стандартный push веткиНет (это не PR)Режим наложения базы
PR-анализ (первый раз, без кэша)YesНет (проведите полный анализ)
PR-анализ (с кэшированной базой)YesРежим наложения
НеPR, нестандартная веткаНетНет

Полные рабочие примеры в различных системах CI см. пример репозитория конфигураций конвейеров CodeQL .

Анализ с дифференцированными данными

Дифференцированный анализ — это оптимизация для анализа pull request. Вместо того чтобы сообщать обо всех оповещениях, найденных в кодовой базе, он сообщает только о тех оповещениях, которые были добавлены или изменены в дифференциативе pull request.

Шаг 1: Определите дифференциальные диапазоны

Вам нужны добавленные или изменённые диапазоны строк из дифференциала pull-request. Вход может поступать из любого источника (git diff, API вашей CI-платформы или другого механизма).

Для каждого изменённого файла составьте список диапазонов со следующей структурой:

  • path: Абсолютный путь к файлу (всегда используйте косые черты вперёд)
  • startLine: Стартовая линия на основе 1
  • endLine: Конечная линия на основе 1

Например, для этого унифицированного дифференциала (порождённого ):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>`;
 }

Полученные диапазоны дифференциали для src/utils.ts будут следующими:

  • ["/path/to/repo/src/utils.ts", 16, 17] (две вставленные линии во втором куске)
  • ["/path/to/repo/src/utils.ts", 27, 27] (модифицированная линия в третьем эпизоде)

Первый кусок содержит только удаление, поэтому не даёт дальности. Обратите внимание, что диапазоны используют номера строк «to» (новый файл), а не номера «from» (старый файл).

Особые случаи:

  • Бинарные файлы или очень большие различия (нет содержимого патча): используйте диапазон {path, startLine: 0, endLine: 0} Sentinel, чтобы обозначить «весь файл».
  • Переименованные файлы без изменений содержимого: Вернуть пустой массив (без диапазонов).
  • Усеченные дифференции: Если ваш источник diff неполный для больших pull request (например, API, ограничивающий количество изменённых файлов), лучше пропустить diff-informed анализ и запустить полный анализ для этого запуска.

Для эталонной реализации парсинга дифференциального анализа см. getDiffRanges() в codeql-action исходном коде.

Шаг 2: Создайте пакет расширения данных

Создайте временную директорию с двумя файлами. Этот расширенный пакет питается в restrictAlertsTo расширяемый предикат, 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]

Каждая строка данных — это [filePath, lineStart, lineEnd]. Номера строк основаны на 1. Особый случай lineStart = 0, lineEnd = 0 обозначает совпадение целого файла.

Внимание

Если в различии нет добавленных или изменённых строк (например, только удалений), вы всё равно должны предоставить непустое расширение данных с записью ["", 0, 0]sentinel . Пустая data секция оставляла restrictAlertsTo предикат неактивным, то есть все оповещения были бы созданы — противоположно желаемому поведению.

Шаг 3: Передайте удлинительный пакет CodeQL CLI

При выполнении запросов добавьте следующие флаги к 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 CodeQL указывает, где найти упаковку на диске. Дополнительные сведения см. в разделе запросы запуска базы данных.
  • --extension-packs CodeQL командует загрузить именованный расширенный пакет.

Шаг 4: Исключить диагностические запросы

При использовании дифференцированного анализа следует исключать запросы с exclude-from-incrementalтегом . Эти диагностические запросы не создают оповещений (например, метрики или покрытие кода), поэтому они не дают ценности в инкрементальном контексте, но всё равно потребляют ресурсы.

Вы можете добавить это в ваш конфигурационный файл сканирования кода:

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

Альтернативно, создайте файл набора запросов (.qls), который исключает эти запросы:

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

Дополнительные сведения см. в разделе Параметры настройки рабочих процессов для сканирования кода.

Шаг 5: Отфильтруйте выход SARIF

После CodeQL генерации файла SARIF необходимо отфильтровать выход на стороне CI, чтобы удалить результаты, расположение которых выходит за пределы дифференциальных диапазонов.

Для каждого результата в SARIF проверяйте, пересекаются ли какие-либо из его locations или relatedLocations с дифференциальным диапазоном для этого файла. Местоположение пересекает диапазон, когда range.startLine <= location.endLine и location.startLine <= range.endLine. Особый случай range.startLine == range.endLine == 0 совпадает с любым местом в файле. Убедитесь, что расположение артефактов SARIF разрешено в том же формате абсолютного пути, что и в дифференциальных диапазонах, прежде чем сравнивать.

Предикат restrictAlertsTo допускает, но не гарантирует, что запросы пропускают оповещения вне зоны зоны зоны действия, поэтому для стабильных результатов требуется фильтрация на стороне CI.

Для эталонной реализации фильтрации SARIF см. filterAlertsByDiffRange() в codeql-action исходном коде.

Сводка флагов CLI для дифференцированного анализа

Команда CLIFlagPurpose
codeql database init--codescanning-config=FILEКонфигурационный файл сканирования кода (для фильтра запросов)
codeql database run-queries--additional-packs=DIRРасположение удлинительного пакета
codeql database run-queries--extension-packs=my-ci/pr-diff-rangeНазвание расширения для загрузки
codeql database interpret-results--sarif-run-property=incrementalMode=diff-informed(По желанию) Помечайте SARIF с метаданными с дифференциальной информацией

Анализ наложения

Анализ наложения ускоряет CodeQL создание базы данных и оценку запросов для pull requests, строя базу на уже существующей «базовой» базе данных:

  1. На ветке по умолчанию: Создать базу данных «overlay-base» (полную базу данных с кэшированными промежуточными результатами). Это может быть любая долговечная ветка, на которую целятся pull request.
  2. О pull requests: Скачайте кэшированную базу данных на базе оверлея, затем создайте лёгкую «оверлейную» базу, которая обрабатывает только изменённые файлы.

Режим наложения (по умолчанию ветвь)

Запускайте режим overlay-base на вашей стандартной или долгоживущей целевой ветке после каждого слияния, чтобы создать и кэшировать базовую базу данных.

1. Инициализировать базу данных с --overlay-base

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

Флаг указывает CodeQL на создание базы данных, которая может служить основой для будущего --overlay-base анализа наложения.

2. Постройте и извлекайте как обычно

Запускайте все этапы сборки и извлечение, как обычно для вашего проекта.

3. OID файлов записей

После завершения извлечения запишите идентификаторы объектов Git (OID) всех отслеживаемых файлов под исходным корнем. Выполните эту команду из исходного корневого каталога (PATH_TO_SOURCE). Этот снимок используется позже, чтобы определить, какие файлы изменились.

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

Разберите этот вывод в JSON-карту и { "relative/path": "git-oid" } храните его вместе с базой данных. Выход включает файлы в подмодулях Git, которые для анализа наложения необходимы для точного отслеживания всех изменений файлов между базой и оверлеем.

4. Запустите запросы и сохраняйте кэш

При выполнении запросов к базе данных с наложением не проходят --expect-discarded-cache. Кэшированные промежуточные результаты делают сборку pull request быстрой. Отказ от них заставил бы полностью пересмотреть каждый PR.

5. Очистить и кэшировать базу данных

После анализа очистите базу данных с помощью overlay уровня очистки:

codeql database cleanup PATH_TO_DATABASE --cache-cleanup=overlay

Уровень overlay очистки сохраняет больше кэшированных данных, чем стандартный clear уровень. Режим оверлея повторно использует эти кэшированные данные для эффективной оценки запросов на pull-запросах, поэтому их отказ устраняет преимущество производительности.

Затем храните базу данных (включая файл OIDs) в вашей кэширующей системе для дальнейшего поиска с помощью сборок pull request.

Режим оверлея (pull requests)

Запускайте режим оверлея на сборках pull request, чтобы создать лёгкую базу данных поверх кэшированной базы. Если в кэше нет совместимой базы данных с оверлеем (например, при первом запуске или после обновления CodeQL CLI версии), пропустите --overlay-changes и запустите обычный полный анализ. Ключи кэша должны включать как минимум версию CodeQL CLI и набор языков, чтобы избежать несовместимых базовых баз данных.

1. Скачайте кэшированную базу данных с наложением

Получите самую свежую базу данных с наложением из вашего кэша. База данных должна включать файл OID, записанный в режиме наложения основания.

2. Вычислить изменённые файлы

Сравните OID, записанные в базовой базе данных, с текущим состоянием Git. Выполните эту команду из того же корневого каталога исходного источника (PATH_TO_SOURCE), используемой в режиме overlay-base:

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

Сравните две карты, чтобы найти файлы, которые были добавлены, удалены или изменены (разный OID). Запишите результат в формате JSON-файла:

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

Пути к файлу должны быть относительно исходного корня.

3. Инициализировать базу данных с помощью --overlay-changes

Запустите codeql database init с восстановленной наложенной базой данных. Они PATH_TO_DATABASE должны указывать на восстановленную кэшированную базу данных с базой наложения, а не на новую пустую директорию — команда расширяет существующую базу для анализа pull request.

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

Внимание

В режиме наложения не проходите --overwrite или --force-overwrite. Вы строите базу данных поверх существующей кэшированной базы данных, а не заменяете её.

4. Создать, извлечь и выполнить запросы как обычно

Переходите к сборке, извлечению и выполнению запросов как обычно. Вы можете добавить --sarif-run-property флаг к существующей codeql database interpret-results команде, чтобы помечать выход SARIF наложением метаданных:

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

Если активны оверлейный и дифференцированный анализ, используйте incrementalMode=overlay,diff-informed.

Оповещения от инкрементального анализа появляются в результатах сканирования кода pull запроса так же, как и оповещения при полном сканировании. Любая база данных с наложением будет работать независимо от возраста, но более свежие базы дают результаты быстрее и точнее.

Как и при дифференцированном анализе, исключайте запросы, отмеченные exclude-from-incremental тегами, при использовании режима наложения. Для подробностей см. Шаг 4: Исключить диагностические запросы.

Сводка флагов CLI для анализа наложения

Команда CLIFlagРежимPurpose
codeql database init--codescanning-config=FILEОверлейКонфигурационный файл сканирования кода (для фильтра запросов)
codeql database init--overlay-baseНаложениеСоздать базовую базу данных для будущего использования наложения
codeql database init--overlay-changes=FILEОверлейПостройте базу данных наложения, используя только изменённые файлы
codeql database init
(нет --overwrite)ОверлейНе перезаписывайте кэшированную базовую базу данных
codeql database run-queries
(нет --expect-discarded-cache)НаложениеСохранить кэшированные промежуточные результаты
codeql database cleanup--cache-cleanup=overlayНаложениеИспользуйте уровень, специфичный для наложения,
codeql database interpret-results--sarif-run-property=incrementalMode=overlayОверлейТег SARIF с наложением метаданных

Минимальные версии наборов CLI

Базовая минимальная версия для анализа наложения — 2.23.8. Некоторые языки требуют более высокие минимальные версии:

ЯзыкВерсия с минимальным CodeQL CLI набором
C/C++2.25.0
C#2.24.1
Go2.24.2
Java2.23.8
JavaScript2.23.9
Python2.23.9
Ruby2.23.9