Справочник

Сканирования из конвейера и порог сборки

Запуск сканирования из задания сборки по API-токену команды и остановка сборки по его результатам.

scansuite-ci.py запускает статический анализ из задания сборки, дожидается результата и роняет сборку, если сканирование подтвердило уязвимости или секреты. Положите скрипт в репозиторий, задайте три переменные и добавьте в конвейер одну строку. Скрипт входит по токену командного API, а не по паролю, и ему не нужно ничего, кроме Python 3.8 или новее.

ScanSuite Teams

Клиент для конвейеров работает через командный API, поэтому ему нужен ScanSuite Teams. В редакции без команд в конвейерах используются прежние скрипты, см. «Интеграция с CI/CD».

Первоначальная настройка

  1. 01
    Создайте продукт для результатов конвейера

    Запомните его ID или точное название.

  2. 02
    Создайте сервисную учётную запись

    В Team settings → Service accounts, с ролью operator. Назовите её, например, ci.

  3. 03
    Выпустите для неё токен

    Сроком не больше 90 дней и с правами из таблицы ниже. Токен показывается только один раз.

  4. 04
    Сохраните токен в системе CI

    В маскированной или секретной переменной SCANSUITE_TOKEN. SCANSUITE_URL и SCANSUITE_TEAM (слаг команды) задайте обычными переменными.

Право токенаДля чего
scan.execute, scan.read, finding.readОбязательны: запуск сканирования, отслеживание хода и чтение результатов.
product.readНужно, если продукт указан по названию, а не по идентификатору.
credential.readНужно для проверки секретов, которая включена по умолчанию.
scan.cancelНеобязательно. С этим правом отмена конвейера останавливает и его сканирование, иначе оно продолжит работать.
report.readНеобязательно, нужно для --report-zip.

Скрипт проверяет свои права до запуска и сразу называет недостающие, а не падает на полпути. Сервисные учётные записи и токены описаны в главе Команды и роли.

Откуда берётся код

ИсточникКак это работает
--source zip (по умолчанию)Раннер упаковывает рабочую копию и отправляет её на сервер. Из Git-репозитория берутся только отслеживаемые файлы (git ls-files), поэтому артефакты сборки в архив не попадают; лишнее исключите через --exclude GLOB. Закрытые репозитории лучше сканировать именно так: кроме архива, с раннера ничего не уходит.
--source git --git-url URLСервер сам клонирует репозиторий. URL и ветку по умолчанию скрипт берёт из данных системы CI. В HTTPS-адресе не должно быть учётных данных: скрипт и сервер отклоняют такие адреса, иначе учётные данные сохранились бы вместе со сканированием и попали в ссылки на уязвимости. Закрытые репозитории клонируются по SSH с ключом репозитория команды.

С --source git сервер сканирует последний коммит ветки на момент клонирования, а он может оказаться новее коммита, который запустил конвейер. С --source zip сканируется ровно то, что лежит на раннере.

--mode incremental (только для git) сканирует изменения с момента последнего подходящего сканирования. Если изменений нет, скрипт сообщает об этом и завершается с кодом 0. С --mode custom-scope --scope ШАБЛОН сканируются только выбранные файлы.

Порог качества

Значение по умолчаниюКогда сборка падает
--fail-on-severity highРоняет сборку, если сканирование обнаружило уязвимости с критичностью high и выше. Уязвимости, которые AI-проверка признала недостижимыми, не учитываются, пока не добавлен ключ --include-unreachable. Значение none отключает порог по критичности.
--fail-on-secrets newРоняет сборку, если этот запуск добавил в продукт новые секреты. Значение all учитывает все секреты продукта, none отключает проверку.

Если AI-проверка выключена, любой найденный секрет считается настоящим: его никто не оценивал. Поэтому без проверки порог по секретам становится строже, а не мягче.

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

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

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

Отчёты

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

API не отдаёт значение секрета, и ни в один из этих файлов оно не попадает. Конвейер видит детектор, место и короткий отпечаток, по которому секреты можно отличить друг от друга.

Задание в GitLab

yaml
scansuite:
  stage: test
  image: python:3.12-slim
  variables:
    SCANSUITE_URL: https://scansuite.example.com
    SCANSUITE_TEAM: appsec
    SCANSUITE_PRODUCT: my-service      # либо SCANSUITE_PRODUCT_ID
  script:
    - python scansuite-ci.py --junit scansuite-junit.xml --summary-json scansuite.json
  artifacts:
    when: always
    reports:
      junit: scansuite-junit.xml
    paths: [scansuite.json]

SCANSUITE_TOKEN задаётся в маскированных переменных проекта. В образах python:*-slim нет git. Без него скрипт не может получить список отслеживаемых файлов и обходит дерево каталогов со своими исключениями по умолчанию.

Повторы и прерывания

  • Повторный запуск упавшего задания не создаёт второе сканирование: скрипт узнаёт задание и подключается к уже запущенному сканированию.
  • При сбое сети или перегрузке сервера скрипт сам повторяет запрос.
  • Если у токена есть право scan.cancel, отмена конвейера отменяет и сканирование.
  • Добавьте --no-wait, если конвейер должен запустить сканирование и идти дальше, не дожидаясь результата.

Прежде чем переводить конвейер на --mode incremental, выполните одно полное сканирование: инкрементальное сравнивает код с последним подходящим сканированием, и без него сравнивать не с чем.

Проверено: 2026-09-20