SAST/DAST в CI без боли
Статический и динамический анализ в пайплайне: как не замедлить разработчиков
Два типа сканеров и почему вам нужны оба
SAST (Static Application Security Testing) анализирует исходный код без запуска приложения. Он видит паттерны — незаэскейпленный пользовательский ввод, SQL-конкатенации, захардкоженные пароли (об этом подробнее в главе про управление секретами) — и отмечает их как потенциальные уязвимости. DAST (Dynamic Application Security Testing) работает с запущенным приложением: отправляет HTTP-запросы с вредоносными payload’ами, проверяет заголовки безопасности, ищет открытые эндпоинты. SAST находит ошибки в коде, DAST находит ошибки в поведении.
Эти подходы дополняют друг друга. SAST ловит уязвимости, которые DAST никогда не увидит: например, race condition в обработке платежей или небезопасную десериализацию в глубине бизнес-логики. DAST ловит то, что SAST пропустит: misconfigured CORS-заголовки, отсутствие rate limiting, проблемы с аутентификацией, которые становятся видны только когда приложение работает целиком. Но каждый из них может стать источником боли для разработчиков, если внедрить его неправильно.
SAST: четыре инструмента, которые стоит знать
Semgrep задумывался как «grep для кода, который понимает синтаксис». Вы пишете правила на YAML, которые выглядят как код, который вы ищете. Semgrep находит совпадения с учётом структуры AST, а не текста. Сканирование занимает секунды на среднем репозитории, потому что Semgrep не строит полный граф зависимостей, а работает с паттернами. Открытый код, self-hosted, запускается локально без внешних зависимостей. В декабре 2024 года Semgrep перенёс несколько функций под коммерческую лицензию, и больше десяти вендоров в январе 2025 форкнули Semgrep CE в проект Opengrep. Для команд, которым нужен контроль над кодом инструмента, Opengrep может оказаться лучшим выбором.
SonarQube Community Edition стоит отдельного внимания. Открытый код, self-hosted, огромная база правил для десятков языков. Около 85% правил касаются качества кода (code smells, дублирование, сложность), и только 15% покрывают безопасность. Это одновременно сила и слабость: разработчики ценят SonarQube за чистый веб-интерфейс, интеграцию с IDE через SonarLint и quality gates в CI. Для серьёзного security-анализа одного SonarQube не хватит, но в связке с Semgrep получается мощная комбинация: SonarQube следит за качеством кода, Semgrep ловит уязвимости.
Trivy от Aqua Security закрывает другую плоскость: сканирование Docker-образов, зависимостей (npm, pip, go modules, Maven) и IaC-конфигураций (Terraform, Kubernetes manifests) на известные CVE. Trivy работает без сервера, запускается одной командой trivy image myapp:latest или trivy fs ., результаты появляются в терминале за секунды. Открытый код, self-hosted, активное сообщество. Если Semgrep и SonarQube ищут ошибки в вашем коде, Trivy ищет уязвимости в том, от чего ваш код зависит.
CodeQL от GitHub компилирует код в реляционную базу данных и позволяет писать запросы на языке QL, которые отслеживают поток данных через весь граф вызовов. CodeQL может найти уязвимость, где пользовательский ввод проходит через 15 вызовов функций, прежде чем попасть в SQL-запрос. За глубину анализа приходится платить временем: первичное сканирование занимает минуты, иногда десятки минут. CodeQL бесплатен для публичных репозиториев; для приватных он входит в GitHub Advanced Security, доступ к которому зависит от вашего тарифа и региона.
Проблема false positives
Исследование EASE 2024 показало, что CodeQL помечает 68% безопасных участков кода как потенциально уязвимые, Semgrep — 75%. Эти цифры означают, что из каждых четырёх findings три — ложные. Представьте себе разработчика, который открывает pull request и видит 12 security warnings, из которых 9 — мусор. Через неделю он перестаёт читать эти warnings вообще. Это alert fatigue, и это главная причина, по которой security-сканеры теряют доверие команды.
Решения существуют, и они все про настройку. Первое: начните с маленького набора высокоточных правил. Semgrep позволяет включать правила по одному и видеть, сколько findings каждое генерирует на вашей кодовой базе. Включите 10 правил для вашего основного языка, посмотрите на результаты, отключите те, что дают 80% false positives. Второе: используйте инкрементальное сканирование — проверяйте только изменённые файлы в PR, а полное сканирование запускайте по расписанию (раз в сутки, например). Это и быстрее, и генерирует findings только по свежему коду, за который разработчик отвечает прямо сейчас.
Третье — тюнинг под вашу кодовую базу. Если ваш фреймворк автоматически экранирует вывод в шаблонах (как Jinja2 с autoescape или React с JSX), все XSS-правила будут генерировать сплошные false positives. Отключите их или напишите кастомное правило, которое ловит только случаи с | safe или dangerouslySetInnerHTML.
DAST: когда и как запускать
DAST требует работающее приложение — и это его главная сложность в CI. Вам нужно развернуть приложение в тестовом окружении, дождаться, пока оно станет доступно, запустить сканер, дождаться результатов, потушить окружение. Полное DAST-сканирование через OWASP ZAP занимает от 15 минут до нескольких часов в зависимости от размера приложения, и это делает его непригодным для проверки каждого PR.
OWASP ZAP (с 2024 года — «ZAP by Checkmarx») предлагает два режима: baseline scan и full scan. Baseline scan проверяет заголовки безопасности, SSL-конфигурацию и самые базовые уязвимости за пару минут — его можно запускать на каждый PR с preview environment. Full scan с активным crawling и fuzzing — это задача для ночного пайплайна или еженедельного прогона.
Практичный подход: запускайте baseline DAST scan на каждый PR, если у вас есть автоматические preview environments (Vercel, Netlify, или собственное решение). Полный DAST-скан запускайте раз в неделю на staging и по расписанию перед релизами. Результаты полного сканирования отправляйте в Jira или GitHub Issues, назначая на security champion соответствующей команды.
Developer-friendly репортинг
Самый частый антипаттерн: security-сканер генерирует PDF-отчёт на 80 страниц, отправляет его на email техлиду, и на этом процесс заканчивается. Отчёт лежит непрочитанный, потому что формат неудобный, приоритеты непонятны, а связь между finding и конкретной строкой кода — размытая.
Findings должны приходить туда, где разработчик уже работает. Для SAST это комментарии в pull request: Semgrep умеет оставлять inline-комментарии через CI-интеграции, SonarQube делает то же через quality gates. Оба инструмента указывают на конкретную строку с проблемой и предлагают исправление в вашем CI/CD-пайплайне. Для DAST это тикеты в трекере с чёткой структурой: что найдено, где воспроизвести, какой severity, ссылка на OWASP или CWE с объяснением.
Severity тоже нужно калибровать. Не каждая SQL-инъекция одинаково опасна: инъекция в публичном API, доступном без аутентификации, — это P0. Инъекция во внутреннем admin-инструменте за VPN — это P2. Если ваш сканер ставит всему Critical, разработчики перестают различать настоящие проблемы и шум.
Рекомендации по внедрению
Начните с SAST в духе shift-left: его проще интегрировать, он не требует работающего приложения, результаты видны в PR. Semgrep хорош для старта: открытый код, self-hosted, быстрый, правила понятные, инкрементальное сканирование работает из коробки. Добавьте 5–10 правил для вашего основного стека, прогоните на кодовой базе, уберите шумные, задеплойте в CI. Параллельно поставьте SonarQube Community Edition для контроля качества кода и Trivy для сканирования зависимостей и Docker-образов. Все три инструмента работают на вашей инфраструктуре и не требуют внешних SaaS-зависимостей.
DAST добавляйте вторым этапом, когда у вас уже есть автоматические preview environments или стабильный staging. ZAP baseline scan — разумная отправная точка: бесплатный, хорошо документированный, Docker-образ ghcr.io/zaproxy/zaproxy:stable запускается одной командой.
Измеряйте две метрики: mean time to remediate (сколько дней от finding до исправления) и false positive rate (какой процент findings разработчики помечают как ложные). Первая показывает, работает ли ваш процесс. Вторая показывает, доверяют ли разработчики вашим инструментам.