Статический анализ в CI полезен, когда он быстрый, понятный и не блокирует разработку из-за сотни старых находок. Semgrep подходит для этой роли: он ищет не по регулярным выражениям, а по синтаксическим шаблонам, не требует сборки проекта и позволяет писать свои правила на YAML. Ниже — практическая настройка для репозитория на Linux, без привязки к конкретному вендору.

Как работает Semgrep

Инструмент разбирает файл в синтаксическое дерево и сопоставляет его с шаблоном. Шаблон subprocess.call($CMD, shell=True) найдёт вызов независимо от переносов строк и имён переменных, а комментарий с похожим текстом не тронет. Метка $CMD — метапеременная, она захватывает произвольное выражение. Дополнительно доступен режим taint-анализа: источник, распространение и сток данных связываются в цепочку.

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

Установка и первый запуск

Для локальной работы удобен pipx: он изолирует инструмент от системного Python. В CI проще использовать официальный образ semgrep/semgrep, чтобы не настраивать окружение.

sudo apt install -y pipx
pipx install semgrep
semgrep --version
semgrep scan --config p/default --metrics off .

p/default — набор правил из реестра Semgrep. --config auto тоже работает, но отправляет метаданные о проекте и не даёт стабильного набора правил между запусками. Для воспроизводимости фиксируйте конкретные наборы и собственный файл правил в репозитории.

Свои правила

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

rules:
  - id: no-requests-verify-false
    languages: [python]
    severity: ERROR
    message: "Проверка TLS отключена. Укажите verify=True или путь к CA."
    patterns:
      - pattern-either:
          - pattern: requests.$METHOD(..., verify=False, ...)
          - pattern: $SESSION.$METHOD(..., verify=False, ...)
      - metavariable-regex:
          metavariable: $METHOD
          regex: ^(get|post|put|patch|delete|head|request)$
    metadata:
      cwe: "CWE-295: Improper Certificate Validation"

Полезно держать рядом тестовые примеры: файл с конструкцией, которая должна сработать, и файл с безопасным вариантом. Команда semgrep scan --test --config rules/ прогоняет такие примеры и показывает, совпали ли ожидания. Это дисциплинирует: правило без тестов быстро обрастает исключениями и перестаёт ловить то, ради чего его писали.

Проверьте синтаксис до запуска: semgrep scan --validate --config rules/. Ошибка в шаблоне обычно означает неполную конструкцию: для условия нужно писать if $X < 5: ..., а не фрагмент выражения. Тестируйте правило на файле с заведомо плохим и заведомо хорошим кодом — это дешевле, чем ловить ложные срабатывания в CI.

Запуск и форматы отчётов

Для ручной проверки хватает текстового вывода. Для CI нужен машинно-читаемый формат и код возврата, который остановит сборку. Флаг --error возвращает ненулевой код при находках, --sarif формирует отчёт для GitHub Code Scanning, а для GitLab есть отдельный формат --gitlab-sast.

semgrep scan --config p/default --config rules/ \
  --error --sarif --output semgrep.sarif --metrics off
semgrep scan --config rules/ --severity ERROR --json \
  --output semgrep.json --metrics off

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

Дифференциальный режим и подавление

В большом проекте старые находки не должны блокировать каждый merge request. --baseline-commit сравнивает текущее состояние с указанным коммитом и показывает только новые проблемы. В CI базовая точка — merge base ветки, а не случайный старый тег.

semgrep scan --config rules/ --baseline-commit origin/main --error
semgrep scan --config rules/ --disable-nosem --json --output all.json
# в Python-файле: # nosemgrep: no-requests-verify-false

nosemgrep отключает правило на конкретной строке или блоке. Всегда указывайте id правила и причину в комментарии рядом: через полгода никто не вспомнит, почему проверка была отключена. Периодически запускайте сканирование с --disable-nosem, чтобы увидеть накопленные исключения.

CI: минимальный рабочий пример

Ниже — job для GitLab CI. Он запускается в merge request и на основной ветке, сохраняет SARIF и не требует токена, если вы используете только локальные правила и публичные наборы. Для панели Semgrep AppSec Platform добавьте SEMGREP_APP_TOKEN в защищённые переменные проекта.

semgrep:
  image: semgrep/semgrep
  stage: test
  variables:
    SEMGREP_SEND_METRICS: "off"
  script:
    - semgrep scan --config p/default --config rules/
        --error --sarif --output semgrep.sarif
  artifacts:
    when: always
    paths: [semgrep.sarif]
  rules:
    - if: $CI_MERGE_REQUEST_IID
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Для GitHub Actions достаточно шага с docker run --rm -v "$PWD:/src" semgrep/semgrep semgrep scan ... и загрузки SARIF через github/codeql-action/upload-sarif. В обоих случаях кэшируйте зависимости проекта, если они нужны правилам, и ограничивайте глубину клона так, чтобы baseline-коммит был доступен.

Производительность и шум

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

  • Сканируйте только исходники: исключите vendor/, node_modules/, сборки и минифицированные файлы.
  • Ограничивайте размер файла через --max-target-bytes, чтобы не разбирать гигантские артефакты.
  • Не повышайте --jobs выше числа ядер: переподписка потоков замедляет анализ.
  • Разделите быстрый блокирующий набор и полный ночной прогон.
  • Для каждой находки назначьте владельца и срок разбора, иначе отчёт превратится в архив.

Чек-лист внедрения

  • Правила и версия Semgrep зафиксированы в репозитории.
  • Локальный запуск воспроизводит результат CI.
  • Блокируются только новые находки высокого уровня.
  • SARIF загружается в систему просмотра, а не теряется в логах.
  • Исключения nosemgrep имеют причину и срок пересмотра.
  • Есть отдельное расписание полного сканирования.

Итог

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

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте