
Привет, Хабр!
На связи Максим Митрофанов, ML-лид команды Application Security в Positive Technologies. Мы занимаемся прикладными вопросами машинного обучения по направлению безопасной разработки, регулярно изучаем новые технические репорты и статьи, разбором одной из которых я и хотел бы поделиться с вами.
Перевод фрагментов статьи, представленных в обзоре, не является дословным. Разбор содержит личные комментарии и размышления, возникшие в процессе чтения, и, на мой взгляд, будет особенно интересен специалистам по информационной безопасности и ML-инженерам, внедряющим ИИ в R&D-процессы компаний.
Исследуя подходы к оценке больших языковых моделей в разрезе безопасной разработки, мы наткнулись на статью LLMs Cannot Reliably Identify and Reason About Security Vulnerabilities (Yet?): A Comprehensive Evaluation, Framework, and Benchmarks, которая посвящена анализу применения LLM для обнаружения уязвимостей в исходном коде.
Работа показалась интересной по нескольким причинам:
Она относительно свежая (насколько это возможно в эпоху LLM): опубликована в декабре 2023-го и с того момента процитирована 74 раза, а с ее последнего обновления и вовсе прошло чуть больше года.
В ней представлено 17 вариантов промптов для решения вышеуказанной задачи.
-
Применимость LLM оценивалась c восьми точек зрения:
Детерминированность ответов.
Значимость параметров запросов.
Подбор промптов.
Согласованность размышлений (reasoning) моделей с бинарным ответом.
Оценка классификации кода на наличие уязвимостей.
Уровень сложности кода.
Устойчивость к изменениям в коде.
Оценка реальных проектов на уязвимости.
К тексту приложена ссылка на репозиторий с датасетом и исходным кодом.
Эту статью тяжело назвать новым стандартом оценки языковых моделей, но количество интересных решений и противоречивых тезисов оказалось достаточно большим, чтобы написать ревью.
Немного о данных, моделях и способах оценки качества
Авторы статьи составили 228 уязвимых сниппетов кода:
48 самописных примеров, покрывающих 8 классов CWE (24 уязвимых и столько же исправленных).
30 реальных CVE, связанных с выбранными CWE (15 уязвимых и столько же исправленных).
150 видоизмененных самописных примеров, составленных из первоначальных 48 (механику и назначение этих примеров рассмотрим позднее в обзоре эксперимента по восприятию LLM аугментации кода).

Первое, что бросается в глаза, — полученный датасет состоит из кода на двух языках: C и Python. Авторы упоминают, что выбор обусловлен разными уровнями абстракции (используются высокоуровневый и низкоуровневый языки программирования). Но я не исключил бы фактор ограниченных возможностей (для составления бенчмарка были привлечены всего двое экспертов по ИБ) в ущерб анализа потребностей сообщества в использовании LLM для задач программирования. Так, недавний анализ, проведенный компанией Anthropic, показал, что чаще всего модели Claude используются для разработки с использованием следующих языков.

Вторым дискуссионным вопросом, связанным с полнотой набора данных, является достаточность использования 8 CWE более чем из 900 категорий.
Из положительного — к статье приложена ссылка на GitHub-репозиторий, который позволяет расширить бенчмарк при желании и необходимости.
Перейдем к моделям, которые были использованы при написании статьи. Авторы представляют результаты для пяти моделей (еще три вынесли в приложение). Общий список — ниже.

Да, предвосхищаю комментарии: модели действительно не самые свежие (совсем недавно OpenAI похоронила GPT-4 в ChatGPT, оставив только доступ по API), но подход к их тестированию кажется интересным. Поэтому не торопитесь закрывать вкладку :)
Перейдем к системе оценки качества, которая в общем виде представлена в виде схемы.

Как это читать? Сейчас расскажу. Берем одну из гипотез (блок Investigation Dimensions), отправляем в выбранную модель, настроив промпт, температуру и параметры Top-p (блок LLM). После некоторых преобразований оцениваем отдельно ризонинг-часть и однозначный ответ (блок Evaluator).
Качество рассчитывается следующим способом:
1. Для бинарного ответа берется accuracy (4).
2. Для ризонинг-части берется summary от GPT-4 (1, 3) и считаются три метрики близости (5):
Rouge.
Cosine similarity.
GPT-4 eval (на замерах авторов дает корректный результат в 48 случаях из 50).
Если две метрики из трех дают положительный результат, то ответ засчитывается.
Ключевое, на что стоит обратить внимание, — это суммаризация ризонинг-решения оцениваемой LLM через GPT-4, которая сокращает ответ до 100 символов. Именно это заключение используется для сравнения с эталоном. На мой взгляд, такой подход выглядит не лучшим образом по нескольким причинам:
LLM-судья может приукрасить саммари исходя из собственных накопленных знаний.
В ходе сокращения ответа до 100 слов могут потеряться важные детали реального ризонинга тестируемой LLM, которые позволили бы отладить решение.
У бенчмарка может случиться вендорлок из-за использования GPT-4 (дважды — в суммаризации и в оценке ризонинга): вендор может отключить модель, и бенчмарк потеряет актуальность.
Разобравшись с общим фреймворком проведения экспериментов, предлагаю перейти к обсуждению результатов.
Гипотезы и результаты
Далее нумерация глав будет соответствовать нумерации из статьи для удобства навигации.
Детерминированность ответов (4.1. Evaluation for Deterministic Responses)
В целом даже на этапе постановки задачи становится понятно, что в контексте ИБ нас интересует в первую очередь стабильность ответов. При использовании языковых моделей это достигается подбором оптимальных параметров temperature и top_p.
Авторы опираются на документацию API OpenAI по оптимальному подбору этих параметров и на статью 2021 года Examining Zero-Shot Vulnerability Repair with Large Language Models. Проведя пару экспериментов с температурами 0,2 и 0,0, они делают вывод, что нулевая температура лучше. Но если посмотреть внимательно на таблицы, то разница будто бы незначительная.


Здесь хотел бы обратить внимание на следующее: учитывая, что 2v/2p — это уязвимые/пропатченные (vulnerable/patched) версии кода уровня сложности — medium, авторы уже дают небольшой спойлер о проблемах моделей в умении их различать.
К слову, вопрос подбора параметров затрагивается в свежей cookbook-статье Google, где среди прочего приведены заметки для кейсов применения пограничных значений, — настоятельно рекомендую к прочтению.
Гипотеза 4.2. Performance Over Range of Parameters по своей сути является расширением первой и проверяет на схожих CWE стабильность языковых моделей в сфере размышления и классификации уязвимого кода, но на большем диапазоне параметров. Выводы совпадают с выводами из предыдущего теста, поэтому предлагаю не задерживаться и идти дальше.
Техники промптинга (4.3. Diversity of Prompts)
На мой взгляд, показательный параграф с точки зрения как широты эксперимента, так и визуализации результатов.

Диаграмма позволяет понять, на скольких уязвимостях промпты отработали вообще без галлюцинаций (длина горизонтальнойго полосы), сколько из них были решены корректно в части бинарного ответа на задачу (доля зеленого) и сколько из них совпали с цепочками рассуждений (белый круг). Для примера — один из промптов (R5) выглядит следующим образом.

Для желающих залипнуть минут на 30...
...к диаграмме с результатами прилагаю таблицу с описанием промптов и код, реализующий сборку инструкций.

А для тех, кто экономит время, приведу свои заметки:
Не существует серебряной пули в виде лучшего промпта для любой архитектуры LLM.
Дополнительная информация из MITRE ATT&CK (при такой постановке задачи, по сути, это внешний источник) позволяла некоторым промптам прибавить в качестве, но ни разу не была использована в лучших по качеству промптах каждой модели. Вообще сложилось впечатление, что эта информация заложена в знания GPT-моделей, потому что не дает практически никакого прироста при наличии chain of thought (далее — CoT).
Грамотное построение доменной инструкции step-by-step CoT дает значимый прирост качества и согласованности ответа с рассуждениями.
Тем не менее при наличии подробных few-shot инструкций для конкретной доменной задачи практически полностью нивелируется польза от общей доменной CoT. В таком случае нужно выбрать, что дешевле реализовать.
Вопросы к эксперименту вызывает формирование некоторых few-shot-промптов (R4, R5, R6, S5, S6, D3, D4, D5): для этого использовались примеры исправления уязвимостей из аналогичной CWE, что в отношении решаемой задачи можно назвать абсурдом. Для масштабирования решения с этой инструкцией придется составить или найти примеры патчей более чем для 900 CWE.
Если обобщить мысли в короткую рекомендацию — назначайте LLM правильную роль и продумывайте цепочку рассуждений, которая будет соответствовать вашему домену. Это может внести больший вклад в успех с наименьшим объемом затрат, и вы сэкономите время на муторной проработке примеров для few-shot и на подключении внешних источников.
Согласованность размышлений (4.4. Faithful Reasoning)
В этой главе акцент делается на неполной согласованности рассуждений и ответов (желтый и голубой), но я думаю, что это тренд, уходящий с развитием языковых моделей. Кажется, что гипотеза полезна при разработке LLM-приложений как некоторый референс согласованности (92–96% Reason Rate в таблице 12) между CoT и финальным ответом.
На текущий момент компании выпускают либо несколько моделей одного поколения, разделяя их по уровню размышления (reasoning), либо одну модель с возможностью настраивать уровень самостоятельно. Как предложение для дальнейших исследований — было бы интересно измерить эти показатели на моделях одного поколения с учетом этого параметра.

Способность обнаруживать уязвимый код (4.5. Evaluation Over Variety of Vulnerabilities)
Авторы проверили способность языковых моделей отличать уязвимый код от пропатченного и описали результат следующим образом:
Observations. Most models show poor performance in classifying the patched versions correctly, which makes these LLMs non-suitable for real-world cases as they will mostly flag safe code as vulnerable, causing many false alarms.
Лучшая из используемых моделей (GPT-4) показала недостаточную полноту ответов для использования в реальной жизни, но в целом идея проверки уже пропатченного кода — отличная метрика для личных бенчмарков в контексте безопасной разработки.
В качестве дальнейшего исследования особый интерес вызвали бы замеры качества моделей малых размеров (1.5B–8B), которые лучше подходят для использования в промышленной разработке.
Оценка по уровню сложности кода (4.6. Code Difficulty Levels)
Авторы проанализировали, как сложность кода влияет на результаты. Итог закономерный: модели лучше справляются с простыми фрагментами кода, исходное предположение подтвердилось.
Но интересным мне показалось следующее. Если я правильно все понял, исследователи допустили ошибку в одном из сниппетов кода и случайно выбрали именно его для разбора в статье. Давайте рассмотрим пример.

Авторы обращают внимание, что LLM обнаруживает уязвимости там, где код корректен: мол, у нас в createQuery параметризуется запрос, поэтому все безопасно.
Без доли сомнения в авторах статьи мне стало интересно, смогут ли модели справиться с задачей сейчас, спустя некоторое время после публикации исследования. Я отправил этот кусок кода без замысловатого промпта в GPT-4o — и (о боже!) следующее поколение LLM тоже попалось в ловушку.

Я решил подсказать модели, что, мол, так и так, мы передаем запрос и параметры в методе createQuery, обрати внимание на возвращаемые значения, — и она обратила. Но только указала, что тут не инъекция, а ошибка в коде, поскольку мы передаем в cursor.execute значение, имеющее тип «Кортеж» (tuple). Ноги в руки — и бегом проверять! После 30 минут вайбкодинга получилось сделать воспроизводимый PoC, подтверждающий наличие ошибки в эксперименте.
Можно ли засчитать результат работы GPT за ошибку — вопрос спорный. Модель просто изучила код не так подробно, как хотелось бы, и, предположив, что в коде ошибок нет (ведь такой задачи у LLM не было, вопрос стоял строго в плоскости обнаружения уязвимости), подсветила явный недостаток: мы в каком-то методе сформировали запрос без параметров в возвращаемом значении и подали на выполнение.
Стабильность при изменениях в коде (4.7. Robustness to Code Augmentations)
В гипотезе утверждается, что модели нестабильны по отношению к различного рода аугментациям (условиям, в которых код имеет неточности, спецсимволы, неиспользуемые методы и т. п.).

Ни одна из моделей не смогла стабильно отработать на всех видах аугментаций. Наиболее стабильные результаты продемонстрировали Code Llama 34B и GPT-4.

Один из примеров, разобранных в статье, — это отвлечение модели от переполнения буфера в случае добавления безопасных методов, которые тем не менее на логику переполнения не влияют (уязвимый код, патч).

Тут логика заключалась в том, что вне зависимости от выбора метода переполнение возникает в другом месте, а именно — в условии замены &, для которого выполняется расширение строки не на 4 символа, а на 5 (что нарушает логику выделения памяти с запасом ×4).
Саммари по задаче аугментации:
Our experiments show that there is no prompting technique that is completely robust as our robustness tests break even the best types of prompting techniques and chain-of-thought for all LLMs, leading to incorrect responses (17% of cases for GPT-4).
Тест на реальных уязвимостях (4.8. Real-World Cases)
В последнем эксперименте авторы выбрали несколько уязвимостей, обнаруженных в open-source проектах, для оценки качества работы LLM на поставленной задаче и получили следующие выводы:
Модели плохо сумели экстраполировать few-shot-техники с примерами тех же классов уязвимостей на техники для других языков/фреймворков.
Zero-shot промпты с доменными инструкциями CoT показали себя лучше других.
Тем не менее ни одна модель не продемонстрировала достаточной надежности для обнаружения уязвимого кода.
Стоит также отметить, что авторы столкнулись с проблемой — ограниченной длиной контекста, из-за которой им пришлось предварительно удалить комментарии и код методов, не относящихся к уязвимости.
Заключение
На мой взгляд, авторы анализируемой статьи заложили хороший фундамент для оценки языковых моделей в части решения задач информационной безопасности. В отличие от многих других доменных бенчмарков, они привели восемь гипотез. Отдельно я хотел бы выделить согласованность размышлений и ответов модели, проверку на чистых примерах и аугментацию данных для оценки стабильности вердиктов.
Бенчмарк полезен, но узок по языков и классов CWE, а разовая «фотография» качества быстро устаревает из‑за темпа релизов моделей (в том числе используемых в качестве судьи). Поэтому он нуждается в постоянном обновлении и расширении, а выводы требуют осторожной экстраполяции.
Если говорить о результатах исследования, то на момент его публикации LLM не были готовы к автономному поиску уязвимостей. Доменные zero‑shot‑инструкции с явной цепочкой рассуждений лучше в соотношении «качество — стоимость», однако универсального промпта нет. Даже сильные модели подвержены стресс‑аугментациям, что ограничивает надежность.
Если вам понравился разбор и вы интересуетесь темой ИИ, то также могу порекомендовать:
✅ Telegram-канал notes.ml, где я пишу про всякое разное вокруг AI, LLM, ML.
✅ Совсем свежий канал False Positive, который ведем мы с коллегами. Планируем выкладывать туда видео и текстовые материалы с обзорами статей, технических репортов и конференций.