Настройка SAST через CodeQL в GitHub Actions: поиск уязвимостей кода

Проверки кода часто дают сотни предупреждений, и команда перестаёт их смотреть. Мы настраиваем CodeQL под ваш поток изменений: убираем шум, оставляем только реальные уязвимости и передаём готовые правила вместе с регламентом. Обсудим задачу и вернёмся с оценкой и планом.
Часто задаваемые вопросы

Настройка CodeQL не приживается: ложные срабатывания и шум в отчётах

Формально проверка в конвейере есть, ветка защищена, отчёт открывается — и при этом никто в него не смотрит. Настройка CodeQL чаще всего сводится к включению готового набора запросов «на всё» без указания шагов сборки: анализатор разбирает код частично, путь данных обрывается на границах модулей, и в отчёте всплывают десятки находок, к уязвимостям не относящихся.

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

Главная потеря от такого шума — не время на разбор, а доверие к инструменту. Разработчики начинают закрывать находки не глядя, ставят исключения на каталоги, отключают шаг проверки при срочном релизе — и статический анализ тихо выпадает из процесса. Игнорирование находок становится нормой раньше, чем команда успевает оценить реальный эффект от внедрения.

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

Разбираем, как это закрывается: от сборки корректной базы и настройки порогов до правил, при которых проверка становится обязательной, но не мешает выпуску.

Что меняется после внедрения: время проверки и покрытие кода

Проверка перестаёт быть узким местом слияния

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

Покрытие анализом видно в цифрах

Настраиваем область: новые коммиты, ветки выпуска, языки проекта. Метрика — доля изменённых строк, прошедших проверку; на старте она обычно ниже половины, после включения обязательной проверки на слияние уходит к 100% по новому коду, историю подтягиваем порциями. Незакрытые участки видно, а не «где-то, наверное».

Классы дефектов, которые не доходят до слияния

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

Дефекты становятся метрикой, а не разовой акцией

Считаем число замечаний на выпуск, долю ложных срабатываний и время от находки до исправления. Порог на слияние не даёт накоплениям расти, сравнение по релизам показывает, работают ли правила. Через несколько циклов видно, какие классы ошибок команда перестала допускать.

Режимы запуска проверок: сравнение вариантов и ограничений

CodeQL не разбирает файл в отрыве от остального кода: перед проверкой собирается база данных проекта, и от её состава зависит, какие цепочки передачи данных движок увидит. Режим запуска — это выбор между скоростью обратной связи и полнотой покрытия, а не просто настройка триггера. Ниже сравнение вариантов сканирования, между которыми реально выбирают на старте.

Вариант запуска Когда подходит Ограничения анализа
Анализ изменённых файлов при push Ветки задач, быстрый отклик автору правки Межфайловые цепочки не собираются: утечка через два-три модуля останется незамеченной
Проверка на каждый коммит в pull request Обязательная проверка до слияния в основную ветку База пересобирается на каждом обновлении; при частых правках сборки встают в очередь
Полный прогон по расписанию Ночной контроль всего репозитория, включая давно не тронутые модули Находка приходит с задержкой, а виновника среди параллельных правок искать дольше
Анализ релизных веток и тегов Перед сборкой артефакта, который уходит в продуктив Позднее обнаружение: правка уже лежит поверх и требует пересмотра
Ручной запуск Разбор инцидента или проверка перед внешним аудитом Регулярности нет: пропущенный запуск никто не заметит

Как режимы сочетаются

На практике их не противопоставляют: быстрый фильтр по изменённым путям даёт автору правки обратную связь за минуты, проверка на каждый коммит в pull request закрывает основную ветку, а полный прогон по расписанию добирает то, что не попало в срез — сгенерированный код, конфигурации, устаревшие модули. Расхождение результатов между режимами разбирают через исключения в конфигурации запросов, чтобы одна и та же находка не всплывала повторно на релизе.

Настройка CodeQL по шагам: от аудита репозиториев до передачи

Настройка CodeQL начинается не с правки конфигурации, а с инвентаризации: пока не видно, какие репозитории что содержат и как часто меняются, любой набор правил стреляет в молоко. Ниже — порядок, по которому идём на каждом проекте; каждый шаг заканчивается артефактом, который передаём команде.

  1. Инвентаризация репозиториев. Собираем список сервисов, языков и релизных веток, отдельно отмечаем архивные проекты — по ним анализ не запускаем.
  2. Сборка базы кода. Разворачиваем базу по каждому языку: для интерпретируемых — чистый анализ, для сборочных — проход с компиляцией; зависимости и генерацию кода фиксируем в описании задачи.
  3. Набор правил запросов. Типовой набор берём за основу, добавляем правила под наш стек и отключаем шумные — итог версионируется как файл конфигурации, а не как ручные правки в интерфейсе.
  4. Интеграция в конвейер. На изменениях — быстрый проход, в ночном прогоне — полный; отчёты не блокируют слияние, если сломался сам анализатор.
  5. Отсечение ложных срабатываний. Каждую пачку находок разбираем с разработчиками: часть закрываем правкой кода, часть — исключением с причиной и владельцем.
  6. Проверка на реальных изменениях. Прогоняем на настоящих ветках: ловит ли анализ новую уязвимость и не рвёт ли сборку там, где не должен.
  7. Передача. Отдаём конфигурации, список исключений с обоснованиями, инструкцию по чтению отчётов и порядок эскалации чужих находок.

Что передаём на выходе: конфигурации, правила и регламенты

Всё, что появляется при настройке CodeQL, остаётся у команды в репозитории и в базе знаний — не в переписке и не в чужой голове. Конфигурация живёт рядом с кодом и проходит ревью как обычное изменение, поэтому проверки не рассыпаются при смене подрядчика или уходе инженера с проекта.

  • Файл конфигурации анализа кода — условия запуска (изменение в ветке, запрос на слияние, ночной прогон по расписанию), список языков и веток, глубина разбора.
  • Наборы правил — разделены на обязательные и рекомендательные, с привязкой к критичности модуля: платёжные и авторизационные пути проверяются строже остального.
  • Схема исключений — что и почему не проверяем, с ответственным и сроком пересмотра, чтобы список не превратился в свалку.
  • Политика доступа к отчётам — кто видит находки, кто вправе закрыть, как результаты разводятся по ролям и зонам ответственности.
  • Регламент реагирования на находки — сроки разбора по уровням критичности, кто ведёт первичный разбор, в какой момент заводится задача.
  • Связка с системой задач — автосоздание дефектов по шаблону, с метками критичности и ответственным по модулю.
  • Документация по проверкам — как запустить анализ локально, как читать отчёт, как добавить или ослабить правило.
  • План развития — какие языки и проверки подключаются следующими, что постепенно уходит из исключений.

С таким комплектом новый инженер выходит на самостоятельную работу за один день, без звонков и пересказа контекста.

Кейс: сократили шум и время проверки на потоке изменений

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

Настройку CodeQL начали с того, что развели анализ на два профиля. Быстрый идёт по изменённым файлам на каждый запрос на слияние, полный — по расписанию на всю ветку. Сгенерированный код и фикстуры ушли в исключения, порог срабатывания привязали к критичности: проверка падает, только если найден путь эксплуатации, а не потенциально опасный вызов. Дополнительно описали правила под наш слой доступа к данным — там, где общий набор молчал.

Доля ложных срабатываний снизилась с 62% до 14%. Время прохождения проверки на запросе на слияние сократилось с 18–25 минут до 4–6 — результат приходит раньше, чем автор переключился на следующую задачу. Доля находок, закрытых до слияния, выросла с 30% до 87%: правка в момент написания кода занимает минуты, разбор последствий в рабочем окружении — часы.

Отдельно закрепили регламент: обход обязательной проверки возможен только по явному решению ответственного и остаётся в истории. Через два месяца таких случаев — единицы, а необходимость самой проверки перестала быть предметом спора. Правила пересматриваем по мере роста кодовой базы, иначе набор снова начнёт шуметь.

Что делать, если проверки уже развёрнуты, но отчёты игнорируют?

Развёрнутые проверки, отчёты которых никто не открывает, — это не безопасность, а фоновая нагрузка на сборку. Причина обычно не в CodeQL, а в том, как встроены его результаты: первые недели приносят сотни находок в унаследованном коде, и доверие к отчёту падает быстрее, чем находится время на разбор.

Первым делом пересматривают порог блокировки слияния. Проверку вешают только на новые изменения — правила применяются к строкам, которые реально правятся в запросе на слияние, а старый код уходит в базовый срез. Сборка начинает падать на нескольких находках вместо сотен, и у команды появляется причина их разобрать, а не отключить проверку целиком.

Второй шаг — пересмотр набора правил: отключают запросы с высоким уровнем ложных срабатываний, исключают из анализа каталоги тестов, сгенерированного кода и внешних зависимостей. Остаются правила, где находка означает эксплуатируемый риск, — такой список не учатся игнорировать.

Третий шаг — ответственность. За каждым каталогом закрепляют владельца в файле CODEOWNERS, а разбор находок — за дежурным инженером с окном реакции в сутки. Находка без владельца живёт до устаревания, находка с владельцем закрывается или помечается как принятый риск с обоснованием.

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

Технические сомнения: сборка, закрытые репозитории и нагрузка

Перед стартом приходят одни и те же сомнения — они касаются не возможностей анализа, а того, как он встроится в текущий процесс разработки. Ниже короткие ответы по каждому пункту, с которым сталкиваемся чаще всего: нестандартные сборки, закрытые репозитории, нагрузка на конвейер, мультиязычные проекты и собственные правила.

Сборка нестандартная — анализ вообще заработает?

Для компилируемых языков разбор идёт по факту сборки, поэтому ключевое условие — воспроизводимость шага сборки в чистом окружении. Если сборка тянет данные извне или требует ручных действий, переключаемся на анализ без компиляции: часть проверок на границах потоков данных теряется, базовый набор дефектов остаётся. На пилотной ветке это видно за один прогон, до раскатки на все репозитории.

Репозитории закрытые, код не должен уходить наружу

Разбор запускается на исполнителях внутри вашего контура, исходники остаются в вашей инфраструктуре. Наружу при необходимости отдаём только агрегированные результаты без фрагментов кода — либо не отдаём вовсе, если отчёты собираются локально.

Насколько это замедлит конвейер?

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

У нас несколько языков в одном проекте

Каждая часть разбирается своим набором правил, результаты сводятся в один отчёт. Монорепозитории размечаем по путям, чтобы владельцы сервисов получали только свои замечания и не гасили чужие.

Можно добавить свои правила проверки?

Да, описываем собственные правила под ваши внутренние соглашения: запрет небезопасных вызовов, обязательные проверки прав доступа, требования к обработке персональных данных. Такие правила версионируются вместе с конфигурацией, чтобы изменения проходили ревью, а не появлялись молча.

Обсудим задачу: подберём состав проверок под ваш проект

Расскажите, какие репозитории и языки нужно закрыть анализом, как сейчас устроен конвейер и что мешает читать отчёты. Проведём аудит на пилотной ветке, покажем, сколько находок реально значимы, и предложим конфигурацию под ваш поток изменений. Объём работ и сроки рассчитываются индивидуально после разбора задачи.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139