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

推荐订阅源

C
Cisco Blogs
Cyberwarzone
Cyberwarzone
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Martin Fowler
Martin Fowler
T
Tor Project blog
N
Netflix TechBlog - Medium
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
V
Visual Studio Blog
GbyAI
GbyAI
PCI Perspectives
PCI Perspectives
D
DataBreaches.Net
Jina AI
Jina AI
H
Heimdal Security Blog
云风的 BLOG
云风的 BLOG
P
Privacy International News Feed
A
About on SuperTechFans
J
Java Code Geeks
美团技术团队
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News | PayPal Newsroom
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
博客园 - 司徒正美
C
Check Point Blog
T
Threat Research - Cisco Blogs
Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
C
Cyber Attacks, Cyber Crime and Cyber Security
I
Intezer
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
Apple Machine Learning Research
Apple Machine Learning Research
Hugging Face - Blog
Hugging Face - Blog
The Last Watchdog
The Last Watchdog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
Cisco Talos Blog
Cisco Talos Blog
W
WeLiveSecurity
Hacker News: Ask HN
Hacker News: Ask HN
Recent Commits to openclaw:main
Recent Commits to openclaw:main
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
Docker
博客园 - Franky
Security Archives - TechRepublic
Security Archives - TechRepublic

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет 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 миллионов точек без потерь
Как сделать Maven build security-aware: AppSec-проверки без дрейфа CI/CD
NicholasKuzy · 2026-05-07 · via Все публикации подряд на Хабре

Практический разбор Maven core extension, который встраивает Java security checks в Maven lifecycle, а не заставляет копировать scanner-конфигурацию по pipeline-файлам.

Вступление

Проблема никогда не была в том, что Maven-проекты не умеют запускать security-инструменты.

Умеют.

Можно в pipeline вызвать tests, Dependency-Check, CycloneDX и SonarQube. Можно прописать plugin blocks в pom.xml. Можно скопировать рабочую конфигурацию из одного сервиса в другой и назвать это стандартом.

Какое-то время это даже работает.

Потом начинаются маленькие отличия.

В одном сервисе есть JaCoCo, но XML coverage не передается в SonarQube. В другом Dependency-Check делает только HTML. В multi-module проекте SBOM генерируется от root aggregator, который сам не является runtime-приложением. В третьем pipeline забыли merge request metadata, поэтому SonarQube analysis технически прошел, но практически получился неполным.

Это и есть security build drift.

Выглядит как автоматизация. Работает как несогласованность.

Я сделал secure-maven-extension, чтобы закрыть именно эту проблему для Maven-проектов.

Не заменять сканеры.

А заставить Maven lifecycle нести security workflow.

Проект: Secure Build Maven Extension

Как обычно начинается Maven DevSecOps

Типичный pipeline выглядит примерно так:

script:
  - ./mvnw test
  - ./mvnw org.owasp:dependency-check-maven:check
  - ./mvnw org.cyclonedx:cyclonedx-maven-plugin:makeBom
  - ./mvnw sonar:sonar

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

На масштабе это превращается в maintenance pattern, которым никто до конца не владеет.

Часть настроек живет в CI/CD. Часть в pom.xml. Часть в документации. Часть в переменных окружения, о которых локальный разработчик узнает только после падения pipeline.

Новый сервис каждый раз заново отвечает на одни и те же вопросы:

  • как включить coverage;

  • куда класть Dependency-Check reports;

  • какие форматы нужны security-команде;

  • как передать SonarQube token;

  • как отличить branch analysis от MR analysis;

  • как сделать SBOM для multi-module проекта;

  • как повторить это локально.

И в какой-то момент становится понятно: build сам по себе не security-aware. Pipeline просто вызывает сканеры рядом с build.

Почему обычный подход неудобен

CI/CD должен быть общей средой выполнения.

Он должен запускать чистый build, публиковать artifacts, включать gates, хранить logs и давать auditability.

Но когда CI/CD еще и владеет всей scanner-конфигурацией, каждый репозиторий становится отдельной custom-интеграцией.

Для разработчика это выглядит так:

mvn verify

локально проходит, но pipeline делает что-то другое:

  • другие goals;

  • другие properties;

  • другие report paths;

  • другой набор форматов;

  • другая SonarQube metadata.

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

Эту дыру я и хотел закрыть.

Maven команды должны остаться знакомыми, но lifecycle должен запускать один и тот же AppSec behavior локально и в CI/CD.

Принцип решения

Ключевая идея:

оставить Maven experience нативным,
но внедрить повторяемые security conventions в lifecycle

Разработчику не нужен отдельный security script для каждого сервиса.

CI/CD не должен заново описывать scanner conventions.

Security-команда не должна по каждому репозиторию объяснять, где лежат отчеты и какие properties нужно передать.

Build должен знать скучные детали сам.

Именно поэтому это Maven core extension, а не просто еще одна команда в pipeline.

Почему Maven core extension

Обычный Maven plugin все равно обычно нужно явно конфигурировать в каждом проекте.

Это тоже можно стандартизировать, но copy-paste полностью не исчезает.

Core extension дает более раннюю точку интеграции.

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

.mvn/extensions.xml

Пример:

<extensions>
  <extension>
    <groupId>io.github.niki1337.securebuild</groupId>
    <artifactId>secure-maven-extension</artifactId>
    <version>0.1.0</version>
  </extension>
</extensions>

Внутри extension работает на стадии Maven afterProjectsRead.

Это важный момент.

На этой стадии Maven уже прочитал root pom.xml и module POMs. Уже известны packaging, modules, existing plugins и properties. Но lifecycle еще не стартовал.

То есть extension может посмотреть на проект и внедрить нужные security plugins до фаз initialize, package, verify и sonar:sonar.

Это удобное место для conventions.

Что подключается под капотом

Extension работает с инструментами, которые Java-команды и так знают:

  • jacoco-maven-plugin для coverage;

  • sonar-maven-plugin для SonarQube analysis;

  • dependency-check-maven для dependency risk reports;

  • cyclonedx-maven-plugin для SBOM.

Разработчик продолжает использовать Maven:

mvn package
mvn verify
mvn sonar:sonar

Разница в том, что команды становятся security-aware.

Например:

mvn package

может собрать приложение и сгенерировать CycloneDX SBOM.

mvn verify

может запустить tests, JaCoCo coverage и Dependency-Check.

mvn verify sonar:sonar

может отправить SonarQube analysis с branch/MR metadata, binaries и coverage paths.

И это главное: workflow выглядит как Maven, а не как набор сканеров, приклеенных вокруг Maven.

Конфигурация из разных источников

Реальная инфраструктура редко бывает идеальной.

Локально разработчик может передавать -D.... В CI/CD значения приходят через environment variables. Какие-то стабильные настройки удобно хранить в pom.xml.

Extension поддерживает все эти источники:

  • environment variables;

  • Maven user properties;

  • project properties из pom.xml;

  • system properties.

Пример project defaults:

<properties>
  <secure.serviceName>payment-api</secure.serviceName>
  <sonar.projectKey>payment-api</sonar.projectKey>
  <sonar.projectName>Payment API</sonar.projectName>
</properties>

Пример CI variables:

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

Пример локального override:

mvn verify \
  -Dsecure.serviceName=payment-api \
  -Dsonar.projectKey=payment-api

Смысл не в том, чтобы заставить всех использовать один стиль конфигурации.

Смысл в том, чтобы итоговое поведение было одинаковым.

Coverage без повторяемой проводки

Coverage часто ломает AppSec workflow тихо.

SonarQube может запуститься без coverage. JaCoCo может сгенерировать отчет, но если XML output не включен или path не передан в SonarQube, анализ будет слабее.

Extension inject-ит JaCoCo для Java jar и war проектов, если JaCoCo еще не настроен.

Lifecycle wiring:

initialize -> jacoco:prepare-agent
verify     -> jacoco:report

XML отчет:

target/site/jacoco/jacoco.xml

Этот path автоматически передается в:

sonar.coverage.jacoco.xmlReportPaths

Это не самая эффектная часть проекта.

Но именно такие повторяемые детали и создают drift, когда их копируют руками.

SonarQube: токена мало

Частая ошибка: считать, что SonarQube setup это URL, project key и token.

Для Java-проектов нормальный analysis зависит еще от source paths, test paths, compiled binaries, coverage XML, branch metadata и merge request metadata.

Extension готовит properties:

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

В GitLab merge request pipeline он берет:

CI_PIPELINE_SOURCE=merge_request_event
CI_MERGE_REQUEST_IID
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
CI_MERGE_REQUEST_TARGET_BRANCH_NAME

И маппит в:

sonar.pullrequest.key
sonar.pullrequest.branch
sonar.pullrequest.base

Для branch pipeline задается:

sonar.branch.name

Это ровно тот тип логики, который становится хрупким, если он размазан по десяткам .gitlab-ci.yml.

В core extension это версионируется и переиспользуется.

Dependency-Check в одном формате

OWASP Dependency-Check inject-ится в Maven lifecycle.

Для single-module проекта:

verify -> dependency-check:check

Для multi-module:

verify -> dependency-check:aggregate

Форматы отчетов:

HTML
JSON
SARIF
XML

Путь:

target/reports/dependency-check

По умолчанию отключаются network-dependent analyzers:

  • RetireJS;

  • Node audit;

  • Node package analyzer;

  • OSS Index;

  • hosted suppressions.

В закрытых средах это важно.

Если каждый pipeline зависит от внешнего endpoint, то безопасность внезапно начинает зависеть от доступности интернета, proxy и rate limits. Внутренний mirror решает эту проблему лучше.

Пример:

DT_API_URL=https://dependency-track.example.com mvn verify

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

failBuildOnCVSS = 11

Это не потому что vulnerabilities не важны.

Это потому что первая стадия внедрения часто должна дать видимость и данные. Blocking gates лучше включать после triage и настройки noise reduction.

SBOM должен описывать полезный artifact

SBOM не должен быть просто файлом ради файла.

Он должен описывать то, что реально деплоится.

Для single-module проектов extension запускает:

package -> cyclonedx:makeBom

Отчеты:

target/reports/cyclonedx

Включаются:

compile dependencies
runtime dependencies

Исключаются:

test scope
provided scope
system scope

Для multi-module проектов root часто является только aggregator. Генерировать SBOM только от root бывает бесполезно.

Extension пытается найти Spring Boot module по:

org.springframework.boot:spring-boot-maven-plugin

Если находит, inject-ит CycloneDX туда.

Если нет, fallback на aggregate SBOM на root:

package -> cyclonedx:makeAggregateBom

Это практичнее для реальных Maven-репозиториев, где deployable artifact живет не в root.

Multi-module Maven

Multi-module Maven проекты требуют отдельной логики.

Extension считает build multi-module, когда Maven видит больше одного project и secure.forceSimpleMode не включен.

Java modules:

jar
war

Можно фильтровать:

<properties>
  <secure.includedModules>api,service</secure.includedModules>
  <secure.excludedModules>test-fixtures</secure.excludedModules>
</properties>

В multi-module режиме extension:

  • настраивает SonarQube на root project;

  • inject-ит JaCoCo в Java-модули;

  • добавляет module-level SonarQube paths;

  • inject-ит aggregate Dependency-Check на root;

  • генерирует CycloneDX из Spring Boot module, если возможно;

  • fallback-ится на aggregate SBOM, если deployable module не найден.

Это отличие между “мы вызвали scanner command” и “build понимает структуру Maven-проекта”.

CI/CD становится меньше

После этого pipeline может быть проще.

GitLab CI пример:

security:maven:
  image: eclipse-temurin:17
  stage: test
  script:
    - ./mvnw -B verify
  artifacts:
    when: always
    expire_in: 7 days
    paths:
      - target/reports/dependency-check/
      - target/reports/cyclonedx/
      - "**/target/reports/dependency-check/"
      - "**/target/reports/cyclonedx/"
      - "**/target/site/jacoco/"

SonarQube можно запускать отдельно:

sonarqube:maven:
  image: eclipse-temurin:17
  stage: test
  script:
    - ./mvnw -B verify sonar:sonar
  rules:
    - if: '$SONAR_TOKEN'

Pipeline остается читаемым.

Security wiring живет в Maven extension.

Чем Maven extension отличается от Gradle plugin

Обе идеи решают одну проблему: security build drift.

Но build systems разные.

Gradle task-oriented, поэтому Gradle plugin дает tasks:

securityAnalyze
dependencyCheckAnalyze
dependencyCheckAggregate
cyclonedxDirectBom
sonar
sonarHelp

Maven lifecycle-oriented, поэтому Maven extension inject-ит security tooling в phases:

initialize
package
verify
sonar:sonar

Коротко:

Gradle plugin:
  security checks как Gradle tasks и conventions

Maven extension:
  обычные Maven lifecycle commands становятся security-aware

Реализация разная.

Цель одна: меньше drift, больше repeatability.

Что extension не решает

Это один build-time слой.

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

  • централизованный vulnerability management;

  • ручной triage;

  • DefectDojo или Dependency-Track;

  • secret scanning;

  • DAST;

  • container scanning;

  • IaC scanning;

  • release approval policy.

Он отвечает за Maven Java build-time checks:

coverage
SonarQube metadata
SCA reports
SBOM generation
repeatable lifecycle behavior

Secret scanning, например, лучше ставить раньше: до commit и push. Это другой слой Secure SDLC.

Итог

secure-maven-extension превращает разрозненную scanner-конфигурацию в переиспользуемую Maven lifecycle convention.

Вместо того чтобы каждый проект вручную подключал JaCoCo, SonarQube, Dependency-Check и CycloneDX, extension inject-ит их до старта lifecycle.

Разработчики продолжают использовать обычные команды:

mvn package
mvn verify
mvn sonar:sonar

Но build становится security-aware.

И это главное.

Не сделать еще один scanner.

А сделать существующие AppSec tools проще для одинакового внедрения в Maven-проекты.

Ссылки

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

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

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

0%Да!0

0%Нет(0

Никто еще не голосовал. Воздержавшихся нет.