На хабре почти ежедневно появляются статьи, типа “уволил программиста, теперь мне claude code / codex / … пишет”. Начинается спор, где с одной стороны восторженные фанаты вайб-кодинга, (которым раньше программист за месяц делал сайт в синей цветовой гамме, а сейчас нейронка за вечер – и на глаз цвет такой же), с другой скептики-луддиты. И все это каждый раз по тому же кругу. В этом не хватало каких-то твердых аргументов. И вот сейчас я нашел один такой (для луддитов). Исследование 2025 года, для конференции IEEE-ISTAS 2025.
Здесь только краткий пересказ (оригинал мне показался излишне подробным). Желающие смогут прочитать оригинал или попросить любую нейронку перевести на русский.
Исследование
Security Degradation in Iterative AI Code Generation: A Systematic Analysis of the Paradox (IEEE-ISTAS 2025) https://ieeexplore.ieee.org/document/11269659 (оригинал, paywall) https://arxiv.org/html/2506.11022v2 (отрыто)
Процесс
Взяли 10 образцов хорошего кода без уязимостей. Применяли 4 стратегии улучшения (добавь фичи / оптимизируй / улучши безопасность / “размытые” просьбы сделать код лучше) 10 раз. Получили 400 образцов кода от ИИ и их исследовали руками и автоматическими сканерами.
Результат
Чем дальше в лес, тем толще партизаны. Уязвимости появлялись и на ранних итерациях, но чем дальше заходил проект, тем хуже все становилось, чем больше уязвимостей создавала LLM. Наибольшее количество уязвимостей (158) появлялось при стратегии добавления функционала. Наименьшее (38) – при стратегии улучшения безопасности (то есть, вы просите исправить уязвимость, а LLM создает новые).
Связь объема кода и уязвимостей
Исследователи обнаружили положительную корреляцию (r = 0,64, p < 0,001) между увеличением сложности кода и количеством уязвимостей безопасности. На каждые 10% увеличения сложности – наблюдали в среднем 14,3%-ное увеличение количества уязвимостей (95% доверительный интервал: 10,7% – 17,9%). Множественный регрессионный анализ с контролем стратегии промптинга и базовых характеристик кода показал, что сложность оставалась значимым предиктором количества уязвимостей (β = 0,64, p < 0,001).
Конкретные примеры
Пример 1: Эволюция управления памятью
Начав с безопасной функции выделения памяти, исследователи обнаружили, что промпты, ориентированные на эффективность, приводили к последовательным изменениям:
-
Итерация 1: Удалена проверка границ для повышения производительности
-
Итерация 3: Внедрены небезопасные шаблоны повторного использования памяти
-
Итерация 5: Добавлены непотокобезопасные статические буферы
-
Итерация 7: Реализован пользовательский пул памяти с множественными уязвимостями use-after-free
-
Итерация 10: Реализована сложная арифметика указателей, сопряженная с рисками переполнения буфера
Пример 2: Трансформация функции аутентификации
Безопасная функция проверки токена аутентификации претерпела значительные изменения в плане безопасности в результате использования запросов, ориентированных на добавление функционала:
-
Итерация 1: Добавлено кэширование, сопряженное с уязвимостями побочных каналов по времени (timing side-channel)
-
Итерация 3: Реализована поддержка нескольких протоколов, сопряженная с уязвимостями парсинга
-
Итерация 6: Добавлено постоянное хранилище, сопряженное с рисками SQL-инъекций
-
Итерация 8: Реализована функция восстановления пароля, сопряженная с уязвимостями раскрытия информации
-
Итерация 10: Разработана сложная многофакторная аутентификация с логическими ошибками в механизмах отката
Пример 3: Эволюция уровня доступа к базе данных
Безопасная функция доступа к базе данных с корректной параметризацией претерпела изменения под воздействием неоднозначных запросов на улучшение:
-
Итерация 2: Упрощено построение запросов, но удалена параметризация
-
Итерация 4: Добавлено динамическое построение запросов с конкатенацией строк
-
Итерация 6: Реализовано кэширование запросов с недостаточной проверкой входных данных
-
Итерация 8: Добавлена поддержка транзакций, сопряженная с состояниями гонки (race conditions)
-
Итерация 10: Разработана абстракция, подобная ORM, сопряженная с множественными уязвимостями инъекций
Успешные улучшения безопасности
Хотя деградация безопасности наблюдалась часто, исследователи отметили некоторые случаи улучшения безопасности. Среди запросов, ориентированных на безопасность, 27% итераций привели к чистому улучшению безопасности, преимущественно на ранних итерациях (1-3). Эти улучшения обычно включали:
-
Добавление проверки входных данных
-
Внедрение корректной обработки ошибок
-
Добавление проверок на NULL
-
Исправление очевидных проблем с управлением памятью
Однако эти улучшения часто нивелировались появлением новых, более скрытых уязвимостей на более поздних итерациях, что приводило к чистому снижению безопасности на протяжении всей последовательности из 10 итераций.
Выводы
Уязвимости безопасности, по-видимому, накапливаются нелинейно на протяжении итераций, при этом на поздних итерациях уязвимости возникают с большей частотой, чем на ранних. Это говорит о том, что по мере роста сложности кода в результате итеративных модификаций поддержание безопасности становится для LLM все более сложной задачей.
Различные стратегии промптинга связаны с различными паттернами уязвимостей, при этом запросы, ориентированные на эффективность, демонстрируют наиболее серьезные проблемы с безопасностью. Это согласуется с устоявшимся принципом безопасности, согласно которому оптимизация часто достигается за счет безопасности.
Даже при явном запросе на улучшение безопасности LLM часто генерируют код, содержащий новые уязвимости, одновременно исправляя очевидные, что указывает на потенциальные ограничения в понимании LLM практик безопасного кодирования в сложных кодовых базах.
Корреляция между сложностью кода и количеством уязвимостей предполагает, что более простые структуры кода могут быть менее подвержены проблемам с безопасностью, что подчеркивает потенциальную ценность простоты в защищенных системах.
Во всех стратегиях промптинга каждая итерация, как правило, создавала код, который выглядел более сложным и продвинутым, несмотря на появление новых уязвимостей. Это создает потенциальную иллюзию улучшения, которая может заставить разработчиков доверять проблемному коду.
Рекомендации
Главная: Human in the loop, человек должен проверять код после каждой итерации (или, хотя бы не больше трех итераций без человеческого контроля).
Более очевидные: использовать статические анализаторы кода (SAST), уделять особое внимание, когда объем кода значительно вырастает (обычно это коррелирует с появлением уязвимостей).
Ограничения исследования
Тестировалась только GPT-4o, и только языки C и Java. Человек ничего не исправлял по ходу итераций.
Мой комментарий
Статья подтвердила интуитивные догадки, которые были, наверное, у каждого программиста. Многие замечали, как нейронка “тупеет” по мере работы над проектом. Дает очень хороший результат вначале, но дальше все начинает идти туго.
Можно сказать, что модель (GPT-4o) слабая, а задачи сложные. Новые модели лучше. В этом есть смысл. Но важно понять, почему плохая модель так ошибалась. И возникает вопрос – а новая хорошая модель – прямо вот принципиально иная, или же она в принципе такая же, просто фатально ошибается не на третьей итерации а на пятой или двенадцатоой?
Требование человеческого контроля после итераций, мне кажется в целом почти убивает смысл LLM. Поверхностный беглый взгляд мало что найдет. А глубокое вдумчивое чтение и понимание чужого кода – это очень долго и медленно. LLM в вайбкодинге – это такой волшебный джинн, ты говоришь ему свое смутное желание, он исполняет (быстро, легко, дешево). А если его надо контролировать – то он уже не джинн, а джун, который норовить написать коварные ошибки и спрятать их в коде (медленно, тяжело, дорого).
Автор: xenon


