У классических инструментов статического анализа безопасности (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 в разработке, оставляем ссылку на наш телеграм-канал.
