У классических инструментов статического анализа безопасности (SAST) есть две хронические проблемы: высокий уровень ложноположительных срабатываний (false positives) и изоляция от повседневного рабочего окружения разработчика. Когда сканер запускается обособленно на CI/CD и выгружает огромный отчет, разработчику приходится вручную восстанавливать путь данных по коду через десятки классов и методов, чтобы просто понять - перед ним реальная уязвимость или FP.

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

TL;DR

В релизе 5.11 мы добавили Veai SAST - это нативная интеграция инструмента статического анализа безопасности в наш плагин. Анализ можно запустить прямо из чата: агент соберет проект, запустит инструмент и поможет вам разобраться с результатами его работы. Плагин делает трассировку интерактивной: непосредственно в IDE вы можете пройти от точки входа (Source) до потенциально опасной операции (Sink), прикрепить находку к чату с агентом, обсудить реальный вектор атаки, оценить уязвимость по шкале CVSS, сгенерировать точечный патч для ее устранения и написать penentration/regression.

Почему классический разбор SAST-находок — это боль

Сценарий, знакомый многим разработчикам в крупных компаниях: перед релизом ИБ-отдел присылает выгрузку из корпоративного сканера. В этом PDF-файле на сотни страниц собраны тонны находок. Большая часть из них снабжена метками вроде Critical. И вот, вместо скроллинга ленты подготовки релиза разработчик сталкивается с трудоемким процессом анализа:

  • Открыть указанный в отчете файл

  • Обнаружить там какую-нибудь generic-функцию, которая принимает строку и склеивает SQL-запрос

  • Раскопать через цепочки вызовов, откуда вообще берутся данные. Приходят ли они напрямую от пользователя из REST-контроллера, контролируются ли они внутренней конфигурацией, или это вообще внутренний технический ID, который заведомо безопасен?

  • Разобраться, была ли где-то по дороге валидация или экранирование

  • Решить, действительно ли это эксплуатируемая уязвимость, или ее появление в отчете вызвано ложным срабатыванием из-за неполноты контекста анализатора.

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

Как это выглядит в Veai

Чтобы разработчику не приходилось прыгать между разрозненными инструментами, мы постарались упаковать весь цикл анализа и исправлений в единый инструментарий внутри JetBrains IDE (IntelliJ IDEA, GigaIDE, OpenIDE или Android Studio). Опишем по шагам, как это работает на практике.

  • Запуск одной кнопкой. В боковой панели чата Veai достаточно нажать кнопку со щитом. Процесс не требует ручного составления сложных конфигурационных файлов YAML или прописывания путей к бинарникам.

  • Сборка и анализ. Плагин в фоновом режиме запускает Gradle или Maven сборку, строит модель зависимостей, после чего выполняет локальный запуск SAST-движка.

  • Дерево результатов. Список находок выводится в отдельную панель IDE. Уязвимости можно группировать по критичности (High/Medium/Low), CWE или структурировать по модулям проекта.

  • Интерактивный граф вызовов. Для уязвимостей, связанных с потоком данных (data-flow), плагин строит пошаговую трассировку. Клик на любой шаг цепочки в панели мгновенно переносит фокус редактора IDE на соответствующее место в коде: например, на REST-контроллер с точкой входа или на репозиторий с опасным вызовом.

  • Бесшовный переход к чату. Каждую находку можно прикрепить к чату с нашим агентом. Это исключает необходимость копирования фрагментов кода вручную: агент сразу получает доступ ко всему графу вызовов и готов анализировать конкретную проблему.

    На самой уязвимости прямо в панели выведены кнопки быстрого действия:

    • «Исправить». Агент читает полный отчет о потенциальной уязвимости и предлагает точечный патч.

    • «Оценить критичность». Агент помогает оценить критичность находки по шкале CVSS

    • «Пометить как False Positive». Если вы вместе с агентом доказали безопасность находки, ее можно скрыть из результатов.

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

Почему для эффективного поиска уязвимостей изолированного использования LLM и классического SAST недостаточно

Реализация автоматического поиска уязвимостей непосредственно в AI-плагине для IDE неизбежно поднимает вопрос: что эффективнее для анализа безопасности — большие языковые модели (LLM) или классические системы статического анализа (SAST)? На практике противопоставление этих подходов некорректно: в больших enterprise-проектах ни один из них не обеспечивает приемлемого качества анализа в изоляции. Большинство критичных уязвимостей в backend-приложениях представляют собой не локальные ошибки, а сквозные data-flow сценарии: пользовательские данные проходят через десятки слоев архитектуры, трансформируются, валидируются, сериализуются и лишь затем достигают опасной операции (sink). Для корректного обнаружения такой уязвимости системе необходимо одновременно понимать структуру проекта, граф вызовов, поток данных и семантику бизнес-логики.

Ограничения LLM-подхода

Большие языковые модели хорошо работают на локальных задачах: ревью коммитов, поиске insecure API usage, анализе небольших фрагментов кода или генерации remediation-рекомендаций. Однако при попытке выполнять полноценный межпроцедурный анализ крупных JVM-проектов возникают фундаментальные ограничения.

Ограниченный и нестабильный контекст

Даже при использовании retrieval-based подходов модель оперирует лишь частью кодовой базы. По мере роста объема контекста ухудшается локализация релевантной информации: модель начинает терять связи между слоями приложения, упускать важные условия валидации, ветвления логики и особенности сигнатур методов. В результате снижается точность reasoning над длинными data-flow цепочками.

Отсутствие полной проектной семантики

Standalone LLM не обладает полноценной моделью проекта. Без специализированного semantic layer модель не может надежно восстановить граф зависимостей Spring Dependency Injection, корректно сопоставить интерфейсы с конкретными реализациями, разрешить dynamic dispatch или учитывать framework-specific особенности runtime-поведения. Это приводит к ложным предположениям и semantic hallucinations при анализе изолированных фрагментов кода.

Высокая вычислительная стоимость

Полноценный анализ large-scale codebase силами LLM требует значительного количества токенов, retrieval-операций и повторных проходов по контексту. Для enterprise-репозиториев это быстро превращается в дорогой и медленный процесс, плохо подходящий для интерактивной работы внутри IDE.

Отсутствие детерминированности

Результат анализа зависит от prompt engineering, выбранной модели, контекста и даже порядка переданных файлов. Одна и та же уязвимость может быть обнаружена в одном запуске и пропущена в другом. Это затрудняет воспроизводимость анализа и использование LLM как единственного источника security-сигналов.

Ограничения классических SAST-систем

Традиционные системы статического анализа строятся на детерминированных алгоритмах: AST/IR-моделях, межпроцедурном анализе графов вызовов и taint-tracking. Такой подход обеспечивает предсказуемость и масштабируемость, однако в современных enterprise-приложениях сталкивается с рядом фундаментальных ограничений.

Неполная модель runtime-поведения

Современные JVM-приложения активно используют Dependency Injection, reflection, dynamic proxies, code generation, asynchronous execution и coroutine-механизмы. Многие связи между компонентами формируются лишь во время выполнения приложения. В результате статический анализ вынужден либо терять часть call edges, либо агрессивно переаппроксимировать граф вызовов, что напрямую влияет на качество taint-анализа и количество false-positive и true-negative результатов

Высокий уровень false positives

Детерминированный анализ хорошо выявляет потенциально опасные конструкции, но слабо понимает бизнес-семантику приложения. Любая динамическая сборка SQL-запроса, URL или shell-команды может быть помечена как критическая уязвимость, даже если входные данные ограничены безопасным enum-значением, внутренним идентификатором или проходят framework-specific sanitization. В больших проектах это приводит к лавинообразному росту ложных срабатываний и превращает triage в отдельную инженерную проблему.

Ограниченность framework-specific анализа

Каждый enterprise-стек содержит собственные abstraction layers, DSL и инфраструктурные паттерны. Для качественного анализа SAST-системе требуется поддержка конкретных framework-моделей, sanitizer-правил, ORM-цепочек и custom API. Поддерживать такую модель вручную крайне дорого, а отсутствие даже части semantic knowledge резко снижает точность анализа.

Проблемы масштабирования precision-анализа

Повышение точности статического анализа почти всегда приводит к экспоненциальному росту вычислительной сложности. Более глубокий path-sensitive или context-sensitive анализ становится слишком дорогим для регулярного запуска внутри CI/CD или IDE. Поэтому многие production-SAST решения сознательно упрощают модель анализа, жертвуя точностью ради производительности.

Отсутствие понимания намерений разработчика

Классический SAST способен определить потенциально опасный data-flow, но практически не понимает контекст изменений, бизнес-инварианты или архитектурные намерения разработчика. Из-за этого анализ часто лишен приоритизации и не способен отличить реально exploitable vulnerability от технически подозрительной, но безопасной конструкции.

Гибридный пайплайн: как Veai объединяет SAST и AI-агента

В основе Veai SAST лежит гибридный конвейер, в котором детерминированный статический анализ и AI-агент выполняют разные задачи и дополняют друг друга. Классический SAST хорошо справляется со структурным анализом проекта: он умеет строить граф вызовов, отслеживать движение данных между слоями приложения и находить потенциально опасные data-flow цепочки. Однако сам по себе такой анализ плохо интерпретирует бизнес-контекст и часто генерирует большое количество шумных алертов. LLM, напротив, хорошо понимают семантику кода и способны рассуждать о реалистичности эксплуатации уязвимости, но плохо масштабируются на анализ всей enterprise-кодовой базы. В Veai эти подходы работают последовательно: SAST-движок сначала выделяет узкий, релевантный security-контекст, после чего AI-агент может выполнить интеллектуальный анализ уже поверх готового data-flow трека.

Фаза 1. Локальный SAST-анализ и построение security-контекста

На первом этапе локальный SAST-анализатор собирает проект через Gradle или Maven, разрешает зависимости и строит внутреннюю модель приложения: контроллеров, сервисов, репозиториев и связей между ними.После этого движок выполняет структурный анализ data-flow и taint propagation - отслеживает движение пользовательского ввода через различные слои системы к потенциально опасным sink-операциям: SQL-запросам, файловой системе, шаблонизаторам, HTTP-клиентам или системным вызовам. Результатом этой стадии становится не полный дамп кодовой базы, а изолированный security-контекст: компактный taint-трек размером примерно 20–50 строк кода вместе с metadata анализа — CWE-классификацией, точкой входа, sink-операцией и цепочкой вызовов.Таким образом, SAST-движок выступает как механизм высокоточной фильтрации и компрессии контекста перед передачей данных AI-агенту.

Фаза 2. AI reasoning, exploitability и remediation

После формирования security-контекста управление передается Veai-агенту. В отличие от standalone LLM-подходов, модель работает не со случайными фрагментами проекта, а с уже подготовленным и структурированным контекстом: taint-треком, call chain, типами параметров, CWE metadata и информацией о точке входа и sink-операции. Это позволяет существенно повысить качество semantic reasoning, снизить количество hallucinations и сфокусировать анализ только на реально релевантном коде.На этом этапе AI-агент может помочь со следующими задачами:

  • анализ бизнес-контекста вокруг потенциальной уязвимости

  • объяснение разработчику причины возникновения проблемы

  • генерация патча

  • формирование pentest- и regression-сценариев для проверки фикса

  • оценка риска и критичности находки по CVSS

Практический пример: разбор SSRF-уязвимости

Чтобы показать механику не абстрактно, разберем реальный сценарий из opensource Java проекта Klaw: SAST обнаруживает, что значение из объекта запроса используется при формировании URL для исходящего HTTP-запроса. Такие уязвимости относятся к классу Server-Side Request Forgery (SSRF). Они возникают, когда пользователь, внешний upstream-сервис или другой недоверенный источник может повлиять на адрес, к которому сервер выполняет сетевой запрос. В результате приложение может быть использовано как прокси для обращения к внутренним сервисам, metadata endpoint’ам, закрытым API или другим недоступным извне ресурсам.

Исходный сценарий

В примере можно выделить три ключевые точки:

  • Source — HTTP-запрос в cluster-api попадает в backend и десериализуется в объект ClusterTopicRequest.

  • Propagation — значение передается через сервисный метод deleteTopic.

  • Sink — значение используется при сборке URL, который затем передается в RestTemplate.exchange.

Упрощенный фрагмент кода выглядит так:

public ApiResponse deleteTopic(ClusterTopicRequest clusterTopicRequest) throws Exception {
    RestTemplate restTemplate = getRestTemplate();

    String deleteTopicsUri =
        getResourceUri(
            clusterTopicRequest.getClusterName(),
            clusterTopicRequest.getEnv(),
            "deleteTopic",
            ResourceType.TOPIC.name());

    deleteTopicsUri = deleteTopicsUri + "/" + clusterTopicRequest.getTopicName();

    HttpHeaders headers =
        clusterApiUtils.createHeaders(
            clusterTopicRequest.getClusterName(), KafkaClustersType.KAFKA);

    HttpEntity<String> request = new HttpEntity<>(headers);

    restTemplate.exchange(deleteTopicsUri, HttpMethod.DELETE, request, String.class);

    return ApiResponse.builder().success(true).message(ApiResultStatus.SUCCESS.value).build();
}

Опасное место находится не только в самом вызове RestTemplate, а в том, как формируется URL:

private String getResourceUri(
    String clusterIdentification, String host, String requestMethod, String requestType)
    throws Exception {

    String relevantUri;

    if (ResourceType.TOPIC.name().equals(requestType)) {
        relevantUri = env.getProperty(clusterIdentification.toLowerCase() + TOPIC_API_URI_KEY);
    } else {
        relevantUri = env.getProperty(clusterIdentification.toLowerCase() + ACLS_API_URI_KEY);
    }

    relevantUri = HTTPS_PREFIX + host + relevantUri;

    return relevantUri;
}

Здесь параметр host приходит из clusterTopicRequest.getEnv(). В контексте cluster-api это поле является частью request body, поэтому SAST корректно рассматривает его как потенциально недоверенное. При этом для triage важно проверить реальный production-flow: в Klaw такой запрос обычно формируется в core, где env заполняется серверным значением вроде kwClusters.getBootstrapServers(), а не напрямую вводом пользователя. Поэтому находку не стоит автоматически считать доказанной эксплуатацией. Более точная формулировка: риск SSRF реален, если cluster-api можно вызвать напрямую или если значение, попадающее в env, может быть изменено пользователем, администратором без строгого контроля, скомпрометированным upstream-компонентом или ошибочной конфигурацией.

Что показывает SAST

После запуска анализа Veai SAST строит taint-trace от входного объекта до сетевого sink. В отчете находка может выглядеть как последовательность шагов:

  • метод получает clusterTopicRequest как потенциально недоверенный объект

  • deleteTopic передает данные из clusterTopicRequest дальше

  • из объекта извлекается значение env

  • значение попадает в параметр host метода getResourceUri

  • host используется при сборке URL

  • собранный URL передается в RestTemplate.exchange.

На этом этапе разработчик видит не просто строку с HTTP-вызовом, а полный путь данных от внешнего входа до sink-операции.Это важно: сам по себе RestTemplate.exchange(...) не является уязвимостью. Уязвимость появляется тогда, когда адрес запроса формируется с участием данных, которые пересекают trust boundary и не имеют строгих destination controls: server-side mapping, allowlist, проверка схемы, хоста и IP-адреса.

Как в разбор включается AI-агент

После прикрепления находки к чату агент получает структурированный security-контекст:

  • CWE-классификацию

  • Source

  • Sink

  • call chain

  • файл и строку с потенциально опасным вызовом

  • описание правила SAST

  • фрагменты кода по трассировке

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

  • контролирует ли пользователь значение, которое попадает в host

  • доступен ли cluster-api только доверенному core или его можно вызвать напрямую

  • является ли env настоящим hostname, bootstrap server’ом из конфигурации или внутренним идентификатором окружения

  • проходит ли значение через allowlist или enum

  • маппится ли env на URL на стороне сервера

  • можно ли передать туда localhost, internal IP или metadata endpoint

  • есть ли риск обращения к внутренней инфраструктуре

  • какой минимальный патч устранит SSRF без изменения бизнес-логики.

Пример ответа агента в таком сценарии:

Потенциально это SSRF, но для triage нужно проверить источник env. В cluster-api поле env приходит из request body и используется как host при сборке URL https://{host}{configuredPath}, после чего URL передается в RestTemplate.exchange(...). Если cluster-api доступен не только доверенному core-сервису или если значение bootstrap servers может быть изменено пользователем/администратором без строгого контроля, риск реален. Надежный фикс — не использовать request field как host напрямую, а получать host по server-side mapping из проверенной конфигурации или валидировать его по строгому allowlist.

Фикс и проверка

Исправление должно убрать возможность использовать произвольный host. Для этого можно применить один из подходов:

  • не принимать host из request body вообще

  • использовать server-side mapping: clusterId/envId -> configured API host

  • проверять host по строгому allowlist

  • разрешать только https

  • запрещать loopback, private и link-local IP-адреса

  • собирать URI через безопасный builder после проверки destination

Таким образом, SAST находит потенциально опасный data-flow, AI-агент помогает быстро выполнить triage и отделить реальную эксплуатацию от условного риска на границе core → cluster-api, а regression-тест фиксирует результат и защищает от повторного появления уязвимости.

Что это меняет для процесса разработки

Главное отличие Veai SAST от классического подхода заключается не только в месте запуска анализа, а в изменении самого workflow. Уязвимость перестает быть строкой в большом отчете, который разработчик получает спустя несколько дней после написания кода. Вместо этого она становится интерактивным объектом внутри IDE: с трассировкой, объяснением, контекстом, оценкой риска и вариантом исправления. Такой подход особенно важен для больших Java/Kotlin-проектов, где стоимость ручного triage часто оказывается выше стоимости самого исправления. SAST-движок берет на себя детерминированную часть работы: построение графа вызовов, taint-tracking и поиск потенциально опасных data-flow цепочек. AI-агент дополняет этот результат semantic reasoning: помогает понять, достижим ли sink из внешнего входа, есть ли по пути sanitizer, насколько реалистична эксплуатация и какой фикс будет минимальным. В результате безопасность становится частью обычного цикла разработки. Разработчик может обнаружить проблему, понять ее контекст, обсудить exploitability, применить патч и добавить проверку, не выходя из IDE и не переключаясь между отчетами, трекерами и отдельными security-инструментами.

Заключение

Мы не рассматриваем LLM как замену классическому SAST. Более практичная модель - объединить сильные стороны обоих подходов. Статический анализ дает воспроизводимый security-сигнал и точный data-flow контекст, а AI-агент помогает разработчику быстрее интерпретировать этот сигнал и превратить его в безопасное изменение кода. Именно поэтому Veai SAST встроен прямо в IDE. Безопасность должна обеспечиваться не только на этапе финального аудита или CI/CD-гейта, а в момент, когда разработчик пишет и изменяет код. Чем раньше уязвимость становится понятной и исправимой, тем дешевле она обходится команде и тем меньше шансов, что она попадет в production.

Устанавливайте Veai 5.11 бесплатно в JetBrains IDE. А если в работе вам не хватает каких-то возможностей или сценариев, пишите нам в чат или на support@veai.ru. Такие сообщения напрямую влияют на план следующих обновлений.

Для всех, кому интересно следить за продуктом, новостями из мира AI и техниками использования AI в разработке, оставляем ссылку на наш телеграм-канал.