Почему Codex Security не включает в себя отчет SAST

OAI_Why_Codex_Security_Doesn%C3%A2__t_In

На протяжении десятилетий статическое тестирование безопасности приложений (SAST) оставалось одним из наиболее эффективных способов масштабирования проверки кода для команд безопасности.

Но когда мы создавали Codex Security, мы сделали осознанный выбор в пользу определенной архитектуры: мы не стали начинать с импорта отчета статического анализа и отправки его агенту для триажа. Мы спроектировали систему так, чтобы она начинала работу с самого репозитория — его архитектуры, границ доверия и предполагаемого поведения — и проверяла найденное перед тем, как привлекать человека.

Причина проста: самые сложные уязвимости обычно не сводятся к проблемам потока данных. Они возникают тогда, когда в коде вроде бы предусмотрена проверка безопасности, но на самом деле эта проверка не гарантирует то свойство, на которое полагается система. Иными словами, сложность заключается не только в отслеживании движения данных по программе, а в определении того, действительно ли средства защиты в коде работают.

Проблема: SAST оптимизирован для потоков данных

Инструменты SAST часто представляют в виде понятного конвейера: определить источник недоверенного ввода, проследить за данными в программе и отметить случаи, когда эти данные доходят до уязвимого приемника (sink) без санитизации. Это элегантная модель, которая покрывает множество реальных багов.

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

Сама по себе эта особенность не является причиной, почему Codex Security не начинает работу с отчета SAST.

Более глубокая проблема заключается в том, что происходит после успешного отслеживания пути от источника к приемнику.

Где статический анализ сталкивается с трудностями: ограничения и семантика

Даже когда статический анализ корректно отслеживает входные данные через множество функций и слоев, ему все равно приходится отвечать на вопрос, который фактически определяет наличие уязвимости:

Действительно ли защита сработала?

Рассмотрим распространенный паттерн: код вызывает что-то вроде sanitize_html() перед выводом недоверенного контента. Статический анализатор видит, что функция санитизации выполнилась. Однако он обычно не может определить, действительно ли эта санитизация достаточна для конкретного контекста отрисовки, шаблонизатора, особенностей кодирования и последующих преобразований.

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

Иными словами: существует огромная разница между «код вызывает функцию санитизации» и «система находится в безопасности».

Пример: валидация до декодирования

Вот паттерн, который постоянно встречается в реальных системах.

Веб-приложение получает полезную нагрузку JSON, извлекает из нее redirect_url, проверяет с помощью регулярного выражения из белого списка, декодирует URL и передает результат обработчику перенаправлений (редиректов).

Классический отчет от источника к приемнику может описать этот поток следующим образом:

untrusted input → regex check → URL decode → redirect

Но настоящий вопрос заключается не в том, существует ли проверка. А в том, продолжает ли эта проверка ограничивать значение после последующих преобразований.

Если регулярное выражение выполняется до декодирования, ограничивает ли оно декодированный URL именно так, как его интерпретирует обработчик редиректов?

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

Многие важные на практике уязвимости выглядят именно так: ошибки в порядке операций, частичная нормализация, неоднозначности парсинга и несоответствия между валидацией и интерпретацией. Поток данных виден. Слабое место кроется в том, как ограничения распространяются — или не распространяются — по цепочке преобразований.

Это не просто теоретический паттерн. В CVE-2024–29041 фреймворк Express пострадал от проблемы открытого перенаправления (open redirect), где некорректно сформированные URL могли обходить стандартные реализации белых списков из-за того, как цели редиректов кодировались, а затем интерпретировались. Поток данных был прост. Более сложный вопрос — и тот, который определял наличие бага — заключался в том, сохранялась ли валидация после цепочки преобразований.

Наш подход: исходить из поведения, затем выполнять валидацию

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

Когда Codex Security сталкивается с границей, похожей на «валидацию» или «санитизацию», система не относится к ней как к галочке в чек-листе. Она пытается понять, что именно код пытается гарантировать, а затем пытается опровергнуть эту гарантию.

На практике это обычно выглядит как комбинация следующих действий:

  • Чтение соответствующего пути в коде с полным контекстом репозитория — так, как это сделал бы специалист по безопасности — в поисках несоответствий между намерениями разработчика и реализацией. Сюда входят комментарии, однако модель не слепо верит комментариям: добавление над вашим кодом фразы //Халвар говорит: это не баг не сбьет ее с толку, если баг действительно есть.
  • Сведение проблемы к наименьшему тестируемому фрагменту (например, конвейеру преобразований для одного входного значения), чтобы ее можно было проанализировать в отрыве от остальной системы. В этом смысле Codex Security выделяет крошечные фрагменты кода и создает для них микрофаззеры.
  • Анализ ограничений сквозь цепочки преобразований, а не рассмотрение каждой проверки по отдельности. Где это уместно, может применяться формализация в виде задачи выполнимости (satisfiability). Иными словами, мы предоставляем модели доступ к окружению Python с библиотекой z3-solver, и она отлично умеет использовать ее при необходимости — точно так же, как пришлось бы действовать человеку при решении особенно сложной задачи на ограничения вводимых данных. Это особенно полезно при поиске целочисленных переполнений (integer overflows) или аналогичных багов на нестандартных архитектурах.
  • Выполнение гипотез в изолированной (песочнице) среде валидации, когда это возможно, чтобы отделить статус «это потенциально может быть проблемой» от «это реальная проблема». Нет лучшего доказательства, чем полноценный рабочий PoC с кодом, скомпилированным в режиме отладки.

В этом и заключается ключевой сдвиг: вместо того чтобы останавливаться на факте «проверка существует», система продвигается до состояния «инвариант выполняется (или не выполняется), и вот доказательства». При этом модель сама выбирает лучший инструмент для выполнения этой задачи.

Почему мы не передаем отчет SAST на вход Codex Security

Логичной реакцией может быть вопрос: почему бы не использовать оба подхода? Начать с отчета SAST, а затем задействовать агента для более глубокого анализа.

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

Во-первых, это может стимулировать преждевременное сужение фокуса. Список найденных проблем — это карта того, где инструмент уже побывал. Если воспринимать его как отправную точку, можно сместить усилия системы в те же самые области с теми же абстракциями, упустив целые классы проблем, которые не вписываются в картину мира конкретного инструмента.

Во-вторых, это привносит неявные суждения, от которых трудно избавиться. Многие находки SAST содержат в себе зашитые предположения о санитизации, валидации или границах доверия. Если эти предположения ошибочны — или просто неполны — их внедрение в цикл рассуждений может переключить агента из режима «расследовать» в режим «подтвердить или отклонить», чего мы вовсе от него не добиваемся.

В-третьих, это затрудняет оценку работы самой системы рассуждений. Если конвейер начинается с вывода SAST, становится трудно отделить то, что агент обнаружил благодаря собственному анализу, от того, что он унаследовал от другого инструмента. Такое разделение критически важно для точного измерения возможностей системы, что необходимо для ее дальнейшего совершенствования.

Поэтому мы создали Codex Security так, чтобы она начинала там, где начинается исследование безопасности: с кода и замысла системы, используя валидацию для повышения планки достоверности перед тем, как отвлекать человека.

Инструменты SAST по-прежнему очень важны

Инструменты SAST отлично справляются со своими задачами: обеспечивают соблюдение стандартов безопасного кодирования, выявляют простые проблемы в цепочке от источника к приемнику и обнаруживают известные паттерны в больших масштабах с прогнозируемыми компромиссами. Они могут быть надежным элементом многоуровневой защиты (defense-in-depth).

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

Стоит также упомянуть связанное с этим ограничение чисто концептуального подхода «от источника к приемнику»: далеко не каждая уязвимость является проблемой потока данных. Многие реальные сбои связаны с состоянием и инвариантами — обходами рабочих процессов (workflow bypasses), брешами в авторизации и багами вида «система находится в неправильном состоянии». Для таких типов уязвимостей испачканное (tainted) значение не доходит до какого-то одного «опасного приемника». Риск кроется в том, что именно программа считает неизменно верным.

Взгляд в будущее

Мы ожидаем, что экосистема инструментов безопасности продолжит развиваться: статический анализ, фаззинг, средства защиты времени выполнения и агентские рабочие процессы займут свои места.

Мы хотим, чтобы Codex Security преуспела в том, что обходится командам безопасности дороже всего: в превращении утверждения «это выглядит подозрительно» в формулировку «это реальная проблема, вот как она проявляется и вот исправление, соответствующее замыслу системы».

Если вы хотите узнать больше о том, как Codex Security сканирует репозитории, проверяет результаты и предлагает исправления, обратитесь к нашей документации.

Автор

OpenAI

Полный текст статьи читайте на OpenAI