Привет, Хабр!

На связи Максим Митрофанов, 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 восьми точек зрения:

    1. Детерминированность ответов. 

    2. Значимость параметров запросов.

    3. Подбор промптов.

    4. Согласованность размышлений (reasoning) моделей с бинарным ответом.

    5. Оценка классификации кода на наличие уязвимостей.

    6. Уровень сложности кода.

    7. Устойчивость к изменениям в коде. 

    8. Оценка реальных проектов на уязвимости.

  • К тексту приложена ссылка на репозиторий с датасетом и исходным кодом.

Эту статью тяжело назвать новым стандартом оценки языковых моделей, но количество интересных решений и противоречивых тезисов оказалось достаточно большим, чтобы написать ревью.

Немного о данных, моделях и способах оценки качества

Авторы статьи составили 228 уязвимых сниппетов кода:

  • 48 самописных примеров, покрывающих 8 классов CWE (24 уязвимых и столько же исправленных).

  • 30 реальных CVE, связанных с выбранными CWE (15 уязвимых и столько же исправленных).

  • 150 видоизмененных самописных примеров, составленных из первоначальных 48 (механику и назначение этих примеров рассмотрим позднее в обзоре эксперимента по восприятию LLM аугментации кода).

Датасет с самописными уязвимыми примерами (в оригинале  так называется Таблица 4)
Датасет с самописными уязвимыми примерами (в оригинале так называется Таблица 4)

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

Наиболее популярные языки программирования, используемые в связке с инструментами Anthropic
Наиболее популярные языки программирования, используемые в связке с инструментами Anthropic

Вторым дискуссионным вопросом, связанным с полнотой набора данных, является достаточность использования 8 CWE более чем из 900 категорий.

Из положительного — к статье приложена ссылка на GitHub-репозиторий, который позволяет расширить бенчмарк при желании и необходимости.

Перейдем к моделям, которые были использованы при написании статьи. Авторы представляют результаты для пяти моделей (еще три вынесли в приложение). Общий список — ниже.

Тестируемые языковые модели и их характеристики. Авторы выбрали языковые модели, различные по ряду параметров: открытость архитектуры модели, количество гиперпараметров, длина контекста и дата последних источников обучающего набора данных. Дата проведения экспериментов: 7 октября 2023 года (в оригинальной статье Таблица 1)
Тестируемые языковые модели и их характеристики. Авторы выбрали языковые модели, различные по ряду параметров: открытость архитектуры модели, количество гиперпараметров, длина контекста и дата последних источников обучающего набора данных. Дата проведения экспериментов: 7 октября 2023 года (в оригинальной статье Таблица 1)

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

Перейдем к системе оценки качества, которая в общем виде представлена в виде схемы.

Архитектура фреймворка SecLLMHolmes (в оригинальной статье График 2)
Архитектура фреймворка SecLLMHolmes (в оригинальной статье График 2)

Как это читать? Сейчас расскажу. Берем одну из гипотез (блок 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, они делают вывод, что нулевая температура лучше. Но если посмотреть внимательно на таблицы, то разница будто бы незначительная.

Результаты экспериментов стабильности ответов с рекомендуемыми параметрами температуры (0,2). Таблица содержит результаты в разрезе исследуемого CWE (CWE-787 на скриншоте статьи) и каждого из стандартных промптов (S1-S6) в формате: количество корректных (в статье таблица 7)
Результаты экспериментов стабильности ответов с рекомендуемыми параметрами температуры (0,2). Таблица содержит результаты в разрезе исследуемого CWE (CWE-787 на скриншоте статьи) и каждого из стандартных промптов (S1-S6) в формате: количество корректных (в статье таблица 7)
Результаты экспериментов стабильности ответов при температуре = 0.0 (в статье таблица 8)
Результаты экспериментов стабильности ответов при температуре = 0.0 (в статье таблица 8)

Здесь хотел бы обратить внимание на следующее: учитывая, что 2v/2p — это уязвимые/пропатченные (vulnerable/patched) версии кода уровня сложности — medium, авторы уже дают небольшой спойлер о проблемах моделей в умении их различать.

К слову, вопрос подбора параметров затрагивается в свежей cookbook-статье Google, где среди прочего приведены заметки для кейсов применения пограничных значений, — настоятельно рекомендую к прочтению.

Гипотеза 4.2. Performance Over Range of Parameters по своей сути является расширением первой и проверяет на схожих CWE стабильность языковых моделей в сфере размышления и классификации уязвимого кода, но на большем диапазоне параметров. Выводы совпадают с выводами из предыдущего теста, поэтому предлагаю не задерживаться и идти дальше.

Техники промптинга (4.3. Diversity of Prompts)

На мой взгляд, показательный параграф с точки зрения как широты эксперимента, так и визуализации результатов.

Оценка языковых моделей в зависимости от используемых инструкций на 48 примерах уязвимого кода. Зелёные и красные полосы показывают количество сценариев с правильными и неправильными ответами для каждой LLM (показатель AccuracyRate). Белый кружок отмечает сценарии, где правильными были и ответ, и рассуждение (CRR). Кроме того, авторы выделяют лучшие промпты для каждой техники: ZS-TO (оранжевый), ZS-RO (жёлтый), FS-TO (зелёный), FS-RO (голубой). Лучшая инструкция отмечена красной рамкой (в статье Таблица 11)
Оценка языковых моделей в зависимости от используемых инструкций на 48 примерах уязвимого кода. Зелёные и красные полосы показывают количество сценариев с правильными и неправильными ответами для каждой LLM (показатель AccuracyRate). Белый кружок отмечает сценарии, где правильными были и ответ, и рассуждение (CRR). Кроме того, авторы выделяют лучшие промпты для каждой техники: ZS-TO (оранжевый), ZS-RO (жёлтый), FS-TO (зелёный), FS-RO (голубой). Лучшая инструкция отмечена красной рамкой (в статье Таблица 11)

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

Промпт R5 FS-RO — инструкция с примерами выполнения аналогичного запроса (FS — few-shot) и доменными особенностями описания (RO — role-oriented, R5 — reasoning-based variant 5)
Промпт R5 FS-RO — инструкция с примерами выполнения аналогичного запроса (FS — few-shot) и доменными особенностями описания (RO — role-oriented, R5 — reasoning-based variant 5)
Для желающих залипнуть минут на 30...

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

Шаблоны инструкций (промптов) (в статье Таблица 3)
Шаблоны инструкций (промптов) (в статье Таблица 3)

 А для тех, кто экономит время, приведу свои заметки: 

  • Не существует серебряной пули в виде лучшего промпта для любой архитектуры 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), либо одну модель с возможностью настраивать уровень самостоятельно. Как предложение для дальнейших исследований — было бы интересно измерить эти показатели на моделях одного поколения с учетом этого параметра.

Последовательность в принятии решений языковых моделей. Таблица иллюстрирует качество размышлений LLM, рассчитанное как отношение согласованных с ответом размышлений модели к общему количеству успешных запросов к LLM (общее число запросов — 816). Зеленый цвет — решение и размышление модели совпали с правильным ответом; желтый цвет — модель приняла корректное решение при неверной цепочке размышлений; красный цвет — принятое решение и размышления не совпали с правильным ответом; синий цвет — модель приняла неправильное решение при верных размышлениях (в статье Таблица 12)
Последовательность в принятии решений языковых моделей. Таблица иллюстрирует качество размышлений LLM, рассчитанное как отношение согласованных с ответом размышлений модели к общему количеству успешных запросов к LLM (общее число запросов — 816). Зеленый цвет — решение и размышление модели совпали с правильным ответом; желтый цвет — модель приняла корректное решение при неверной цепочке размышлений; красный цвет — принятое решение и размышления не совпали с правильным ответом; синий цвет — модель приняла неправильное решение при верных размышлениях (в статье Таблица 12)

Способность обнаруживать уязвимый код (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)

Авторы проанализировали, как сложность кода влияет на результаты. Итог закономерный: модели лучше справляются с простыми фрагментами кода, исходное предположение подтвердилось.

Но интересным мне показалось следующее. Если я правильно все понял, исследователи допустили ошибку в одном из сниппетов кода и случайно выбрали именно его для разбора в статье. Давайте рассмотрим пример.

 GPT-4 ошибается как в решении, так и в размышлениях относительно третьего примера категории CWE-89 с исправленной уязвимостью (3p = 3 scenario of patched code) (в статье Рисунок 6)
 GPT-4 ошибается как в решении, так и в размышлениях относительно третьего примера категории CWE-89 с исправленной уязвимостью (3p = 3 scenario of patched code) (в статье Рисунок 6)

Авторы обращают внимание, что LLM обнаруживает уязвимости там, где код корректен: мол, у нас в createQuery параметризуется запрос, поэтому все безопасно.

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

Ответ GPT-4o
Ответ GPT-4o

Я решил подсказать модели, что, мол, так и так, мы передаем запрос и параметры в методе createQuery, обрати внимание на возвращаемые значения, — и она обратила. Но только указала, что тут не инъекция, а ошибка в коде, поскольку мы передаем в cursor.execute значение, имеющее тип «Кортеж» (tuple). Ноги в руки — и бегом проверять! После 30 минут вайбкодинга получилось сделать воспроизводимый PoC, подтверждающий наличие ошибки в эксперименте.

Можно ли засчитать результат работы GPT за ошибку — вопрос спорный. Модель просто изучила код не так подробно, как хотелось бы, и, предположив, что в коде ошибок нет (ведь такой задачи у LLM не было, вопрос стоял строго в плоскости обнаружения уязвимости), подсветила явный недостаток: мы в каком-то методе сформировали запрос без параметров в возвращаемом значении и подали на выполнение

Стабильность при изменениях в коде (4.7. Robustness to Code Augmentations)

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

Описание типов аугментации кода (в статье Таблица 6)
Описание типов аугментации кода (в статье Таблица 6)

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

Результаты экспериментов с аугментацией кода. В таблице приведены значения, иллюстрирующие то, насколько модель стала хуже работать с кодом до аугментации (числитель) к коду до каких либо изменений (знаменатель). Эксперимент измерял как влияние на конечный ответ (a — answer), так и на размышления модели (r — reasoning). Для каждой модели были выбраны три вида промпта: базовый (S1), лучший среди zero-shot и лучший среди few-shot (в статье таблица 15)
Результаты экспериментов с аугментацией кода. В таблице приведены значения, иллюстрирующие то, насколько модель стала хуже работать с кодом до аугментации (числитель) к коду до каких либо изменений (знаменатель). Эксперимент измерял как влияние на конечный ответ (a — answer), так и на размышления модели (r — reasoning). Для каждой модели были выбраны три вида промпта: базовый (S1), лучший среди zero-shot и лучший среди few-shot (в статье таблица 15)

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

Пример аугментации NT-5 для CWE-787, где наличие метода strcat (является уязвимым методом, но не в контексте примера) запутали модель GPT-4, так как его замена на безопасный метод strncat с сохранением настоящей уязвимости примера сбивает модель, которая меняет свой ответ (в статье Рисунок 7 b)
Пример аугментации NT-5 для CWE-787, где наличие метода strcat (является уязвимым методом, но не в контексте примера) запутали модель GPT-4, так как его замена на безопасный метод strncat с сохранением настоящей уязвимости примера сбивает модель, которая меняет свой ответ (в статье Рисунок 7 b)

Тут логика заключалась в том, что вне зависимости от выбора метода переполнение возникает в другом месте, а именно — в условии замены &, для которого выполняется расширение строки не на 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, который ведем мы с коллегами. Планируем выкладывать туда видео и текстовые материалы с обзорами статей, технических репортов и конференций.

Комментарии (0)