TL;DR
Исследование измеряет, сколько даёт «обвязка» агента (инструменты, контекст, цикл проверки) и для каких моделей. Агенты искали уязвимости в реальном коде по трём стадиям: быстрый отбор подозрительных файлов, углублённый разбор каждого файла агентом и повторная проверка находок в отдельной сессии. Затем авторам нужно было понять, что меняется, если в разбор добавить готовую карту кода: кто кого вызывает, откуда приходит ввод пользователя, где опасные места.
Главная находка: ценность контекста зависит от модели. Малым и средним моделям карта кода дала заметный прирост в находках, причём чем слабее модель, тем больше прирост. У топовой модели число находок не изменилось в пределах шума, упали только ложные срабатывания. Вторая находка: ни одна модель не находит всё. Каждая пропускает часть уязвимостей, которые видят другие, а отдельные баги нашла только одна модель из многих. Третья: топовые модели не находят больше, они реже ошибаются. Дешёвые сильные модели дают сопоставимый урожай находок в разы дешевле. Один прогон тоже неполон: повторный запуск той же модели находит заметно другой набор.
Метод простой: не выбирай одну модель и один промпт «на все случаи». Для слабой модели готовь контекст заранее, для сильной не трать на это силы. Прогоняй задачу несколькими моделями и повторно. Каждую находку проверяй вторым проходом.
Схема метода
ШАГ 1 (Scan): быстрый отбор подозрительных файлов (регулярки/эвристики) → список файлов-кандидатов
ШАГ 2 (Process): агент разбирает файлы пачкой, получает контекст-блок → находки с серьёзностью и объяснением
ШАГ 3 (Revalidate): отдельная сессия перепроверяет каждую находку → метки: true-positive / false-positive / fixed / uncertain
ШАГ 4 (Объединение): прогон разными моделями и по 2 раза → слияние дублей, разные формулировки одного бага сводятся в один
Шаги 2 и 3 идут отдельными сессиями (разные запросы). Контекст-блок собирается без участия LLM, детерминированно, статическим анализом кода.
Пример применения
Задача: Команда маркетплейса на Node.js готовит релиз модуля оплаты: приём заказа, расчёт скидок по промокодам, вызов платёжного шлюза. Пентест будет через два месяца, а проверить код надо сейчас. В Cursor или Claude Code есть дешёвая и сильная модели.
Промпт (стадия Process, дешёвая модель, с контекстом):
Ты исследователь безопасности. Проверь файл src/payments/applyPromo.ts
на уязвимости, которые может использовать внешний атакующий.
<Контекст_кода>
Точки входа: POST /api/orders, POST /api/promo/apply
Кто вызывает applyPromo: orderController.create, cartController.recalc
Что вызывает applyPromo: db.promoCodes.find, pricing.calcDiscount, gateway.charge
Источники ввода от пользователя: req.body.promoCode, req.body.items[].qty, заголовок X-Forwarded-For
Опасные места (sinks): SQL-запрос в promoCodes.find, gateway.charge(amount)
Проверки на пути: authMiddleware (есть), rateLimit (только на /api/orders)
Контекст_кода>
Для каждой находки укажи: серьёзность (Critical/High/Medium/Low),
файл и строки, объяснение, как атакующий до этого места доберётся.
Если это просто баг логики без пути для атакующего, пометь как Bug, не как уязвимость.
Промпт (стадия Revalidate, новая сессия, та же или другая модель):
Вот находка из предыдущего анализа: {находка}.
Открой код и перепроверь. Есть ли защита, которую анализ не учёл?
Ответь одной меткой: true-positive / false-positive / fixed / uncertain,
и в 2-3 предложениях объясни почему.
Результат: После Process модель выдаст список находок с серьёзностью, строками кода и описанием пути атакующего. Часть будет шумом. После Revalidate в отдельной сессии каждая находка получит метку с коротким обоснованием. Повторите на сильной модели и сведите дубли: одну и ту же дыру модели опишут по-разному. Дешёвая модель с картой кода даст больше находок, сильная даст меньше ложных. Серьёзность модели обычно завышают, поэтому её надо пересматривать самому.
Почему это работает
Слабость: модель, которой дали файл «в лоб», видит только его. Уязвимость часто лежит между файлами: ввод приходит в одном месте, опасный вызов в другом. Слабая модель этой связи не достраивает, и находки получаются либо поверхностными, либо выдуманными.
Сильная сторона: если связи уже выписаны текстом (кто вызывает, откуда ввод, где защита), модель отлично рассуждает по ним. Ей остаётся оценить, есть ли путь от ввода до опасного места. Это проще, чем самой восстанавливать структуру проекта. Топовая модель восстанавливает её сама, поэтому карта ей почти ничего не добавляет. Информация дублируется, и прирост в пределах шума.
Как метод обходит слабость: контекст берёт на себя «поиск связей», который слабой модели не по силам. Отдельная сессия перепроверки фильтрует шум. Прогон несколькими моделями закрывает слепые зоны каждой.
Рычаги управления: - Размер контекста → не заваливай всё подряд. Целевой контекст (вызовы, ввод, опасные места) лучше «навалом» из документации и конфигов. Это общая закономерность из смежных работ: лишний контекст вредит. - Модель → слабой даёшь карту кода, сильной достаточно хорошего промпта. Так экономишь время на подготовку. - Число прогонов → запускай минимум дважды: один прогон воспроизводит лишь большую часть находок модели. - Набор моделей → добавь дешёвую модель вместо второй дорогой. Покрытие растёт, стоимость почти нет. - Метки при перепроверке → добавь свои (например, «нужен человек»), чтобы сомнительное не терялось.
Шаблон промпта
Это реконструкция по описанию стадий из статьи, а не дословный оригинал: полные тексты промптов в доступной части исследования не приведены.
Ты исследователь безопасности. Ищешь уязвимости, которые может использовать внешний атакующий.
Проверь файлы: {список_файлов}
<Контекст_кода>
Точки входа: {роуты, обработчики, middleware}
Кто вызывает код в этих файлах: {вызывающие}
Что вызывают эти файлы: {вызываемые}
Источники ввода от атакующего: {поля запроса, заголовки, файлы}
Опасные места (sinks): {SQL, shell, файловая система, платёжные вызовы, eval}
Проверки на пути: {авторизация, валидация, лимиты}
Контекст_кода>
Для каждого опасного места:
проследи путь от источника ввода до него;
проверь, есть ли защита на пути;
если пути для атакующего нет, это не уязвимость.
Баг логики без пути для атакующего помечай как Bug, не как уязвимость.
Стадия перепроверки (новая сессия):
Вот находка: {находка}.
Открой код и перепроверь. Есть ли защита, которую анализ не учёл?
Ответь меткой: true-positive / false-positive / fixed / uncertain, и объясни в 2-3 предложениях.
Что подставлять: {список_файлов} берёшь из быстрого отбора (grep по опасным вызовам: exec, query, eval). Блок <Контекст_кода> собираешь по реальному коду.
🚀 Быстрый старт — вставь в чат (лучше в Claude Code или Cursor с доступом к проекту):
Вот шаблон поиска уязвимостей с контекстом кода. Адаптируй под мой проект: {опиши проект и стек}.
Сначала собери по репозиторию блок <Контекст_кода>: точки входа, кто кого вызывает,
источники ввода, опасные места, проверки на пути. Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит про стек, точки входа и то, какие файлы критичны. Эти вопросы нужны для карты кода, на которой метод держится. Она возьмёт паттерн из шаблона и соберёт контекст для твоего проекта.
Ограничения
⚠️ Один репозиторий: основной эксперимент шёл на одной кодовой базе (TypeScript, API-шлюз для LLM). Часть эффекта проверили на другом наборе задач, но широкого подтверждения нет.
⚠️ Смешанное сравнение: сравнение «минимальный промпт → инженерный промпт с картой» меняет и промпт, и контекст сразу. Чистый вклад карты авторы отделили отдельным сравнением, но цифры 20–86% относятся к обоим изменениям вместе.
⚠️ Карту строил статический анализатор, не LLM: в статье контекст собирал CodeQL. Если ты просишь агента нарисовать карту сам, это не проверено: она может быть неполной или с ошибками.
⚠️ Серьёзность завышена: модели чаще всего присваивают находкам более высокую серьёзность, чем потом признают мейнтейнеры. Каждую находку смотри глазами человека, особенно Critical.
⚠️ Самопроверка не идеальна: перепроверка собственных находок — фильтр, а не доказательство. Модель может отбросить настоящую уязвимость и пропустить ложную. Для отчётов наружу нужно подтверждение запуском эксплойта.
⚠️ Нет универсального рецепта: результаты про конкретные модели на момент исследования. Какие модели «слабые» и «сильные», быстро меняется. Принцип (проверяй, помогает ли контекст твоей модели) устойчивее цифр.
⚠️ Нужна экспертиза: для безопасности и кода-ревью без человека, который читает находки, метод не заменяет специалиста.
Как исследовали
Авторы взяли открытый сканер DeepSec от Vercel Labs и прогнали его на реальном продакшн-коде: API-шлюзе для LLM, примерно 150 файлов-кандидатов на TypeScript. Уязвимости в нём не были публичными на момент сканирования, поэтому модель не могла просто вспомнить известную CVE из обучающих данных. Обычно бенчмарки берут уже закрытые баги, и успех там может быть памятью, а не поиском.
Сначала проверили 24 связки модель + запуск. Все находки свели в канонический список: 265 уникальных причин уязвимостей и 511 багов логики. Ни одна связка не покрыла всё. Лучшие два прогона вместе дают лишь 60,4% списка, а 14,3% находок обнаружила только одна модель.
Затем выбрали четыре модели от топовой до небольшой (GPT-5.5, Composer-2.5, GLM-5.1, Gemma-4-31B) и сравнили восемь условий: два базовых промпта и шесть вариантов контекста. Менялась только стадия Process, остальное было зафиксировано. Прирост убывал с силой модели: +86% у малой, +36% у средней, +20% у дешёвой сильной, +2% у топовой. Последний результат попадает в шум между прогонами. Направление повторили на отложенной выборке из CyberGym.
Удивило, что топовая модель не нашла больше уязвимостей, у неё упали только ложные срабатывания. Для практики это значит: платить за топ имеет смысл ради чистоты отчёта, а не ради охвата. Ещё один вывод: прогон восстанавливает лишь 68–91% набора модели из двух запусков, то есть один запуск ненадёжен.
Адаптации и экстраполяции
💡 Адаптация для код-ревью в Cursor/Claude Code: тот же принцип «контекст для слабой модели, чистота для сильной» работает не только для уязвимостей. Если ревьюишь пул-реквест дешёвой моделью, сначала дай ей список затронутых функций, их вызывающих и тестов. Сильной модели достаточно диффа. Статья этого не проверяла, это перенос принципа на другую задачу.
Ты ревьюишь пул-реквест. Вот дифф: {дифф}.
Контекст: функции, которые вызывают изменённый код: {вызывающие}.
Тесты, покрывающие эти места: {тесты}.
Найди только те проблемы, которые ломают поведение для вызывающих. Если не уверен, помечай uncertain.
🔧 Техника: разные модели на разных стадиях → дешевле при том же охвате. Дешёвая сильная модель делает широкий проход (Process), топовая только перепроверяет находки (Revalidate). В статье у каждой модели проверка шла «своих» находок, а смешение моделей на стадиях — наша экстраполяция.
Экстраполяция: ансамбль + перепроверка. Прогони одну и ту же задачу двумя-тремя моделями. Объедини находки одним запросом:
Вот находки трёх моделей: {список}. Сведи дубли: одну проблему, описанную разными словами, считай одной. Для каждой уникальной проблемы укажи, сколько моделей её нашли.Проблемы, которые нашла только одна модель, не выбрасывай, а проверяй вручную: в статье именно такие составляют заметную долю.
Ресурсы
- Название: How Much Does the Harness Matter, and for Whom? A Multi-Model Study of Context in LLM-Driven Vulnerability Discovery
- Авторы: Ganni Galea Curmi (Georgian; University of Toronto), Ben Wilde (Georgian)
- Конференция: Proceedings of Machine Learning Research 349, 2026, Conference on Applied Machine Learning for Information Security
- Инструменты из статьи: DeepSec (Ubl, 2026, Vercel Labs), Trailmark (Arciszewski, 2026), CodeQL, бенчмарк CyberGym (Level-0), ZeroDayBench
- Смежные работы: Big Sleep (Google Project Zero), DARPA AI Cyber Challenge
