SAST-анализ с Semgrep: настройка CI/CD, кастомные правила, триаж

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

Semgrep настройка: почему анализ кода не ловит уязвимости

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

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

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

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

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

Скорость проверок, шум в отчётах, покрытие правил

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

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

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

Эффект считаем по потоку находок: сколько подтверждено, сколько отклонено как ложные, какие классы дефектов не доходят до релиза. Инъекции в запросы, небезопасная десериализация, ключи и токены, зашитые в код, перестают всплывать на тестировании проникновением — они блокируются при слиянии.

Режимы сканирования: конвейер сборки, локальный запуск, расписание

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

Режим Когда уместен Чем ограничен
Полный скан ветки в конвейере сборки Защищённые ветки и запросы на слияние в основную Время растёт с объёмом репозитория: на крупном монорепозитории это десятки минут, поэтому держим его только на ключевых ветках
Проверка только изменённых файлов Каждый коммит в рабочих ветках Не видит проблемы в коде, который не менялся, и требует корректной базы сравнения при переписывании истории
Локальная проверка перед отправкой Хук перед коммитом и ручной прогон перед отправкой изменений Зависит от дисциплины и версии правил на машине разработчика: расхождение с сервером и обход проверки
Запуск по расписанию Ночной полный прогон основной и долгоживущих веток Отчёт приходит с задержкой и не блокирует слияние; нужен канал доставки и регулярный триаж находок

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

Semgrep настройка: порядок работ от аудита до передачи

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

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

Артефакты: конфигурации правил, схемы, регламент разбора находок

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

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

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

Кейс: доля ложных срабатываний и время проверки до и после

Исходная точка

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

Что изменили

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

Результат

Доля ложных срабатываний упала с 71% до 9%, и отчёт снова стали читать целиком, а не выборочно. Время проверки изменений сократилось с 45 минут до 7, при этом ни одна критичная находка не потерялась: их ловят на этапе проверки, а не на проде. Через несколько недель команда перестала спорить о шуме и занялась развитием правил — новый набор под сервис добавляется за один проход.

Чем штатные правила отличаются от собственных и когда нужны оба

Готовый набор Semgrep закрывает типовые классы уязвимостей: инъекции, слабую криптографию, секреты в репозитории, опасные вызовы популярных библиотек. Находки появляются с первого прогона — правила опираются на общие сигнатуры языка, а не на структуру вашего кода.

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

Набор Когда подходит Ограничения
Штатный Первый прогон, внешние зависимости, типовые ошибки Не видит внутренних соглашений, шумит на безопасных для вас конструкциях
Собственный Свой слой доступа к данным, обязательные проверки прав Требует сопровождения: после рефакторинга шаблоны надо перепроверять
Оба вместе Продукт с собственной архитектурой и внешними зависимостями Нужна приоритизация, иначе поток находок смешивается и разбор растягивается

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

Граница проходит по простому вопросу: сработает ли правило в незнакомом проекте на том же языке? Если да — это штатный набор; если оно опирается на ваши модули — собственный. Оба вместе закрывают и общий фон, и специфику архитектуры проекта.

Ограничения и типовые вопросы перед внедрением

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

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

Ложные срабатывания парализуют работу команды?

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

Что будет при обновлении правил или движка?

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

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

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

С чего начнём работу по вашему репозиторию

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

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

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

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

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

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