TL;DR
SecGent — это схема работы агента с кодом из пяти этапов. Сначала модель придумывает, как эту задачу можно реализовать уязвимо, и по этому описанию находит подходящие записи из каталога уязвимостей CWE. Затем отдельный «тестировщик» до написания кода пишет два теста: функциональный (нормальные данные дают нужный результат) и безопасностный (атакующие данные не дают вредного эффекта). Потом «кодер» пишет реализацию, прогоняет тесты и исправляет ошибки. Исправление принимается, только если одна из оценок выросла, а другая не упала.
Главная находка: код, который проходит обычные тесты, часто небезопасен. Почти треть работающих реализаций (29,2%) проваливает проверку безопасности. Если просто добавить в промпт информацию об уязвимостях, модель начинает «перестраховываться»: безопасность растёт, а нужное поведение ломается. В примере из статьи модель поставила жёсткую проверку на язык в заголовке и стала отвергать нормальные значения вроде en-US,en;q=0.9. Получается «безопасный», но бесполезный код.
Метод лечит это двумя ходами. Знание об уязвимостях подаётся как «возможные риски», а не как повод менять требуемое поведение. Замороженные заранее тесты с двумя целями не дают исправлению улучшить безопасность за счёт функциональности. Итог: на пяти языках и пяти моделях SecGent даёт лучшую совместную долю «работает и безопасно» среди всех сравниваемых подходов.
Схема метода
ШАГ 0 (один раз, заранее): подготовить каталог уязвимостей CWE
→ каждая запись переписана «языком кода»: ввод, операция, последствие
ШАГ 1: модель описывает, как задачу можно реализовать уязвимо
→ черновик уязвимого кода + 1-2 предложения «чего не хватает»
ШАГ 2: по черновику и ТЗ найти и отобрать 5 самых подходящих записей CWE
→ «контекст слабостей» (текст, без примеров кода)
ШАГ 3: агент-тестировщик (кода ещё нет, кода он не видит)
→ 1 функциональный тест + 1 безопасностный тест (привязан к CWE)
→ тесты проверены и ЗАМОРОЖЕНЫ
ШАГ 4: агент-кодер (тестов не видит) пишет реализацию
→ риски CWE = «возможные проблемы», не повод менять поведение
ШАГ 5: прогон тестов → оценка (функц., безопас.), например (1, 0)
ШАГ 6: ремонт (ограниченное число попыток), кодер видит код и ошибки
→ правка принимается, только если одна оценка выросла, а другая не упала
Шаги 1–2 в статье автоматизированы поиском по базе. Шаги 3–6 — это роли и порядок работы, их можно задать инструкцией агенту.
Пример применения
Задача: Вы делаете бэкенд для сервиса доставки еды. Клиент скачивает PDF-чек по имени файла. Нужна функция, которая отдаёт файл из папки /var/receipts. При этом должны работать и вложенные пути вида 2025/03/order_48213.pdf. Если вы просто попросите «сделай безопасно», модель может запретить все слэши и сломать выдачу чеков.
Промпт (для Claude Code или другого агента, который умеет запускать тесты):
Ты ведёшь разработку в три роли: Аналитик рисков, Тестировщик, Кодер.
Выполняй этапы строго по порядку.
Python-функция get_receipt(path_param: str) -> bytes.
Читает PDF-чек из папки /var/receipts по относительному пути от клиента.
Допустимые пути: order_48213.pdf и 2025/03/order_48213.pdf.
Если файла нет — вернуть None. Сигнатуру менять нельзя.
Напиши ГИПОТЕТИЧЕСКУЮ УЯЗВИМУЮ реализацию этой функции
и 1-2 предложения: какая операция или какая пропущенная проверка делает её опасной.
Гипотеза может быть неточной — это нужно для поиска рисков.
Затем назови до 5 типов слабостей (CWE), которые к ней относятся.
Кода ещё нет. Ты его не видишь.
Напиши ровно два теста:
- test_functional: валидные пути из задачи дают правильный результат.
- test_security: атакующий ввод не даёт вредного эффекта.
Привяжи его к одной CWE из списка этапа 1.
Сохрани тесты в файл и больше не меняй их.
Напиши реализацию. Тесты не открывай.
Сохрани сигнатуру, поведение на валидных путях, возвращаемые значения.
Риски из этапа 1 — это ВОЗМОЖНЫЕ проблемы, а не повод менять требуемое поведение.
Запусти тесты. Запиши оценку: (функциональный, безопасностный), где 1 = прошёл, 0 = упал.
While оценка != (1,1) and попыток < 3:
Предложи исправление, учитывая текст ошибки.
Сохрани интерфейс и то, что уже проходило.
Запусти тесты на предложении.
If одна оценка выросла и другая не упала: принять исправление.
Else: отклонить, оставить прежнюю версию.
В конце покажи: итоговый код, оценку и что отклонено.
Результат: Агент покажет гипотезу об уязвимой версии (скорее всего, склейка пути без проверки выхода за папку) и список CWE. Затем появятся два теста. Далее будут код, оценка прогона, например (1, 0), и один-два цикла исправлений. В конце вы получите функцию, которая отдаёт вложенные чеки и не даёт выйти за пределы папки. Рядом будет журнал: какие правки приняты, какие отклонены.
Почему это работает
Слабость модели. Когда просишь «пиши безопасно», модель угадывает, чего бояться. Либо пропускает риск, который не очевиден из ТЗ: в задаче про заголовок нет слов «перевод строки» и «response splitting». Либо видит предупреждение и перестраховывается: добавляет лишние проверки и ломает нужное поведение. Безопасность и функциональность тянут в разные стороны, и без второй цели модель жертвует ею.
Сильная сторона. Модель хорошо умеет придумывать, как можно сделать плохо, а также писать тесты и чинить код по тексту ошибки. В статье именно описание уязвимой реализации помогло найти нужную слабость. Поиск по сухому ТЗ нашёл правильные CWE у 47,1% задач, а через «гипотетический уязвимый код» — у 89,1%.
Как метод это использует. Он переводит ТЗ на «язык уязвимостей» и фиксирует обе цели тестами до написания кода. Кодер не видит тестов и не подгоняет решение под них. Правило «одна оценка выросла, другая не упала» не даёт ремонту обменять безопасность на функциональность. В статье именно ремонт по обратной связи чаще всего возвращал потерянное поведение и при этом сохранял безопасность.
Рычаги управления: - Число попыток ремонта → меньше для простых функций, экономия времени; больше для сложных. - Число CWE в контексте (в статье 5) → меньше даёт точность, больше даёт охват. - Условие принятия правки → можно ужесточить: «сначала обе оценки должны быть не хуже, а общая сумма выше». - Один тест на цель → если нужны более широкие проверки, разреши несколько проверок внутри одного теста, как в статье. - Разделение чатов → тестировщик и кодер в отдельных диалогах или субагентах: так кодер действительно не видит тестов.
Шаблон промпта
Ты ведёшь разработку в три роли: Аналитик рисков, Тестировщик, Кодер.
Выполняй этапы строго по порядку.
Язык: {язык}
Что сделать: {описание_функции}
Сигнатура (менять нельзя): {сигнатура}
Допустимые входные данные (должны продолжать работать): {валидные_примеры}
Ошибки и крайние случаи: {поведение_при_ошибке}
Напиши ГИПОТЕТИЧЕСКУЮ УЯЗВИМУЮ реализацию функции
и 1-2 предложения: какая операция или пропущенная проверка делает её опасной.
Гипотеза может быть неточной.
Назови до {число_CWE} типов слабостей (CWE), которые к ней относятся.
Кода ещё нет. Ты его не видишь.
Напиши ровно два теста на {тестовый_фреймворк}:
- test_functional: валидные входные данные дают ожидаемый результат.
- test_security: атакующий ввод не даёт вредного эффекта; привяжи к одной CWE из этапа 1.
Сохрани и не меняй.
Напиши реализацию. Тесты не открывай.
Сохрани сигнатуру, поведение на валидных данных, возвращаемые значения и побочные эффекты.
Риски из этапа 1 — возможные проблемы, а не повод менять требуемое поведение.
Запусти тесты командой {команда_запуска}.
Запиши оценку (функциональный, безопасностный): 1 = прошёл, 0 = упал.
While оценка != (1,1) and попыток < {число_попыток}:
Предложи исправление по тексту ошибки.
Сохрани интерфейс и то, что уже проходило.
Запусти тесты на предложении.
If одна оценка выросла и другая не упала: принять.
Else: отклонить, оставить прежнюю версию.
Покажи: итоговый код, оценку, список принятых и отклонённых правок.
Подставьте язык, описание функции, сигнатуру и 2-3 примера допустимых данных. Чем точнее вы опишете допустимый ввод, тем меньше шансов, что модель его «зарежет».
🚀 Быстрый старт — вставь в чат или в сессию агента:
Вот шаблон SecGent (безопасный код через тесты до кода). Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит про язык, сигнатуру, примеры допустимых данных и команду запуска тестов. Допустимые данные нужны, чтобы тестировщик написал функциональный тест, а кодер не сломал нужное поведение. Команда запуска нужна, чтобы агент мог проверить код сам.
Ограничения
⚠️ Нужен запуск кода: без запуска тестов (Claude Code, Cursor, любой агент с терминалом) схема превращается в «подумай ещё раз». Эффект от обратной связи держится на реальных прогонах.
⚠️ Оригинал — это конвейер на коде: поиск по каталогу CWE, ранжирование и контейнеры для тестов собраны программно. Шаблон выше — упрощённый перенос идей через инструкцию. Модель сама назовёт CWE по памяти, а в статье записи подбирались поиском. Такой перенос авторы не проверяли.
⚠️ Изоляция ролей: в одном диалоге кодер видит тесты и может подгонять код под них. В статье тестировщик и кодер были раздельными агентами. Для честной проверки разнесите их по отдельным чатам или субагентам.
⚠️ Проверяются только то, что покрыто тестами: прохождение двух тестов не доказывает безопасность. Каждый тест проверяет одну выбранную слабость. Другие уязвимости остаются непокрытыми.
⚠️ Один бенчмарк: результаты получены на наборе CWEval (119 коротких функций). Для больших проектов с зависимостями и несколькими файлами результат не проверялся.
⚠️ Тесты могут оказаться плохими: если сгенерированный тест пропускает опасный ввод или проверяет не то, исправление «пройдёт» без пользы. В статье такие наборы помечаются непригодными и не запускают ремонт.
Как исследовали
Авторы взяли CWEval — набор из 119 задач, где у каждой есть два независимых проверяющих теста: на функциональность и на безопасность. Метрика FS@k — вероятность, что среди k вариантов хотя бы один проходит обе проверки. Это важно: многие приёмы улучшали безопасность только потому, что убирали нужное поведение, а по раздельным метрикам такого не видно.
Сравнивали SecGent с четырьмя подходами на этапе генерации: обычным запросом, RAG с записями CWE, автоматическими оптимизаторами промптов (MIPROv2, GEPA) и AutoGen. Тестировали четыре открытые модели (например, Qwen3-Coder-30B) и Claude Sonnet 5.5 на пяти языках (Python, C, C++, Go, JavaScript). Отдельно сравнили с Claude Code с промптом про безопасность.
Результаты. SecGent выигрывает у всех на всех моделях. На открытых моделях средний FS@1 вырос на 15,5% относительно RAG и на 19,8% относительно лучшего оптимизатора промптов. На Sonnet FS@1 дошёл до 92,1%, это на 4,8% выше AutoGen. Claude Code с промптом про безопасность уступил на 6,2%. Обратная связь от тестов добавила до 6,7% по сравнению с генерацией без неё.
Удивило, откуда берётся прирост от обратной связи. Он пришёл в основном из возвращения потерянной функциональности при сохранении безопасности, а не из дополнительной «закалки» кода. Для практики это значит, что ремонт без двух целей почти бесполезен: безопасный, но неработающий код легко получить, а вот безопасный и работающий — трудно. Ещё один сюрприз: даже обычный RAG с CWE улучшает безопасность и одновременно снижает функциональность. У CodeGuarder (поиск с примерами уязвимого и исправленного кода) потери функциональности в основном съели выигрыш по безопасности.
Чтобы тесты не «подтекали» в оценку, авторы не использовали вердикты CWEval для ремонта. Ремонт шёл только по тестам, которые написал сам агент.
Адаптации и экстраполяции
🔧 Техника: добавить правило в CLAUDE.md / AGENTS.md → агент всегда проверяет обе цели
Если не хочется вставлять большой шаблон каждый раз, оставьте в файле инструкций короткий вариант:
При написании кода, который принимает внешний ввод (HTTP, файлы, БД, логи, команды):
1. Сначала напиши, как эту функцию можно реализовать уязвимо, и назови 2-3 релевантные CWE.
2. Напиши два теста: функциональный (валидный ввод) и безопасностный (атакующий ввод). Не меняй их при исправлениях.
3. Риски — это «возможные проблемы», а не повод менять требуемое поведение и сигнатуру.
4. Принимай исправление, только если одна оценка выросла, а другая не упала.
Это перенос идей статьи в файл инструкций, а не измеренная в ней конфигурация. Работает как стартовая точка, дальше проверяйте на своих задачах.
Ресурсы
- Secure and Functional Code Generation with Self-Evaluation — Md Afif Al Mamun (University of Calgary, Канада), Gias Uddin (York University, Канада), 2026.
- Бенчмарк: CWEval (проверка функциональности и безопасности исполняемыми тестами).
- Источник знаний об уязвимостях: MITRE CWE (представление CWE-1000, 944 слабости).
- Метод поиска: HyDE (Hypothetical Document Embeddings).
- Сравнивались: RAG, MIPROv2, GEPA, AutoGen, CodeGuarder, RESCUE, Claude Code.
