Semgrep появился в моей практике года три назад. Тогда мы искали замену SonarQube — он работал, но жутко тормозил на больших репозиториях и постоянно выдавал какой-то бред в алёртах. Коллега сказал: «Попробуй Semgrep, он быстрый». Попробовал. И знаете, с тех пор отношение к SAST-инструментам у меня изменилось.
В этой статье разберём, что умеет Semgrep, где он хорош, где не очень, и когда имеет смысл смотреть в сторону альтернатив. Статья построена на реальном опыте внедрения — и моих ошибок в том числе.
Что такое Semgrep и почему о нём все говорят
Semgrep — это статический анализатор кода с открытым исходным кодом. Название расшифровывается как «semantic grep»: инструмент ищет паттерны в коде, но не просто как grep по тексту, а с пониманием синтаксиса языка.
Написан на OCaml (да, это редкость), развивается компанией Semgrep Inc. Есть бесплатная версия и платные тарифы с дополнительными фичами.
Главная фишка: semgrep rules пишутся на простом YAML-подобном DSL. Не нужно знать абстрактные синтаксические деревья, не нужно писать плагины на Java. Написал правило — запустил — получил результат.
Базовый пример правила:
rules:
- id: detect-hardcoded-password
pattern: password = "..."
message: "Hardcoded password detected"
languages: [python]
severity: ERROR
Это правило найдёт любую строку вида password = "..." в Python-коде. Просто? Да. Но за простотой скрывается мощь.
Semgrep понимает:
- Импорты и алиасы (
import requests as r— он знает, чтоrэтоrequests) - Наследование классов
- Область видимости переменных
- Вызовы методов и функций
Короче, он не просто ищет текст. Он анализирует структуру.
Как Semgrep работает: взгляд под капот
На одном проекте мы внедряли Semgrep для проверки security-уязвимостей в Python-сервисе. Было около 50 тысяч строк кода. Первый запуск занял 12 секунд. SonarQube на том же проекте работал 8 минут. Разница ощутимая, правда?
Скорость достигается за счёт того, что Semgrep не строит полный граф потока данных (по крайней мере в бесплатной версии). Он матчить паттерны по AST. Это накладывает ограничения, о которых поговорим позже.
Типичный workflow выглядит так:
# Установка
pip install semgrep
# Запуск с готовыми правилами
semgrep --config=auto .
# Или с конкретным набором
semgrep --config=p/security-audit --config=p/secrets .
Параметр --config=auto подтягивает правила из реестра Semgrep на основе языков в проекте. Удобно для быстрого старта.
Но реальные преимущества раскрываются, когда пишешь свои semgrep rules. Вот пример из практики — мы искали потенциально опасное использование eval():
rules:
- id: dangerous-eval
patterns:
- pattern: eval($EXPR)
- pattern-not: eval("...")
message: "eval() with dynamic expression — potential code injection"
languages: [python]
severity: ERROR
Здесь мы ищем все eval(), но исключаем случаи с литеральной строкой — они обычно безопасны (хотя тоже спорно). Таких нюансов много, и они делают правила точными.
Плюсы Semgrep: что реально работает
За годы использования я выделил для себя главные преимущества.
Скорость. Уже говорил, но повторю. Semgrep быстрый. Очень быстрый. На проекте в 200 тысяч строк он отрабатывает за 30-40 секунд. Это позволяет запускать его на каждый коммит в CI, не превращая пайплайн в пытку.
Понятные правила. После SonarQube, где написание кастомных правил требовало изучения Java API и внутренней архитектуры, Semgrep показался глотком свежего воздуха. Новое правило можно написать за 10-15 минут. README с примерами — и ты в деле.
Open-source ядро. Базовая функциональность бесплатна и доступна. Можешь запустить на своём сервере, можешь встроить в любой CI/CD. Никакой привязки к облачному сервису.
Хорошая база готовых правил. В реестре Semgrep тысячи правил для разных языков и фреймворков. Django, Flask, React, Spring — для популярного стека почти всё уже написано. Можно не париться.
Поддержка множества языков. Официально: Python, JavaScript, TypeScript, Java, Go, Ruby, PHP, C, C++, C#, Rust, Scala, Swift, Kotlin. Ещё есть экспериментальная поддержка для других. Для polyglot-проектов это критично.
Честно? Большинство команд остановились бы на этом списке и были бы довольны. Но есть и обратная сторона.
Минусы Semgrep: где он подводит
Никакой инструмент не идеален. Semgrep — не исключение.
Ложные срабатывания. Они есть. Иногда много. Проблема всех статических анализаторов: инструмент видит потенциальную проблему, но не понимает контекст. Вот пример, который бесил нас на одном проекте:
# Semgrep ругался на это как на potential SQL injection
query = f"SELECT * FROM users WHERE id = {user_id}"
Формально — да, это плохо. Но если user_id приходит из доверенного источника (например, внутренний конфиг), то проблемы нет. Semgrep этого не знает.
Решение — писать исключения в правилах или использовать nosemgrep-комментарии. Но это увеличивает шум и снижает ценность анализа.
Ограниченная глубина анализа. Бесплатная версия Semgrep не умеет межпроцедурный анализ в полном объёме. Она не отследит, что переменная x в функции foo() потом попадает в eval() в функции bar(). Для этого нужна платная версия с Semgrep Pro Engine.
Цена платной версии? Не буду называть точные цифры (они меняются), но для небольшой команды это ощутимые деньги. Если сравнивать с зарплатами разработчиков — может и окупиться. Но для стартапа или индивида — дорого.
Нет понимания бизнес-логики. Это критично. Semgrep найдёт eval(), найдёт hardcoded secrets, найдёт слабую криптографию. Но он не скажет, что ваша архитектура уязвима, или что вы неправильно используете фреймворк.
Интеграция не из коробки. Чтобы Semgrep оставлял инлайн-комментарии в Merge Request, нужно настраивать CI/CD, генерировать отчёты в нужном формате, интегрировать с GitLab/GitHub API. Не rocket science, но времени уйдёт.
Semgrep rules: как писать свои правила
Вот здесь Semgrep shines. Написание правил — это отдельный навык, но порог входа низкий.
Структура правила:
rules:
- id: unique-rule-id
pattern: source code pattern to match
message: "What to tell the developer"
languages: [list, of, languages]
severity: WARNING # или ERROR, INFO
Но реальные правила сложнее. Используются комбинации:
rules:
- id: jwt-hardcoded-secret
patterns:
- pattern: jwt.encode($PAYLOAD, $SECRET, ...)
- pattern-not: jwt.encode($PAYLOAD, "...", ...)
message: "JWT secret should not be hardcoded"
languages: [python]
severity: ERROR
metadata:
category: security
cwe: "CWE-798"
Паттерны комбинируются через:
patterns— все условия должны выполнитьсяpattern-either— хотя бы одноpattern-not— исключениеpattern-inside— матчить только внутри определённого контекста
Полный список операторов в документации внушительный. Но для 80% случаев хватит базового набора.
Совет: тестируйте правила на реальном коде. Semgrep Playground (semgrep.dev/playground) — отличный инструмент для отладки. Пишешь правило, загружаешь код, видишь результат в реальном времени.
Сравнение Semgrep с альтернативами
Не буду делать вид, что Semgrep — единственный вариант. Рынок статического анализа живёт и развивается.
Для наглядности — сравнительная таблица:
| Критерий | Semgrep (free) | Semgrep (Pro) | SonarQube | CodeQL | Distiq |
|---|---|---|---|---|---|
| Скорость на 100k LOC | ~20 сек | ~20 сек | 5-10 мин | 2-5 мин | ~30 сек |
| Свой сервер | Да | Частично | Да | Да | Нет |
| Написание правил | Простое YAML | Простое YAML | Сложное (Java) | Сложное (QL) | Не нужно |
| AI-анализ | Нет | Частично | Нет | Нет | Да |
| Инлайн в MR/PR | Через CI | Из коробки | Через CI | Через CI | Из коробки |
| Ложные срабатывания | Средне | Мало | Много | Средне | Мало |
| Цена | Бесплатно | $$$ | $$-$$$ | Бесплатно | $ |
SonarQube — классика. Если нужен один инструмент для всего (quality + security + coverage), это выбор многих команд. Но он тяжёлый, медленный, и писать свои правила — боль.
CodeQL от GitHub — мощный инструмент, но QL (Query Language) — это отдельный язык, который нужно учить. Для security-исследователей отлично, для обычной команды разработки — overkill.
Для российских компаний сейчас важен ещё один фактор: где хранятся данные. Semgrep Inc — американская компания. Бесплатная версия работает локально, но платные фичи требуют облачной обработки.
Когда Semgrep подходит, а когда нет
По моему опыту, Semgrep отлично работает для:
- Security-first проектов. Банковский сектор, fintech, healthcare. Где критично найти уязвимости до продакшена.
- Команд с сильными разработчиками. Если команда готова писать и поддерживать свои правила, Semgrep окупится сполна.
- Microservice-архитектуры. Быстрый анализ позволяет проверять каждый сервис на каждый MR.
- Open-source проектов. Semgrep бесплатно работает на публичных репозиториях.
Не так хорош для:
- Маленьких команд без security-экспертизы. Настройка и поддержка правил требует времени и знаний.
- Legacy-проектов. Количество ложных срабатываний будет зашкаливать. Команда быстро устранит игнорировать алёрты.
- Проектов с нестандартным стеком. Если используете редкий язык или фреймворк, готовых правил не будет.
На одном проекте мы месяц настраивали Semgrep, писали правила, чистили ложные срабатывания. Через полгода команда сказала: «Это лучшее, что мы внедряли». На другом — через две недели отключили, потому что «слишком много шума, нет времени разбираться». Разница была не в инструменте, а в готовности команды.
Практические советы по внедрению
Если решились на Semgrep, вот что рекомендую:
Начинайте с готовых правил. Не пытайтесь сразу написать свои. Запустите --config=auto, посмотрите, что найдётся. Отключите то, что не актуально.
Настраивайте постепенно. Включите правила уровня ERROR, потом WARNING. Не включайте INFO на старте — утонете.
Интегрируйте в CI, но не блокируйте. Сначала пусть Semgrep просто сообщает о проблемах. Через месяц-два, когда команда привыкнет, можно добавить блокировку для критичных правил.
Заведите процесс для ложных срабатываний. Кто-то должен регулярно просматривать их и либо исправлять правила, либо добавлять исключения. Иначе инструмент превратится в шум.
Рассмотрите гибридный подход. Semgrep для well-defined паттернов + AI-анализ для контекстных проблем. Это даёт лучший результат.
Российская альтернатива: Distiq
Скажу прямо: я работаю с командой Distiq, поэтому буду объективен, но с оговоркой о заинтересованности.
Distiq — это AI-бот для code review, который работает в GitLab, GitHub и GitVerse. Ключевое отличие от Semgrep: вместо pattern-matching используется большая языковая модель, которая понимает контекст.
Что это даёт на практике:
- Меньше ложных срабатываний — AI понимает, что
password = os.environ['PASSWORD']это нормально, аpassword = "admin123"— нет - Инлайн-комментарии в MR/PR из коробки — не нужно настраивать CI/CD
- Понимание бизнес-логики — AI видит архитектурные проблемы, а не только паттерны
- Серверы в РФ — для компаний с требованиями по локализации данных
Сравнивать напрямую Semgrep и Distiq не совсем корректно — это разные подходы. Semgrep — rule-based, детерминированный, быстрый. Distiq — AI-based, контекстный, чуть медленнее на больших MR.
Если бюджет позволяет, я бы рекомендовал использовать оба: Semgrep для базовых паттернов (он быстрее и дешевле на больших объёмах), Distiq — для качественного review каждого MR.
Distiq интегрируется за пару минут — добавляете webhook в репозиторий, и бот начинает комментировать merge request'ы. Пример комментария:
Потенциальная SQL-инъекция в строке 42.
Параметр user_id не экранируется перед
подстановкой в запрос. Рассмотрите использование
параметризованных запросов:
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
Это не просто «найдена проблема», а конкретное предложение по исправлению.
Semgrep — отличный инструмент, который заслуженно популярен. Он быстрый, гибкий, с активным сообществом. Но он не серебряная пуля. Выбирайте инструмент под задачу, а не задачу под инструмент.
Если нужен детерминированный SAST для поиска известных паттернов — Semgrep отличный выбор. Если хотите контекстный анализ с минимальной настройкой — посмотрите в сторону AI-решений. В идеале — комбинация обоих подходов.
