Мы выпустили очередной релиз NEOMSA ESB. Фокус этого цикла — состав поставки: мы прошли по всем контурам сборки, сформировали SBOM, устранили уязвимости уровня Critical и High и зафиксировали версии так, чтобы они не «уехали» при следующей сборке.

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

Коротко о NEOMSA ESB

ESB, или сервисная шина предприятия, это класс систем, через которые организация связывает свои приложения между собой. NEOMSA ESB — отечественная On‑Premise интеграционная платформа на базе Apache Camel, наш форк проекта Camel Karavan. Идея платформы в том, чтобы дать инженеру визуальный проектировщик интеграционных маршрутов, не отбирая при этом сам Camel: то, что собрано в конструкторе, остаётся обычным Camel‑маршрутом в YAML — его можно править руками, хранить в Git и ревьюить как код.

В состав входят веб‑приложение проектировщика, серверная часть на Quarkus, расширение для VS Code, а также каталог компонентов и Kamelet‑шаблонов. Этого набора достаточно для типовых задач интеграции корпоративных информационных систем: учётных, отчётных, CRM, ERP и отраслевых АБС/MES‑контуров.

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

В качестве ориентиров для сравнения — IBM App Connect, Oracle Service Bus, TIBCO BusinessWorks, MuleSoft, WSO2 Enterprise Integrator. Это один класс систем: классическая интеграционная шина (ESB), а в гибридном или облачном исполнении — iPaaS.

Почему для шины состав зависимостей — отдельный контур риска

Для прикладного сервиса уязвимость в библиотеке — это риск одного сервиса. С интеграционной шиной всё иначе, и на это стоит смотреть отдельно.

  • Шина — точка концентрации. Через неё идут потоки между всеми системами: процессинг и АБС, ERP и MES, CRM, антифрод, отчётность регулятору. Так работает любая ESB‑шина: вместо десятков связей «точка‑точка» между системами используется единый узел, который принимает сообщение, преобразует его и доставляет адресату. Компрометация шины даёт не доступ к одной системе, а влияние на весь интеграционный контур сразу.

  • Через неё проходят самые чувствительные данные. В маршрутах живут реквизиты платежей, номера счетов, персональные данные. Многие из них в шине не хранятся — но проходят через её память.

  • Долгий срок жизни. Интеграционный слой внедряют на годы. Библиотеки стареют быстрее, чем выходят релизы шины: то, что при внедрении было свежим, через пару лет набирает десяток CVE.

  • Широкая поверхность. Apache Camel — это сотни компонентов и коннекторов. Каждый из них представляет собой готовый адаптер к конкретному протоколу или сервису, на базе которых строится интеграция корпоративных систем. Даже если заказчик использует пятнадцать компонентов, в итоговую сборку попадает существенно больше.

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

Как устроена проверка релизной сборки

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

  • Формирование SBOM. Спецификация состава в формате CycloneDX собирается на этапе сборки и сохраняется как артефакт конвейера.

  • Анализ состава (SCA). Grype проверяет SBOM. OWASP Dependency‑Check идёт по дереву репозитория и захватывает то, чего нет в Maven‑графе, — в первую очередь lock‑файлы фронтенда.

  • Статический анализ (SAST). SonarQube и Semgrep с офлайн‑набором правил.

  • Поиск секретов. Gitleaks по репозиторию и истории коммитов.

  • Сведение результатов. Все отчёты выгружаются в DefectDojo: дедупликация находок, автоматическое закрытие устранённых, контроль сроков устранения.

Образ собирается через Kaniko, без Docker‑демона и привилегированного режима, и тегируется коротким хешем коммита. Это даёт однозначную трассируемость «образ → исходный код» — на приёмке у службы безопасности вопрос снимается сразу.

Состав зафиксирован: для Java — координаты Maven, для фронтенда — lock‑файлы с точными версиями и контрольными суммами каждого пакета. Повторный прогон на том же коммите даёт тот же состав.

Как мы устраняли найденные уязвимости

Базовый принцип: поднимаем компонент до минимальной версии, в которой уязвимость исправлена по advisory, а не до последней доступной.

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

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

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

Исправления нет в текущей ветке

postcss 7.x уязвим, и патча для 7.x не будет — ветка закрыта сопровождающими. Чинить пришлось не версию, а потребителя: resolve‑url‑loader 4 работает только с postcss 7, а версия 5 — уже с postcss 8. Обновили потребителя, и ветка postcss 7 ушла из дерева целиком.

Минимальной исправленной версии не существует

По advisory для lodash уязвимы все версии до 4.17.23 включительно — то есть исправление должно быть в 4.17.24. Такой версии в реестре нет: линия ушла сразу на 4.18.0. Мы поставили 4.18.0 и при установке увидели пометку сопровождающих: «Bad release. Please use lodash@4.17.21 instead». Проверили всю ветку — 4.18.1 такой пометки не имеет, на нём и остановились.

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

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

Один из компонентов сборочного контура — библиотека, которая сама по себе ничего не выполняет: она лишь сообщает сборщику, под какие браузеры готовить итоговый код. Казалось бы, обновление без последствий. После него сборка одного из контуров упала с синтаксической ошибкой, причём внутри стороннего пакета, которого мы вообще не трогали.

Причина оказалась непрямой. Вместе с библиотекой обновляется справочник поддержки браузеров, а вслед за ним меняется и набор целей, под которые сборщик подгоняет код. Часть современных конструкций языка при новом наборе поддерживается уже везде и переписывать их не требуется, часть — всё ещё требует адаптации. Ровно на этой границе сборщик и споткнулся: в стороннем пакете встретилась конструкция, которую он привёл к форме, недопустимой по правилам языка. Ошибка возникла не в нашем коде и не в самой обновлённой библиотеке, а на стыке трёх независимых компонентов.

Починили точечно — уточнили набор целевых браузеров так, чтобы спорная конструкция не переписывалась вовсе. Вывод для нас оказался скорее процессным, чем техническим: «безобидных» обновлений в сборочном контуре не бывает. Именно поэтому каждое изменение состава проверяется полной пересборкой всех контуров, а не только повторным аудитом. Аудит в этой ситуации показал бы зелёный статус — а собранного артефакта на выходе не было бы.

Lock‑файл, который врал

Отдельная история. В одном из контуров версия уязвимого пакета в lock‑файле была поднята вручную, но поля с адресом загрузки и контрольной суммой остались от старой версии.

Формально файл сообщал, что стоит исправленная версия. Фактически менеджер пакетов скачал бы прежний, уязвимый архив: он идёт по адресу и сверяет контрольную сумму, а не читает строку с номером версии. Сканер при этом смотрит именно на номер и рапортует, что всё хорошо. Уязвимость остаётся в поставке, а отчётность показывает, что её нет.

С тех пор в проверку добавлен контроль соответствия версии и адреса загрузки по всем записям lock‑файлов. На текущей сборке это 5 866 записей, расхождений нет.

И одна находка не из зависимостей

Semgrep указал на передачу данных в открытом виде (CWE-319): во вспомогательном скрипте был захардкожен адрес внешнего сервиса по http. Адрес вынесен в переменную окружения и удалён из репозитория.

Методика учёта уязвимости

Считаем по фактически установленному составу, а не по объявленным в манифестах диапазонам. Запись вида «^1.2.3» ничего не говорит о том, что реально окажется в сборке, — смотреть нужно на зафиксированный состав.

Срез охватывает пять контуров сборки и 5 872 пакета.

Уровень

На старте работ

В текущей сборке

Critical

0

0

High

187

0

Medium

408

59

Low

82

53

И здесь стоит сказать то, о чём обычно умалчивают. Ноль в графе High — это не свойство продукта, а состояние на дату среза.

Пока мы готовили этот материал, вышли два новых advisory — по js‑yaml и svgo. Одиннадцать пакетов в трёх контурах снова стали красными. Мы подняли их до исправленных версий, пересобрали все контуры и перепроверили; цифры в таблице приведены уже после этого.

Это и есть главное практическое наблюдение. Ноль уязвимостей — это не достижение, которое можно предъявить один раз и забыть. Это режим: он либо поддерживается регулярно, либо через месяц перестаёт что‑либо значить. Поэтому проверка состава привязана у нас к конвейеру, а не к дате релиза.

Что происходит с оставшимися Medium‑уязвимостями

Оставшиеся уязвимости — 59 уровня Medium и 53 уровня Low. Все они разобраны, по каждой есть решение, и они ведутся в DefectDojo со своими сроками устранения.

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

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

Предел точечных исправлений

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

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

Что дал переход на новую версию

Мы перевели форк на версию 4.18. Сборочный контур интерфейса в ней построен на другом инструменте — Vite.

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

Показатель

Предыдущая версия

Версия 4.18

Контуров сборки

5

3

Пакетов в составе, всего

5 872

1 683

Пакетов в основном контуре интерфейса

1 641

598

Состав сократился в три с половиной раза, а основной контур интерфейса — почти втрое. Для эксплуатации это очень даже весомо. Каждый пакет в поставке — это потенциальный будущий advisory, который придётся разобрать, обосновать и закрыть в срок. Втрое меньше пакетов — это втрое меньше такой в будущем и заметно меньше мест, где уязвимость вообще может появиться.

Вместе с сокращением ушёл и сам источник большинства прежних находок — тот самый хвост устаревших сборочных зависимостей, который нельзя было закрыть переопределениями. Класс проблем устранён не обходным путём, а в корне.

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

Что это даёт заказчикам

  • Состав поставки известен и подтверждён. SBOM формируется на каждой сборке и предоставляется по запросу. Вопрос «из чего это собрано» закрывается документом, а не перепиской.

  • На момент поставки нет Critical и High. Нулевой уровень критических рисков. На момент релизного среза в поставке нет уязвимостей категорий Critical и High, а статус по Medium и Low прозрачно зафиксирован с методикой подсчёта.

  • Приёмка у службы безопасности идёт быстрее. Отчёты собраны в одном формате, находки не дублируются, устранённые закрываются автоматически. Меньше итераций между интегратором и ИБ заказчика.

  • Воспроизводимость. Один и тот же коммит даёт один и тот же состав. Это важно и при расследовании инцидентов, и при повторной проверке.

  • Периметр закрыт. Платформа работает on‑premise, данные не уходят наружу, анализ выполняется офлайн‑инструментами.

  • Меньше поверхности — меньше работы впереди. Сокращение состава в три с половиной раза снижает не только текущий объём находок, но и нагрузку на службу безопасности заказчика в последующие годы эксплуатации.

Мы продолжаем вести состав платформы в том же режиме: каждая сборка — это SBOM, полный набор проверок и актуальная картина в DefectDojo. Архитектура интеграционной шины, её функции и поддерживаемые сценарии интеграции подробно разобраны на странице продукта. Если вам нужны детали процесса проверки или SBOM по конкретной версии — напишите на почту neomsa@neoflex.ru или оставьте вопрос в форме на сайте NEOMSA, обсудим.

Комментарии (0)