Безопасность как код: как встроить политики в процесс разработки

Команда меняет код через пулл-реквест: обсуждает решение, проверяет diff, сохраняет историю и при необходимости откатывает изменение. Но правила безопасности нередко остаются изменяемым состоянием вне этого процесса.

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

Policy as Code переносит такие решения в Git. В SourceCraft режимы сканирования, исключения путей и подавления находок можно хранить в .sourcecraft/security/settings.yaml. Политика проходит обычное ревью и получает автора, обоснование и проверяемую область действия. Ценность здесь не в YAML, а в том, что конфигурация безопасности развивается вместе с кодом.

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

Политика становится частью изменения

Представим, что анализатор начал создавать «шум» в каталоге со сгенерированными файлами. Если исключить путь только в настройках продукта, разработчики не увидят это решение рядом с изменением структуры проекта. В репозитории тот же шаблон становится частью пулл-реквеста: его можно проверить, сузить или отклонить, а позднее — найти причину в истории.

Это не делает любое решение безопасным автоматически. Зато AppSec и разработчики обсуждают в ревью конкретный diff, а не абстрактную настройку. Политика становится таким же изменяемым артефактом, как конфигурация сборки или инфраструктуры.

Файл политики не включает сканирование сам по себе. Сначала для репозитория нужно включить сканирование безопасности. После этого settings.yaml определяет правила последующих запусков.

Чем управляет settings.yaml

Минимальный пример — в репозитории:

version: "0.0.0"

path_ignores:
  - assets/**

suppressions:
  rules: []

settings:
  overallStatus: ENABLED
  prScanStatus: ENABLED
  uploadStatus: ENABLED
  backgroundScanStatus: ENABLED
  engineTypeStatuses:
    SCA: ENABLED
    SAST: ENABLED
    SECRETS: ENABLED
    SBOM_SPDX_GEN: ENABLED
    SBOM_CYCLONEDX_GEN: ENABLED

version фиксирует формат политики. path_ignores задаёт пути, которые не нужно анализировать, suppressions.rules — точечные исключения для находок, а settings — режимы запуска и типы анализаторов. Пустой rules: [] означает только отсутствие suppressions: assets/** и переключатели продолжают влиять на последующие сканирования.

settings.yaml не управляет политикой лицензий зависимостей: для неё используется отдельный файл .sourcecraft/security/licenses.yaml.

У переключателей разные роли:

  • overallStatus управляет политикой целиком;
  • prScanStatus, uploadStatus и backgroundScanStatus — соответствующими режимами;
  • engineTypeStatuses выбирает SCA, SAST, поиск секретов и генерацию SPDX- или CycloneDX-SBOM.

Для каждого поля используются значения ENABLED и DISABLED.

Для конкретного результата одновременно должны быть включены общий статус, соответствующий режим запуска и тип анализатора. Логика — «И». Например, для SAST в пулл-реквесте нужны settings.overallStatus, settings.prScanStatus и settings.engineTypeStatuses.SAST. Если любой из переключателей выключен, такой результат не появится.

Исключение «шума» без создания слепых зон

path_ignores использует glob-шаблоны относительно корня репозитория. Поэтому assets/** соответствует assets/img.png, а не пути внутри .sourcecraft/security. Glob учитывается для результатов, у которых доступен путь к файлу, на данные без такого пути он не распространяется. Чем шире шаблон, тем больше риск исключить из анализа не только «шум». Например, ** может создать слепую зону во всём дереве репозитория.

Точечное suppression сначала сопоставляет ruleName и engine с данными находки — точно и с учётом регистра. Поле findings позволяет дополнительно задать RE2-выражения для обнаруженного значения. Если выражений несколько, действует логика «ИЛИ». Если findings отсутствует, правило подавляет все находки, совпавшие по паре ruleName и engine.

Идентификаторы нельзя подбирать по памяти: их нужно взять из фактической находки. Например, gitleaks и Gitleaks считаются разными значениями. RE2-шаблон позволяет ограничить исключение конкретной тестовой строкой вместо всего класса результатов. Чем меньше область совпадения, тем проще ревьюеру проверить принятое решение и тем ниже риск скрыть будущую реальную проблему.

В reason стоит записать проверяемое объяснение и контекст согласования. При совпадении правила результаты последующих сканирований не создают новые находки, но уже существующие findings не пересчитываются. И само suppression не исправляет проблему: действительный секрет сначала нужно отозвать и перевыпустить, как рекомендуется в документации SourceCraft.

Как политика применяется и проверяется

SourceCraft выбирает один ближайший файл целиком. Приоритет — политика репозитория, затем организации, затем глобальная политика платформы. Поля разных уровней не объединяются: репозиторный settings.yaml не дополняет организационный, а заменяет его. Поэтому все необходимые для репозитория режимы и анализаторы нужно указать явно.

Изменение в feature-ветке можно обсудить и проверить, но активной становится версия из default-ветки. Это отделяет ревью политики от её применения.

Политика начинает действовать после попадания валидной версии в default-ветку и применяется только к последующим сканированиям. Старые запуски и находки не пересчитываются. Если обновлённый файл невалиден, SourceCraft оставляет активной последнюю валидную политику и сообщает об ошибке конфигурации. Новая версия не применяется частично.

Поэтому успешный merge сам по себе не доказывает, что изменённая политика активна: конфигурационную ошибку нужно проверить отдельно, а результат подтвердить новым сканированием.

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

Если вы проверяете suppression, используйте тестовую находку, которая совпадает с ruleName, engine и, если задано, одним из выражений findings. Затем убедитесь, что новое совпадающее событие не создало находку, а другие результаты остались видимыми. Такая проверка отличает реально работающую политику от YAML, который просто успешно прошёл ревью.

Начинать лучше с одного репозитория и пустого списка suppressions. Следующее исключение стоит добавлять отдельным пулл-реквестом с фактическими ruleName и engine, узким RE2-выражением и понятной причиной. Тогда ревью обсуждает конкретное принятие риска, а не абстрактную возможность что-то скрыть.

Policy as Code не заменяет AppSec-экспертизу. Он делает правила безопасности частью процесса, которым команда уже управляет: изменения видны в diff, причины остаются в истории, а границы исключений можно проверить. Посмотрите репозиторий-пример и начните с минимальной политики без suppressions.

Безопасность как код: как встроить политики в процесс разработки
Войдите, чтобы сохранить пост