惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
V
Visual Studio Blog
博客园_首页
Last Week in AI
Last Week in AI
Apple Machine Learning Research
Apple Machine Learning Research
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
SegmentFault 最新的问题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Martin Fowler
Martin Fowler
Recent Announcements
Recent Announcements
J
Java Code Geeks
月光博客
月光博客
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Fortinet All Blogs
P
Privacy & Cybersecurity Law Blog
C
CERT Recently Published Vulnerability Notes
C
CXSECURITY Database RSS Feed - CXSecurity.com
B
Blog RSS Feed
S
Schneier on Security
酷 壳 – CoolShell
酷 壳 – CoolShell
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
W
WeLiveSecurity
A
Arctic Wolf
U
Unit 42
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
有赞技术团队
有赞技术团队
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
Microsoft Security Blog
Microsoft Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
量子位
B
Blog
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
博客园 - 三生石上(FineUI控件)
H
Help Net Security
博客园 - 叶小钗
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
Hugging Face - Blog
Hugging Face - Blog
Google DeepMind News
Google DeepMind News
小众软件
小众软件
雷峰网
雷峰网
TaoSecurity Blog
TaoSecurity Blog
Schneier on Security
Schneier on Security

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Хватит копировать security YAML: AppSec-слой для Java-проектов через Gradle convention plugin
NicholasKuzy · 2026-05-07 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение9 мин

Охват и читатели630

Кейс

Практический разбор того, как я вынес security-проверки Java-проектов из разрозненных CI/CD-скриптов в переиспользуемый Gradle convention plugin.

Вступление

Самая сложная часть Java AppSec обычно не в том, чтобы найти еще один сканер.

Сканеры у команд и так часто есть.

Есть SonarQube для анализа кода. Есть OWASP Dependency-Check для проверки зависимостей. Есть CycloneDX для SBOM. Есть JaCoCo или Kover для покрытия. Есть GitLab CI, Jenkins, TeamCity, GitHub Actions или какой-нибудь внутренний CI, который все это запускает.

И все равно процесс начинает разъезжаться.

Один сервис кладет Dependency-Check-отчеты в одну директорию. Второй генерирует только HTML. Третий правильно передает merge request metadata в SonarQube. Четвертый гоняет все как обычную branch analysis. Один проект делает SBOM только по runtime-зависимостям. Другой включает test-зависимости и получает шумный отчет. В multi-module проекте появляется исключение, кто-то копирует кусок YAML из соседнего репозитория, чуть правит, и так оно живет дальше.

В первый день это не выглядит проблемой.

Через несколько месяцев это уже нормальный такой дрейф security build-процесса.

И вот эту проблему я хотел закрыть через secure-build-gradle-plugin.

Не новым сканером.

А слоем build tooling, который стандартизирует, как существующие AppSec-инструменты подключаются к Gradle-проекту.

Проект: Secure Build Gradle Plugin

Проблема, которую я хотел убрать

Обычно DevSecOps начинается с CI/CD YAML.

Для одного Java-сервиса это нормальный старт:

script:
  - ./gradlew test
  - ./gradlew dependencyCheckAnalyze
  - ./gradlew cyclonedxBom
  - ./gradlew sonar

Все понятно. Команды видны. Pipeline запускается. Отчеты появляются.

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

Каждый сервис постепенно становится ответственным за свою security-интеграцию. Команды копируют старые куски pipeline. Версии плагинов и сканеров расходятся. Пути отчетов расходятся. Где-то добавили SARIF, где-то забыли. Где-то SonarQube получает coverage XML, где-то нет. Где-то branch analysis, где-то pull request analysis. Где-то локально можно повторить проверку, а где-то только пушить и ждать pipeline.

Снаружи это выглядит как автоматизация.

На практике это превращается в набор чуть разных ручных интеграций.

И самое неприятное: команды начинают спорить не про риск, а про проводку инструментов.

Почему отчет не там?

Почему SonarQube не видит coverage?

Почему в одном сервисе Dependency-Check падает, а в другом только пишет отчет?

Почему multi-module проект опять особенный?

В этот момент проблема уже не в том, что “нам нужны еще tools”.

Проблема в том, что нет одного места для conventions.

Почему держать всю security-логику только в CI/CD неудобно

CI/CD хорош как общая среда выполнения.

Он дает чистый runner, логи, artifacts, gates, approvals, общий audit trail. Это все нужно.

Но CI/CD не очень хорош как единственное место, где живет вся логика security-проверок.

Если вся логика находится только в .gitlab-ci.yml или Jenkinsfile, локальная разработка становится вторичной. Разработчик пушит код не только чтобы открыть merge request, но и чтобы понять, что вообще думает security pipeline.

Это плохой feedback loop.

В идеале разработчик должен иметь возможность запустить ту же базовую проверку до merge request:

./gradlew clean securityAnalyze --no-daemon

А CI/CD должен запускать тот же workflow и собирать предсказуемые артефакты.

То есть CI/CD должен исполнять security workflow, а не каждый раз заново описывать его в каждом репозитории.

Это важное отличие.

Идея решения

Принцип был простой:

security behavior должен жить ближе к коду,
а CI/CD должен запускать тот же behavior без повторной реализации

Для Gradle это естественно ложится в convention plugin.

Gradle уже является местом, где проект описывает, как он собирается, тестируется, публикуется и какие tasks доступны. Значит, туда же можно вынести повторяемые AppSec conventions.

Не политику компании.

Не финальное risk acceptance.

Не vulnerability management.

А именно build-time слой:

  • как запускать Dependency-Check;

  • куда складывать отчеты;

  • какие форматы генерировать;

  • как делать SBOM;

  • как передавать coverage в SonarQube;

  • как различать branch и merge request analysis;

  • как жить с multi-module проектами;

  • как дать разработчику одну понятную команду.

Как подключается Gradle plugin

Проект применяет plugin один раз:

plugins {
  id "java"
  id "io.github.niki1337.securebuild.gradle-java" version "0.1.0"
}

Дальше в build-файле остаются только значения конкретного сервиса:

securityConventions {
  serviceName = "payment-api"
  sonarProjectKey = "payment-api"
  allowLocalSonar = false
}

Для multi-module проекта plugin обычно подключается на root-уровне:

plugins {
  id "io.github.niki1337.securebuild.gradle-java" version "0.1.0"
}

securityConventions {
  serviceName = "payments-platform"
  sonarProjectKey = "payments-platform"
  includedModules = ["api", "service"]
  excludedModules = ["test-fixtures"]
}

После этого меняется модель владения.

CI/CD все еще запускает проверки. Security все еще отвечает за требования и triage. Разработчики все еще чинят findings.

Но повторяемая scanner-проводка живет в build system, а не копируется из репозитория в репозиторий как устное предание.

Что происходит под капотом

Plugin не заменяет существующие инструменты.

Он подключает и стандартизирует:

  • SonarQube analysis;

  • OWASP Dependency-Check;

  • CycloneDX SBOM;

  • JaCoCo или Kover coverage;

  • Gradle single-module и multi-module behavior;

  • Git branch metadata;

  • GitLab merge request metadata.

Главная команда для разработчика:

./gradlew clean securityAnalyze --no-daemon

securityAnalyze становится нормальной точкой входа для локальной AppSec-проверки. Она может запускать тесты, coverage, SBOM generation и dependency analysis.

Если нужно разобрать отдельные части, underlying tasks никуда не прячутся:

./gradlew cyclonedxDirectBom --no-daemon
./gradlew dependencyCheckAnalyze --no-daemon
./gradlew sonarHelp --no-daemon

Для multi-module:

./gradlew dependencyCheckAggregate --no-daemon

Смысл не в том, чтобы спрятать инструменты.

Смысл в том, чтобы нормальный путь был очевидным.

Почему это лучше голого CI/CD

Можно сказать: “А зачем plugin? Можно же просто написать нормальный .gitlab-ci.yml”.

Можно.

Для одного репозитория это часто нормально.

Но на масштабе начинаются обычные проблемы:

  • пути отчетов расходятся;

  • scanner settings расходятся;

  • SonarQube metadata забывается;

  • локальный запуск отличается от pipeline;

  • multi-module проекты требуют отдельных костылей;

  • новые сервисы копируют старый boilerplate;

  • security-команда получает разные форматы артефактов;

  • разработчики не понимают, какую команду запускать до MR.

Gradle convention plugin дает одно версионируемое место для этой логики.

CI/CD остается важным, но становится execution layer:

GitLab CI / Jenkins
  запускает ./gradlew securityAnalyze
  собирает artifacts
  применяет gates

А build behavior находится ближе к проекту:

Gradle plugin
  знает tasks
  знает report paths
  знает multi-module structure
  знает SonarQube metadata conventions

В закрытых контурах это особенно полезно.

Во многих СНГ-компаниях нельзя просто взять SaaS и красиво подключить все по документации. Есть внутренний GitLab, свой Nexus, Harbor, self-hosted SonarQube, прокси, внутренние CA, ограничения на интернет, отдельные runners. В такой среде предсказуемость важнее красивой картинки в документации.

SonarQube: проблема почти правильной настройки

SonarQube легко настроить “почти правильно”.

Это как раз опасная зона.

Pipeline зеленый. Analysis ушел. В UI что-то появилось.

Но Java binaries не передались. Coverage XML не передался. Merge request metadata не передалась. В итоге результат вроде есть, но он слабее, чем должен быть.

Plugin решает эту рутину централизованно.

Он читает настройки из environment variables, Gradle properties или securityConventions:

export SONAR_HOST_URL="https://sonarqube.example.com"
export SONAR_PROJECT_KEY="payment-api"
export SONAR_TOKEN="token-value"

И готовит типовые Java SonarQube properties:

sonar.sources
sonar.tests
sonar.java.binaries
sonar.java.test.binaries
sonar.java.libraries
sonar.java.test.libraries
sonar.coverage.jacoco.xmlReportPaths
sonar.exclusions
sonar.test.exclusions
sonar.cpd.exclusions
sonar.coverage.exclusions

Для GitLab merge request pipeline он может маппить CI variables:

CI_MERGE_REQUEST_IID                  -> sonar.pullrequest.key
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME   -> sonar.pullrequest.branch
CI_MERGE_REQUEST_TARGET_BRANCH_NAME   -> sonar.pullrequest.base

Для branch pipeline задается branch analysis metadata.

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

Dependency-Check: отчеты должны быть скучными

Dependency-Check полезен тогда, когда его отчеты предсказуемы.

Я не хочу, чтобы один сервис генерировал JSON, второй только HTML, третий SARIF в непонятной директории, а четвертый вообще все складывал в кастомный путь.

Plugin стандартизирует форматы:

HTML
JSON
SARIF
XML

И пишет их в:

build/reports/dependency-check

По умолчанию отключаются сетевые анализаторы, которые могут делать pipeline нестабильным в закрытой инфраструктуре:

  • OSS Index;

  • RetireJS;

  • Node audit;

  • Node package analyzer;

  • hosted suppressions;

  • CISA KEV analyzer.

В идеальном мире у всех быстрый интернет и доступ к нужным внешним источникам.

В реальном enterprise-мире часто есть прокси, firewall, mirror, внутренние registry и запрет на прямой outbound. Поэтому security build не должен случайно становиться медленным или нестабильным из-за внешнего endpoint.

Если есть внутренний mirror, можно использовать его:

DT_API_URL=https://dependency-track.example.com \
./gradlew dependencyCheckAnalyze --no-daemon

По умолчанию build не падает по CVSS:

failBuildOnCVSS = 11

Так как максимальный CVSS равен 10, это означает: отчет генерируем, но build не валим только по score.

Это осознанный выбор.

На первом этапе команде часто нужна видимость и triage. Если сразу включить жесткие gates на шумных данных, разработчики быстро начнут воспринимать security как блокер без смысла. Сначала данные, валидация и false-positive reduction. Потом уже blocking policy.

SBOM без лишнего шума

SBOM полезен только тогда, когда он описывает полезный artifact.

Если один проект включает test dependencies, а другой нет, сравнивать результаты сложно. Если root multi-module проекта является только aggregator, root-level SBOM может плохо описывать реальное приложение.

Plugin фокусируется на runtime dependencies и уменьшает лишний шум:

  • runtime dependencies включаются;

  • test dependencies не добавляются;

  • license text не встраивается;

  • serial number можно отключить;

  • лишняя metadata уменьшается.

Типовые команды:

./gradlew cyclonedxDirectBom --no-daemon
./gradlew cyclonedxBom --no-daemon

Отчеты:

build/reports/cyclonedx
build/reports/cyclonedx-direct

Для multi-module Spring Boot-проектов plugin старается найти deployable module, например модуль с bootJar, и сделать SBOM ближе к реальному приложению, а не к пустому root aggregator.

Это кажется мелочью, пока не начинаешь отправлять SBOM в Dependency-Track и разбирать, почему половина findings относится к тому, что не попадает в runtime.

Coverage wiring

Coverage нужен для нормального SonarQube analysis, но paths часто расходятся.

Plugin поддерживает:

  • JaCoCo;

  • Kover.

В режиме auto он использует Kover, если Kover уже есть, иначе подключает JaCoCo:

securityConventions {
  coverageProvider = "auto"
}

Для JaCoCo plugin:

  • использует JaCoCo 0.8.13;

  • делает jacocoTestReport зависимым от tests;

  • включает XML output;

  • передает XML path в SonarQube.

Типовой путь:

build/reports/jacoco/test/jacocoTestReport.xml

Опять же, это не rocket science. Это просто та самая повторяемая настройка, которую не хочется чинить в каждом сервисе отдельно.

Multi-module Gradle проекты

Multi-module проекты быстро показывают, насколько интеграция зрелая.

Для demo single-module проекта почти любой scanner пример выглядит красиво.

А потом приходит реальный репозиторий:

root
  api
  service
  domain
  client
  test-fixtures

И начинается: где source? где tests? где binaries? какой модуль deployable? какие модули включать в analysis? что делать с root?

Plugin определяет Java subprojects с:

java
java-library

И поддерживает фильтры:

securityConventions {
  includedModules = ["api", "service"]
  excludedModules = ["test-fixtures"]
}

В multi-module режиме он:

  • настраивает coverage по Java-модулям;

  • собирает coverage XML paths;

  • конфигурирует root-level SonarQube analysis;

  • задает module-level source/test/binary/library paths;

  • запускает aggregate Dependency-Check;

  • старается не генерировать шумный root-level SBOM;

  • использует deployable module для SBOM, когда это возможно;

  • оставляет единый root-level securityAnalyze.

Это и есть отличие между “мы прикрутили сканер” и “у нас есть build convention для реальных Java repositories”.

Как выглядит CI/CD после этого

CI/CD становится проще.

Пример GitLab CI:

security:gradle:
  image: eclipse-temurin:17
  stage: test
  variables:
    GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"
  script:
    - ./gradlew clean securityAnalyze --no-daemon
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - build/reports/dependency-check/
      - build/reports/cyclonedx/
      - build/reports/cyclonedx-direct/
      - "**/build/reports/jacoco/"

SonarQube можно вынести отдельно:

sonarqube:gradle:
  image: eclipse-temurin:17
  stage: test
  script:
    - ./gradlew sonar --no-daemon
  rules:
    - if: '$SONAR_TOKEN'

Важная мысль:

CI/CD вызывает build tasks,
а не заново реализует security conventions

Pipeline становится читаемее. Артефакты становятся предсказуемее. Локальный запуск становится ближе к CI.

Что этот plugin не пытается решить

Plugin специально ограничен.

Он не заменяет:

  • SonarQube;

  • OWASP Dependency-Check;

  • CycloneDX;

  • DefectDojo или Dependency-Track;

  • ручной vulnerability triage;

  • risk acceptance;

  • secret scanning;

  • DAST;

  • container scanning;

  • IaC scanning;

  • release approval process.

Это не “весь DevSecOps в одной зависимости”.

Это build-time слой для Java AppSec:

SCA
SBOM
coverage
SonarQube metadata
local and CI/CD behavior
report conventions

Secret scanning я сознательно отношу к более раннему слою: до commit и push. Про это лучше писать отдельно, потому что там другая логика и другой feedback loop.

Итог

secure-build-gradle-plugin решает не проблему “нет сканеров”.

Он решает проблему “сканеры подключены везде по-разному”.

Вместо того чтобы каждый сервис заново изобретал SonarQube, Dependency-Check, CycloneDX и coverage wiring, проект получает один Gradle-слой:

./gradlew securityAnalyze

Разработчики получают более раннюю обратную связь.

CI/CD получает предсказуемые artifacts.

Security-команда получает более стабильные форматы отчетов.

Multi-module проекты получают поведение, которое понимает структуру репозитория.

И главное: security tooling становится частью нормального Java engineering workflow, а не набором скриптов вокруг него.

Ссылки

Черновые хабы для Habr

Java, Gradle, DevOps, Информационная безопасность, CI/CD