- BrainTools - https://www.braintools.ru -
Поводом для этого проекта был не абстрактный интерес [1] к AI и не желание сделать ещё один инструмент для ревью.
На одном из рабочих проектов довольно быстро стало видно, что на pull request уже нельзя смотреть по старой модели. Команда начала двигаться в сторону AI-first разработки. В продукт стало прилетать больше изменений от людей с очень разной глубиной контекста: часть работала рядом с продуктом, часть приходила из смежных команд, часть собиралась с активной помощью AI. Скорость изменений выросла. А вот глубина понимания конкретной зоны у автора PR часто, наоборот, стала ниже.
В этот момент review начинает ломаться не на чтении синтаксиса. Оно зависает на другом. Не на вопросе “что делает эта строчка”, а на вопросах вроде:
это вообще корректное изменение для этой области кодовой базы?
мы точно хотим так менять auth-логику?
это правда просто чистка конфига?
почему один PR одновременно трогает workflow, права доступа и доменную логику [2]?
можно ли это мержить без подтверждения от человека, который действительно понимает этот кусок системы?
То есть проблема оказалась не в том, что код невозможно прочитать. Проблема в том, что слишком много изменений начали приносить люди, которые не обязаны хорошо понимать поведение [3] именно этого продукта, а ревьюить их всё равно должен кто-то внутри команды.
И вот здесь обычное review очень быстро упирается в предел. Если PR локальный, а автор хорошо понимает зону, review в основном сводится к проверке качества решения. Если PR приходит из AI-first потока, от соседней команды или от человека с поверхностным знанием домена, review превращается в другую задачу: нужно не просто прочитать diff, а восстановить, что именно это изменение делает с системой и можно ли его пропускать дальше.
В какой-то момент перестал устраивать привычный вопрос “что тут не так?”. Он слишком общий. Намного полезнее оказался другой: что команда должна сделать с этим PR прямо сейчас?
Так и начал собираться PRShield как отдельный рабочий MVP. Хотелось проверить, можно ли под эту задачу сделать не очередного review-бота, а отдельный слой принятия решения на этапе мержa.
В самом грубом виде ответов у него всего три:
SAFE
REVIEW_REQUIRED
BLOCK
Не “найти всё плохое”. Не “оставить побольше замечаний”. Не “сгенерировать summary по diff”. А помочь принять решение перед мержем.
Я не думаю, что code review “сломался”. Скорее он перестал быть только про качество конкретной реализации.
Когда изменение маленькое и локальное, всё более-менее привычно: читаешь diff, проверяешь логику, задаёшь пару вопросов, идёшь дальше. Но есть другой тип pull request. Там код может быть вполне нормальным, а вопрос всё равно остаётся. Причём вопрос не про баг, а про смысл изменения.
Например:
- if (!user.hasScope('payments:write')) return deny();
+ if (!user.isAuthenticated) return deny();
Формально всё хорошо. Код не стал “сломанный”. Но изменилось правило доступа. Раньше нужен был конкретный scope, теперь — просто authenticated user.
Или так:
- if (!request.IsInternalCall) return Forbid();
+ if (!request.HasValidCallbackSignature) return Forbid();
Снаружи это обычная замена одного if на другой. Но по сути меняется то, кого система вообще считает допустимым источником вызова.
Или ещё скучнее:
- failureMode: closed
+ failureMode: open
Такой diff отлично прячется под config cleanup. Только он меняет очень практичную вещь: если проверка не сработала, система закрывает путь или пропускает его дальше.
Или:
- validate_refund!(request)
+ process_refund(request)
Тут может не быть вообще никакой “ошибки по учебнику”. Но если это критичный для бизнеса сценарий, то вопрос уже не в синтаксисе. Вопрос в том, кто подтвердил, что именно так этот процесс теперь должен выполняться.
Вот этот класс PR и начал меня по-настоящему интересовать. Не как уязвимости или подозрительный код. А как изменения, где нужен процесс принятия взвешенного решения при слиянии веток.
Когда начинаешь обсуждать такие вещи, разговор почти автоматически утягивает в знакомую сторону:
SAST,
findings,
сигнатуры,
классы уязвимостей,
dangerous patterns.
И это понятно. Мы много лет привыкали именно к такому языку. Но проблема, из которой у меня вырос PRShield, лежит чуть в стороне.
SAST отвечает на очень полезный вопрос: что здесь потенциально плохо в коде. ASOC отвечает на другой: как жить с результатами разных инструментов, не потерять их и нормально триажить.
Но здесь быстро возникает и третий вопрос: можно ли этот набор изменений спокойно мержить без дополнительного подтверждения? Это уже не совсем про “найди проблему”. Это про контроль изменения поведения [4] системы в перед влитием изменений.
Например, правка lockfile, изменение workflow permissions, ослабление auth-правила или переход из fail-closed в fail-open не обязаны выглядеть как “вот готовая уязвимость”. Но это вполне может быть PR, который нельзя пропускать как рутинные изменения.
Мне кажется, это и есть главный сдвиг оптики: не “найти всё плохое”, а понять, что именно этот diff меняет в системе и можно ли с этим жить прямо сейчас.
Если упростить до одной строки, то разница для меня такая: SAST чаще отвечает на вопрос “что здесь может быть опасным?”
merge-time control пытается отвечать на вопрос “что workflow должен сделать с этим PR?”
Когда начал собираться этот MVP, сразу не хотелось сводить всё к схеме “берём diff, отправляем в LLM, спрашиваем, можно ли мержить”. Такой путь позволяет быстро сделать демо. Но не очень понятно, как потом этому доверять.
Поэтому PRShield почти сразу начал складываться как decision-centric pipeline:
integration-gateway
-> workflow-orchestrator
-> evidence-collector
-> analysis-core
-> ai-risk-engine
-> policy-engine
-> decision-engine
-> delivery-enforcement
-> run store
То есть этот рабочий MVP с самого начала собирался вокруг идеи, что в центре должен быть не “анализ как таковой”, а решение. Если инструмент умеет только сказать “это выглядит подозрительно”, он может быть полезен, но это ещё не отдельный слой контроля.
Если он умеет:
принять событие;
собрать контекст;
интерпретировать изменение;
наложить политики;
вынести вердикт;
встроить его в workflow;
тогда это уже начинает быть похоже на отдельный слой в процессе merge.
Здесь мне было важно, чтобы всё не сводилось к простому “модель посмотрела diff и что-то решила”.
Во-первых, в analysis-core есть детерминированный слой. Diff не летит в модель в сыром виде. Сначала из него собираются сигналы, которые описывают, что именно изменилось и в какой зоне.
Во-вторых, в проекте есть отдельный класс дифференциальных сигналов, которые смотрят не просто на новое состояние кода, а на пару до/после.
Проще говоря, система уже умеет отдельно выделять случаи, когда:
вместо конкретной проверки scope или роли остаётся более общее правило;
внутренний маршрут начинает жить по другой модели доверия;
поведение при сбое меняется с fail-closed на fail-open;
из чувствительного сценария убирают шаг валидации, но само действие остаётся.
Дальше уже начинается слой, который помогает не превратить всё это в “давайте просто скормим модели весь PR”.
В ai-risk-engine сначала:
ранжирует файлы;
выделяются подозрительные участки;
собирается краткая сводка по риску PR;
и только потом в модель уходит не бесконечный diff, а более компактное и структурированное представление изменения.
Мне нравится эта деталь просто потому, что она дисциплинирует весь разговор. PR начинает обсуждаться не как бесконечный diff, а как набор изменений с выделенными зонами риска.
Перед отправкой ещё и вычищаются чувствительные данные: ключи, токены, private key blocks и похожие вещи.
Это не то, чем обычно хочется хвастаться, но именно такие вещи помогают инструменту не выглядеть как игрушка.
Отдельно мне нравится, что в проекте есть защитный слой против слишком самоуверенных ответов модели. Если эвристический анализ говорит “здесь чувствительное изменение”, а модель почему-то отвечает слишком мягко, её уверенность можно понизить, а рекомендацию — ужесточить.
То есть LLM здесь не единственный голос. И если она недооценила риск относительно детерминированных сигналов, это не проходит незаметно.
Ещё один важный слой режет слабые объяснения:
пустые;
тавтологичные;
не привязанные к diff;
просто не полезные для reviewer.
Для меня это вообще один из самых важных моментов во всём проекте. Если система не может внятно показать, почему она пришла к такому выводу, её вердик сам по себе ничего не стоит.
В decision-engine есть простое правило: если уверенность низкая или контекст недостаточный, вердикт деградирует в review_required, а не в safe.
Это кажется очевидным, но именно из таких правил и складывается разница между “автоматизацией ради демо” и “автоматизацией, с которой хотя бы теоретически можно жить”.
Результат не исчезает вместе с PR comment. В хранилище сохраняется:
вердикт;
какие правила сработали;
как именно система пришла к вердикту;
на какие данные и фрагменты diff она опиралась;
кто, когда и почему вручную обошёл это решение.
То есть решение потом можно разбирать постфактум, а не вспоминать [5] по скриншотам, что там “бот когда-то написал”.
Если смотреть не на идеи, а на код, тесты и probe matrix, то сильнее всего сейчас у проекта стоят такие поверхности.
Когда PR лезет в auth, claims, permission, role, это уже само по себе отдельный merge-risk. Не потому, что каждая такая правка плохая, а потому, что это как раз тот тип изменений, который команда редко хочет пропускать без человеческого взгляда.
Изменения в .github/workflows, особенно широкие permissions и рискованные trigger semantics, уже выделены и в analysis layer, и в taxonomy, и в policy.
Lockfile, Dockerfile, Terraform/Kubernetes — всё это уже живёт в проекте не как “технический шум”, а как отдельный merge-risk.
И это, по-моему, очень близко к реальному ощущению: не всякое изменение в инфраструктуре плохое, но многие из них заслуживают review просто потому, что меняют то, как система должна вести себя в рабочем контуре.
Наверное, это вообще самые показательные сценарии. В probe matrix у проекта уже есть кейсы вроде:
supply-chain плюс auth change;
workflow permissions плюс sensitive path;
disabled test плюс auth change.
И вот это уже совсем похоже на живую реальность. Неприятные PR редко опасны одной строчкой. Намного чаще они опасны тем, что несколько почти нормальных изменений собрались в один набор изменений.
Ограничения у такого подхода, конечно, остаются. PRShield всё ещё работает как эвристическая система, а не как человек с полным контекстом продукта. Он умеет достаточно хорошо замечать некоторые классы смысловых сдвигов в diff, но не может по-настоящему понимать всю программу целиком.
Причём это во многом сознательный компромисс. Там, где система не уверена, она чаще предпочитает промолчать, чем начать шуметь. Часть false negative здесь встроена в дизайн просто потому, что доверие к такому инструменту теряется быстрее от потока мусорных срабатываний, чем от осторожного поведения.
Есть и более приземлённые границы. Например, более жёсткие комбинации сигналов, которые теоретически можно было бы довести до BLOCK, пока живут в режиме наблюдения, а не в основном контуре принятия решения.
Меня во всей этой истории цепляет довольно простой вопрос. Есть ли рядом с code review, командными правилами и security tooling ещё один слой, который отвечает не на вопрос “что здесь не так?”, а на вопрос: “что workflow должен сделать с этим PR прямо сейчас?”
Такой слой должен смотреть не только на код и не только на срабатывания. Он должен смотреть на изменение поведения системы. Именно это сейчас и проверяется на практике в формате отдельного рабочего MVP.
Особенно интересны реальные PR, которые снаружи выглядели безобидно, а на деле оказывались изменением доступа, поведения при сбое или критичного процесса. На таких примерах лучше всего видно, есть ли у этой идеи практический смысл.
Посмотреть на текущую версию можно здесь — prshield.tech [6].
Похожие кейсы, возражения и мысли по теме буду рад обсудить.
Автор: alyadusov
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29742
URLs in this post:
[1] интерес: http://www.braintools.ru/article/4220
[2] логику: http://www.braintools.ru/article/7640
[3] поведение: http://www.braintools.ru/article/9372
[4] поведения: http://www.braintools.ru/article/5593
[5] вспоминать: http://www.braintools.ru/article/3999
[6] prshield.tech: https://prshield.tech
[7] Источник: https://habr.com/ru/articles/1030810/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1030810
Нажмите здесь для печати.