DarkRiDDeR13 мин

Отключение правила: как сделать явное решение, ограничение и rollback

БезопасностьПроцессы

Когда статическое правило мешает выпуску, решение «выключить его» кажется самым коротким. В нём нет owner, scope, причины, срока и способа вернуться назад. Проблема не в том, что любое исключение плохо. Проблема в том, что глобальное отключение меняет обработку будущих сигналов, а команда обычно обсуждает один конкретный результат в одном изменении.

Цена неявного выключения двойная. Опасная категория — построение команды из недоверенного значения — исчезает из наблюдаемой поверхности даже там, где контекст другого компонента ещё не изучен. Одновременно исчезает след решения: через месяц невозможно понять, почему правило стало тише и кто проверял границу. Альтернатива не требует тяжёлого процесса: три разрешённых действия, небольшой evidence record и rollback, который возвращает предыдущую видимость правила, а не обещает отменить уже выпущенный код.

Три решения вместо одной кнопки

Первое решение — keep: result остаётся видимым, пока владелец проверяет контекст. Оно подходит, когда есть signal, но нет основания менять rule или исключать результат. Второе — tune: команда меняет правило, потому что обнаружила слишком широкую или неверную гипотезу. Такое изменение обязано иметь новую rule revision, ясный diff и review того, какие формы теперь перестанут совпадать. Третье — suppress: временно ограничивается обработка одного идентифицируемого результата, но само правило сохраняется.

Глобальное disable сознательно не входит в этот набор для учебной модели. Это не универсальный запрет на все проекты: иногда policy меняется широко и осознанно. Но тогда это уже отдельное решение уровня правила или набора правил — с владельцем policy, ожидаемым влиянием и самостоятельным review. Нельзя прятать такую смену под исключение одного fingerprint. В нашем fixture action disable-globally отвергается, чтобы API не поощрял неверный уровень действия.

Контракт явного решения
ActionМинимальный evidenceОбласть действияЧто остаётся неизвестнымRollback
keepowner, reason, reviewByresult остаётся видимымдостижимость и итог security reviewничего не применяется; повторить triage
tuneowner, reason, rule revision, diff summaryбудущие совпадения изменённой гипотезыэффект diff на реальном проектевернуть прежнюю rule revision отдельным diff
suppressowner, reason, fingerprint, scope, expiresOnодин конкретный resultбезопасность и влияние подавленияубрать scoped exception и повторить review
disable globallyне является исключением одного resultвсе текущие и будущие результатымасштаб потери сигнала без отдельной оценкитребует отдельной policy-процедуры

Evidence record должен быть коротким и достаточным

Для решения хватает пяти обязательных полей: owner, reason, reviewBy, action и связь с точным объектом. Для tune этим объектом служат новая rule revision и описание изменения; повторить текущую revision под видом tune нельзя. Для suppress — result fingerprint, scope exact-synthetic-fingerprint и expiresOn. Поле reason не должно быть «шум»: оно называет проверенную границу, например «synthetic exception демонстрирует ограниченное подавление». В настоящем проекте reason должен ссылаться на воспроизводимый context или задачу проверки, а не на авторитет автора комментария.

Дата пересмотра не делает исключение безопасным автоматически. Она лишь создаёт точку, в которой решение снова будет видно. Обе даты нужны в формате календарного дня, а reviewBy не может быть позже expiresOn: иначе обязательная проверка придёт уже после снятия исключения. Если к reviewBy не собраны данные, честный результат — продлить review с объяснением или вернуть result в обычный поток. Нельзя молча продлевать suppress в каждом PR: срок полезен только вместе с владельцем и входным evidence. Важно также различать reviewBy, то есть дату следующей проверки решения, и expiresOn, то есть предел действия scoped exception.

Учебный fixture: план решения без изменения конфигурации

Команда ниже строит synthetic SARIF result, добавляет synthetic context и планирует keep, tune или suppress. Она не меняет файл конфигурации, не загружает rule pack, не открывает pull request и не переключает CI. План содержит поле applied: not-applied-by-fixture; rollback возвращает только visible-in-synthetic-plan. Этим пример полезен для проверки полей, но его нельзя использовать как журнал фактического выпуска или как доказательство, что исключение было применено.

node web/scripts/upgrade-2023-05.mjs --verify-fixture

# Все URI, строка, fingerprint, правило и context ниже synthetic.
# Команда создаёт deterministic object только в памяти: не читает проект,
# не запускает Semgrep/другой scanner, не открывает сеть и не выводит CVE.
# PASS проверяет контракт SARIF 2.1.0, границы context и решения в учебном плане.
# PASS не доказывает finding, покрытие, достижимость, exploitability или безопасность.

Fixture проверяет несколько отрицательных случаев. Action disable-globally отклоняется. Suppress без точного synthetic fingerprint, scope и expiresOn отклоняется; календарная дата вроде 2023-02-30 и review после expiry тоже не проходят. Tune без revision и change summary либо с неизменённой revision отклоняется. Отдельная проверка удаляет trust boundary из context и получает context-incomplete. Это не тест реального правила: он детерминированно проверяет, что сама процедура не стирает нужные границы и не может назвать outcome безопасным.

Гейт решения для synthetic результата: после полного context выбираются keep, tune или suppress; suppress требует точный fingerprint и expiry; глобальное отключение выведено за пределы обычного решения; rollback возвращает прежнюю видимость учебного плана.
Схема отображает policy-контракт, а не конфигурацию CI или факт применения исключения. Она не содержит реальные findings, данные репозитория, метрики или сведения о безопасности выпуска.

Маршрут: симптом → причина → проверка → действие

  1. Симптом. Правило хотят убрать после одного или нескольких неудобных results. Остановите обсуждение на уровне конкретного ruleId и fingerprint, не на уровне общего раздражения.
  2. Причина. У исключения нет scope и срока, а policy всего правила смешана с обработкой одного участка. Будущие результаты получают непреднамеренно другой режим.
  3. Проверка. Сверьте rule revision, context record, owner и точный result. Для tune прочитайте diff и проверьте, что revision новая; для suppress убедитесь, что fingerprint относится к одному объекту, даты календарные, а reviewBy не позже expiry.
  4. Действие. Выберите keep, tune или scoped suppress. Запишите evidence в review и сформулируйте следующий технический шаг: проверить flow, обновить rule или снять исключение до срока.
  5. Проверка контракта. Выполните fixture. PASS должен показать, что global disable и unscoped suppress отклонены, а rollback ничего не применяет к scanner config.
  6. Rollback. Для реального изменения приготовьте обратный config diff, критерий запуска повторного анализа и owner. Не называйте rollback успешным, пока эти действия не выполнены в границах проекта.

Tune — это изменение гипотезы, а не фильтр шума

У rule есть цена сопровождения: чем точнее гипотеза, тем больше контекста она требует; чем шире, тем больше результатов отправляет в review. Tune отвечает на вопрос, какую форму команда хочет искать после изменения. Поэтому change summary должен быть техническим: «требуется явный marker boundary» или «исключён generated path при условии X». Формулировка «сделать правило тише» скрывает условие и не позволяет reviewer оценить trade-off.

После tune не стоит заявлять, что ложных результатов стало меньше или опасные случаи не потеряны, пока этого не доказали конкретной проверкой. Достаточно честного статуса: rule revision изменена, ожидаемая граница названа, реальный effect ещё надо посмотреть на выбранном наборе. Если такого набора нет, решение должно предусматривать следующий review, а не статистику из памяти. M6-голос здесь важнее уверенности: он удерживает связь между policy diff, evidence и ограничением метода.

Scoped suppress должен уметь исчезнуть

Временное suppress допустимо только на самом узком идентификаторе, который проект может проверить повторно. В fixture это synthetic fingerprint; в настоящем инструменте формат идентификатора и механизм подавления надо уточнять по версии и документации анализатора. Scope нужен, чтобы исключение не захватило соседние results, похожие по тексту. Expiry нужен, чтобы обсуждение вернулось в очередь. Owner нужен, чтобы задача не стала бесхозной. Ни одно из этих полей не делает сигнал ложным или безопасным; они ограничивают только политику обработки.

Rollback не равен удалению комментария. Для scoped exception rollback — удалить или изменить точное исключение, снова запустить согласованный анализ и посмотреть, какой result вернулся. Для tune rollback — вернуть предыдущую rule revision и проверить diff правила. Для широкой смены policy нужен отдельный план, потому что её blast radius выходит за один fingerprint. Учебная функция rollbackSyntheticRuleDecision() специально возвращает configEffect: not-applied-by-fixture, чтобы не скрывать этот разрыв между планом и применением.

Ограничения модели и следующий шаг

Модель не знает формат подавления вашего анализатора, доступы к CI, baseline, code ownership, регуляторные требования, язык кода, сроки релиза и фактическое число результатов. Она не заявляет, что Semgrep v1.20.0 или любой другой producer создаёт указанные fingerprints именно так. Исторические ссылки фиксируют только SARIF 2.1.0 как OASIS Standard от 27 марта 2020 года и наличие --sarif в исходнике Semgrep v1.20.0, опубликованного 28 апреля 2023 года.

Следующий шаг — оформить одну реальную decision record с минимальными полями и без секретных фрагментов кода. Если выбран keep, назначить метод проверки context. Если tune, подготовить небольшой rule diff и review его границы. Если suppress, указать самый узкий scope и дату возвращения. Если разговор всё-таки про global policy, вынести его из PR в отдельное решение с владельцем и ожидаемым воздействием. Так команда не притворяется, что noise исчез сам, и не превращает удобный выключатель в необратимую потерю сигнала.

Проверяемые источники