Справочник

CI/CD и автоматизация

Как запускать SAST, DAST и сканирование инфраструктуры из конвейеров CI/CD и скриптов и останавливать сборку, если найдены уязвимости.

scansuite-ci запускает сканирование ScanSuite из задания CI, скрипта или терминала, ждёт его окончания и завершает задание с ошибкой, если найдены подтверждённые уязвимости или секреты. Клиент умеет запускать статический анализ кода (по умолчанию), динамическое сканирование работающего веб-приложения и сканирование инфраструктуры — хостов, сетей и образов контейнеров. Сканирует сервер ScanSuite, поэтому раннеру нужен только Python 3.8 или новее либо образ контейнера с клиентом.

ScanSuite Teams

Клиент обращается к API команды, а оно есть только в ScanSuite Teams.

Подготовка

  1. 01
    Заведите сервисный аккаунт

    На странице «Команды»: вкладка «Люди», карточка «Автоматизация и API-токены». Назначьте ему роль «Оператор» и имя, например ci.

  2. 02
    Выпустите токен

    Нажмите «Новый токен», оставьте набор «Конвейер CI» и задайте срок действия до 90 дней. Скопируйте токен: он показывается только один раз.

  3. 03
    Добавьте переменные в систему CI

    SCANSUITE_TOKEN — в маскированную (секретную) переменную, а SCANSUITE_URL, SCANSUITE_TEAM (слаг команды) и SCANSUITE_PRODUCT (точное название продукта) или SCANSUITE_PRODUCT_ID — в обычные.

Перед запуском клиент проверяет, хватает ли токену прав, и называет недостающие (код выхода 3). Когда до истечения срока токена остаётся меньше 14 дней, в журнале задания появляется предупреждение. Ключ --token-file ФАЙЛ читает токен из файла, а не из переменной окружения.

Как получить клиент

Скачайте его со своего сервера ScanSuite — там всегда лежит подходящая серверу версия:

bash
curl -fsSO https://scansuite.example.com/ci/scansuite-ci.py
curl -fsS https://scansuite.example.com/ci/scansuite-ci.py.sha256 | sha256sum -c -

Или возьмите образ контейнера appsec4u/scansuite-ci:1: в нём клиент запускается командой scansuite-ci, а Git уже установлен. Ключ --list-scanners показывает, какие сканеры есть на вашем сервере для каждого вида сканирования. Если на сервере есть версия клиента новее, он сообщит об этом при запуске (отключается ключом --no-version-check).

Продукты

Результаты каждого сканирования попадают в один из продуктов команды. Укажите его точное название ключом --product-name (SCANSUITE_PRODUCT) или номер ключом --product-id (SCANSUITE_PRODUCT_ID). Если продукта с таким названием нет, клиент завершается с кодом 3.

С ключом --create-product (SCANSUITE_CREATE_PRODUCT=1) продукт создаётся при первом запуске, а следующие запуски находят его сами — для нового репозитория или сервиса ничего не нужно готовить в ScanSuite заранее. Токену для этого нужно право Создавать и изменять продукты, оно входит в набор «Конвейер CI». Если команда работает с DefectDojo, продукт заводится и там, как при создании из консоли; если DefectDojo недоступен, продукт всё равно появится в ScanSuite, а в журнале будет предупреждение. Чтобы у каждого сервиса монорепозитория был свой продукт, передайте каждому заданию его каталог и продукт: --source-dir services/payments --product-name payments --create-product.

Что и когда запускать

СобытиеЧто запускать
Merge request или pull request--profile quick-classic --changed-only (без AI) или quick-ai: только изменённые файлы, несколько минут.
Слияние в основную ветку--profile standard-ai, а без AI — standard-classic.
Раз в сутки или раз в неделю--profile full-ai, а без AI — full-classic.
После развёртывания на тестовый стенд--scan-type dast по развёрнутому приложению.
Релиз или изменения на серверах--scan-type infra по серверам, на которых работает релиз.

Статический анализ

Наборов шесть: три без AI и три с AI.

--profileСканеры
quick-classicСигнатурный Semgrep и поиск секретов. Несколько минут.
standard-classicSemgrep с полным локальным набором правил, поиск секретов и проверка зависимостей.
full-classicВсе классические сканеры: Semgrep (полный, быстрый и собственные правила команды), CodeQL и штатный анализатор каждого языка в репозитории, KICS для IaC, поиск секретов и проверка зависимостей.
quick-aiAI Native SAST без проверки достижимости и истории Git, поиск секретов с AI-проверкой.
standard-ai (по умолчанию)AI Native SAST с проверкой достижимости, поиск секретов с AI-проверкой и проверка зависимостей с AI-анализом достижимости.
full-aiAI Native SAST со всеми возможностями AI (достижимость, анализ архитектуры, межфайловый поиск и история Git), поиск секретов с AI-проверкой и проверка зависимостей с AI-анализом достижимости. Работает дольше всех и расходует больше всего AI.

Наборы с AI сообщают только о секретах, которые подтвердила AI-проверка; если для каких-то секретов проверка не сработала, клиент предупреждает, сколько их. full-ai анализирует историю Git, поэтому в конвейере нужна полная история (GitLab GIT_DEPTH: 0, GitHub fetch-depth: 0). Прежние названия работают и дальше: quick — это quick-classic, standard — standard-ai, deep — full-ai.

Ключ --scanners задаёт сканеры вместо тех, что в наборе, --add-scanners дополняет набор (например, --add-scanners snyk — для Snyk на сервере нужен его токен), а --no-ai-verification отключает AI-проверку. Ключ --list-scanners выводит наборы и все сканеры, которые есть на вашем сервере, и отмечает те, что для вашей команды запустить нельзя (OpenVAS без учётных данных, AI-сканеры без провайдера AI); если выбрать такой сканер, клиент завершится с кодом 3 ещё до начала сканирования.

С ключом --changed-only проверяются только файлы, изменённые относительно целевой ветки. В конвейерах merge request и pull request целевую ветку сообщает система CI, в остальных случаях её указывают ключом --base. Поиску секретов и проверке зависимостей нужен весь репозиторий, поэтому в этом режиме они не запускаются. Если изменений нет, задание завершается успешно.

По умолчанию раннер отправляет на сервер рабочую копию архивом: только отслеживаемые файлы из рабочего каталога CI или из --source-dir КАТАЛОГ, лишнее можно исключить ключом --exclude GLOB. Так сканируется ровно тот коммит, на котором запущен конвейер, серверу не нужен доступ к закрытому репозиторию, а ссылки в находках всё равно ведут на файлы этого коммита в вашем репозитории (адрес берётся из переменных CI или из --repository-url). С ключами --source git --git-url URL сервер клонирует репозиторий сам — закрытые репозитории по SSH ключом команды (настройки команды, вкладка «Сканирование», Ключ SSH для клонирования). Учётные данные в адресе указывать нельзя, ветку задаёт ключ --branch. --mode incremental (только для Git) сканирует лишь то, что изменилось в ветке с прошлого сканирования.

С ключами --mode custom-scope --scope ШАБЛОН (можно повторять или перечислить шаблоны в файле --scope-file ФАЙЛ) сканируются только подходящие файлы; сканеры, которые нельзя ограничить отдельными файлами, пропускаются с пометкой в журнале. Ключ --lang ЯЗЫК задаёт основной язык вместо выбранного набором, а --git-history добавляет к AI Native SAST анализ истории Git (тогда в архив попадает и .git).

Сканирование веб-приложений и инфраструктуры

Цели задаются ключом --target (его можно указать несколько раз или перечислить цели через запятую) либо переменной SCANSUITE_TARGETS. Каждая цель должна быть в списке разрешённых целей команды (страница «Команды», вкладка «Сканирование»), иначе клиент завершится с кодом 3 и сканирование не начнётся.

DAST-сканирование тестового стенда
python scansuite-ci.py --scan-type dast --target https://staging.example.com \
  --profile standard --header "Authorization: Bearer $STAGING_TOKEN" --junit scansuite-dast.xml
--profile с --scan-type dastСканеры
quickБыстрое веб-сканирование и определение технологий.
standard (по умолчанию)Быстрое веб-сканирование, Nuclei, определение технологий и поиск скрытых путей.
deepСбалансированное веб-сканирование, Nuclei, определение технологий, поиск скрытых путей и AI DAST.

Чтобы сканировать закрытую часть приложения, передайте заголовки ключом --header "Имя: значение" (несколько — через |) или cookie ключом --cookie "ключ=значение" (несколько — через ;). Ключом --instructions ТЕКСТ можно подсказать AI-пентестеру (ai_pentest, для веб-приложений и инфраструктуры), на чём сосредоточиться.

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

Сканирование инфраструктуры
# Два хоста, все TCP-порты, даже если хосты не отвечают на ping
python scansuite-ci.py --scan-type infra --scanners openvas,nuclei \
  --target 10.0.4.10,10.0.4.11 --no-ping --fail-on-severity critical

# Образ контейнера
python scansuite-ci.py --scan-type infra --scanners docker_image_scan \
  --target registry.example.com/shop/api:1.4.2

--ports принимает All TCP (по умолчанию), Top 1000 TCP/UDP или список портов.

Когда сборка завершается с ошибкой

В расчёт идут уязвимости из этого сканирования в статусе Open или In Progress. Уязвимости, которые AI-проверка признала недостижимыми, не учитываются, если не добавить ключ --include-unreachable. Уязвимости, найденные AI, учитываются всегда, а результаты сканеров (Semgrep, KICS, Trivy, ZAP, Wapiti, Nuclei, OpenVAS и других) — тоже, если команда не выключила их сохранение (по умолчанию оно включено): страница «Команды», вкладка «Сканирование», карточка «Уязвимости сканеров на странице „Уязвимости“».

Если какой-то из сканеров не отработал, сборка завершается с кодом 2, даже когда больше ничто её не останавливает; уязвимости от отработавших сканеров по-прежнему дают код 1.

КлючЧто останавливает сборку
--fail-on-severity highСборку останавливают уязвимости этой критичности и выше. По умолчанию high; none отключает проверку.
--max high=0,medium=10Сколько уязвимостей каждой критичности допускается. Для перечисленных уровней действует вместо --fail-on-severity.
--block-class sql_injectionУязвимости этих классов останавливают сборку при любой критичности.
--min-confidence reachableСборку останавливают только уязвимости с подтверждённой достижимостью или известным эксплойтом.
--fail-on-secrets newСборку останавливают секреты, которых в продукте раньше не было (по умолчанию). all учитывает все секреты, none отключает проверку. Только для статического анализа.

У каждого ключа есть одноимённая переменная: SCANSUITE_FAIL_ON_SEVERITY, SCANSUITE_MAX, SCANSUITE_BLOCK_CLASS, SCANSUITE_MIN_CONFIDENCE, SCANSUITE_FAIL_ON_SECRETS.

Коды возврата

КодЗначение
0Проверки пройдены или сканировать было нечего.
1Проверки не пройдены: уязвимостей или секретов больше допустимого.
2Сканирование завершилось с ошибкой, отменено или отклонено сервером (например, исчерпан лимит сканирований команды) либо какой-то из его сканеров не отработал.
3Ошибка в настройках, токене или целях либо сертификат TLS не прошёл проверку.
4Истекло время ожидания (--timeout, по умолчанию 7200 секунд).
5ScanSuite недоступен или вернул ошибку.

Неуспешный запуск заканчивается строкой с кодом выхода и его расшифровкой, например [scansuite] Exit code 3: configuration error: option, token, permission, target or certificate. Некоторые системы CI, в том числе GitLab, показывают для любого упавшего задания код 1 — по этой строке видно, что именно случилось.

С ключом --soft-fail коды 2, 4 и 5 заменяются на 0 с предупреждением, чтобы сбой ScanSuite не задерживал релиз.

Отчёты

КлючЧто сохраняется
--junit ФАЙЛJUnit XML: GitLab и Jenkins покажут найденное как упавшие тесты.
--sarif ФАЙЛSARIF 2.1.0 для GitHub code scanning, Azure DevOps и IDE.
--summary-json ФАЙЛСведения о сканировании, итог проверок, уязвимости и секреты.
--report-zip ФАЙЛПолный отчёт в архиве.

В журнале задания перечисляются все блокирующие уязвимости и секреты вместе с правилом, которое их заблокировало; ключ --max-print N ограничивает их число (по умолчанию 50).

GitLab CI

Подключите шаблон, который раздаёт ваш сервер ScanSuite, и задайте переменные из раздела Подготовка в Settings → CI/CD → Variables. Переменную SCANSUITE_TOKEN отметьте как Masked, но не Protected: защищённые переменные не попадают в конвейеры merge request из незащищённых веток, и задание завершается с кодом 3.

yaml
include:
  - remote: 'https://scansuite.example.com/ci/scansuite.gitlab-ci.yml'

Шаблон добавляет два задания: scansuite-merge-request проверяет изменённые файлы в каждом merge request набором quick-classic, а scansuite-default-branch сканирует основную ветку набором standard-ai. Для собственных заданий унаследуйте скрытое задание .scansuite, например чтобы запускать DAST после развёртывания. Переменные проекта в GitLab важнее переменных задания, поэтому, чтобы отправить результаты одного задания в другой продукт, передайте --product-name в SCANSUITE_EXTRA_ARGS, а не задавайте SCANSUITE_PRODUCT в самом задании.

yaml
scansuite-dast:
  extends: .scansuite
  stage: verify
  needs: [deploy-staging]
  variables:
    SCANSUITE_SCAN_TYPE: dast
    SCANSUITE_TARGETS: https://staging.example.com

Без шаблона:

yaml
scansuite:
  stage: test
  image:
    name: appsec4u/scansuite-ci:1
    entrypoint: [""]
  script:
    - scansuite-ci --junit scansuite-junit.xml
  artifacts:
    when: always
    reports:
      junit: scansuite-junit.xml

Подключаете ScanSuite к GitLab впервые? В разделе Пошаговая настройка GitLab CI всё показано на скриншотах: от выпуска токена до merge request, который не удаётся слить из-за найденных уязвимостей. Там же готовые рецепты — DAST и сканирование образов, ночное сканирование, проверка релизов, монорепозитории. Эти рецепты есть и в примерах для GitLab.

Другие системы CI

В GitLab CI, GitHub Actions, Jenkins, Azure DevOps и Bitbucket Pipelines клиент сам берёт репозиторий, ветку, коммит и целевую ветку merge request из переменных окружения. В других системах передайте --base для --changed-only и уникальный для каждого запуска --idempotency-key, чтобы повторный запрос не запустил второе сканирование.

Ожидание и отмена

Клиент следит за сканированием до конца: проверяет состояние раз в 20 секунд (--poll-interval) и ждёт не дольше двух часов (--timeout, SCANSUITE_TIMEOUT; код выхода 4). С ключом --cancel-on-timeout он заодно отменяет сканирование, когда перестаёт ждать. С ключом --no-wait клиент только запускает сканирование и сразу завершается с кодом 0.

Если задание CI отменили, клиент отменяет и своё сканирование (токену нужно право Останавливать сканирования, оно входит в набор «Конвейер CI»); с ключом --no-cancel-on-interrupt сканирование продолжится. Запрос, повторённый после сетевой ошибки, подхватывает уже запущенное сканирование, а не начинает новое; если сервер перегружен, клиент повторяет запрос позже. Ключ --print-logs-on-failure выводит конец журнала сканирования, если оно завершилось с ошибкой.

Сертификаты TLS

Если сертификат сервера самоподписанный или выдан неизвестным раннеру центром сертификации, клиент выводит предупреждение и работает дальше без проверки. Чтобы сертификат проверялся, укажите сертификат центра ключом --ca-bundle; чтобы вместо этого клиент завершался с кодом 3, добавьте --strict-tls.

Проверено: 2026-10-03