30 KiB
Процесс 16: Использование инструментов композиционного анализа
Дата публикации: 5 мая
Время на чтение: ~13 мин
ТГ канал: Про_SEC_со
В декабре 2025 года команда React опубликовала разбор критической уязвимости в React Server Components: CVE-2025-55182, она же React2Shell. Сухая строка в advisory звучит почти буднично — unauthenticated remote code execution, CVSS 10.0. Но в жизни это означает другое: если у вас React 19.x с уязвимыми react-server-dom-webpack, react-server-dom-parcel или react-server-dom-turbopack, а приложение использует Server Components или Server Functions, внешний HTTP-запрос может добраться до выполнения кода на сервере. Без логина. Без «пользователь сам открыл файл». Просто потому, что серверный Flight-декодер обрабатывает недоверенные данные.
Патчи вышли в React 19.0.1, 19.1.2 и 19.2.1; GitHub advisory по React отдельно перечисляет затронутые пакеты и описывает проблему как небезопасную десериализацию. Vercel указывала, что затронуты и Next.js-приложения на уязвимых версиях с App Router. И вот тут слово «зависимость» перестаёт быть строкой в package-lock.json. Оно становится серверным входом в вашу DMZ: фреймворк, который вчера был просто частью frontend-стека, внезапно оказывается местом исполнения кода в Node.js-процессе.
Если команда не может за десять минут ответить, где у неё используется react-server-dom-*, какие версии попали в production-артефакты, включены ли Server Components, какие endpoint’ы принимают RSC/Server Actions-запросы и блокирует ли сборка выпуск с CVSS 10.0, проблема уже не в одной библиотеке. Проблема в отсутствии процесса.
ГОСТ Р 56939-2024 называет этот процесс 5.16: «Использование инструментов композиционного анализа». Простыми словами — это инвентаризация и проверка сторонних компонентов: какие библиотеки, модули, контейнерные слои и пакеты попали в ПО, откуда они взяты, какие версии используются, есть ли в них известные уязвимости, и что команда делает, когда такая уязвимость находится.
Суть процесса простыми словами
Композиционный анализ, или SCA (Software Composition Analysis), отвечает на неприятный, но честный вопрос: «Из чего на самом деле собран наш продукт?» Не из красивой архитектурной схемы. Не из списка «основных технологий» в презентации. А из конкретных зависимостей с версиями, источниками и рисками.
В ГОСТе прямо сказано: под композиционным анализом понимается формирование перечня зависимостей ПО, определение особенностей их использования, выявление уязвимостей и/или иных недостатков в зависимостях. То есть задача не только «прогнать сканер», а построить рабочий контур: список зависимостей → проверка на известные уязвимости → анализ применимости → корректирующее действие → доказуемый след в артефактах.
Знакомая картина: сервис написан на 20 тыс. строк собственного кода, но в сборку тянется 700 пакетов транзитивно. Разработчик явно добавил один HTTP-клиент, а вместе с ним приехали парсер, логгер, XML-библиотека, криптографический адаптер и ещё пол-экосистемы. Часть из них никогда не видна в код-ревью, потому что они лежат глубже — в lock-файле, контейнерном образе или build plugin. Процесс 5.16 как раз вытаскивает эту «подземную карту» наружу.
Он связан с архитектурой (5.6), моделью угроз и поверхностью атаки (5.7). ГОСТ не случайно ссылается на артефакты этих процессов: одно дело — уязвимая dev-зависимость, которая не попадает в релиз; другое — библиотека десериализации в публичном API, который принимает данные из интернета. Версия одна, риск разный.
Требование 5.16.2.1: Регламент композиционного анализа
Разработать регламент композиционного анализа.
Регламент — это не папка «для аудитора». Это инструкция для момента, когда в пятницу вечером выходит CVE с оценкой 10.0, а у вас вопрос: «Мы вообще это используем?» Если ответ ищут вручную по чатам и репозиториям, процесс уже трещит.
Артефакт 5.16.3.1: что должно быть в регламенте
ГОСТ требует, чтобы регламент композиционного анализа содержал:
- Обязанности сотрудников и их роли при проведении композиционного анализа
- Правила отслеживания уязвимостей для заимствованных компонентов, участвующих в сборке ПО
- Правила анализа заимствованных компонентов на наличие известных уязвимостей
- Правила принятия компенсирующих и защитных мер против угроз в цепочке поставки сторонних компонентов
- Периодичность проведения композиционного анализа в соответствии с практиками сборки ПО
Пример: карта ролей и правил
Роли:
- Владелец продукта: принимает риск, если обновление нельзя поставить сразу
- Разработчик: обновляет зависимость, фиксирует результат в задаче
- AppSec/ИБ: настраивает правила сканирования, проверяет критичные исключения
- DevOps/Release: встраивает SCA в pipeline и блокирующие quality gates
Правила:
- SCA запускается на merge request и перед релизной сборкой
- Critical/High уязвимости в компонентах поверхности атаки блокируют релиз
- Исключение допускается только с анализом применимости и сроком пересмотра
- Перечень зависимостей обновляется при каждом изменении lock-файлов,
сборочных файлов, базового контейнерного образа или vendor-пакета
Важная деталь: регламент должен описывать не только «кто запускает сканер», но и что делать с результатом. Иначе SCA быстро превращается в шумную ленту: красные находки копятся, команда привыкает их игнорировать, а критическая уязвимость тонет среди старых исключений.
Требование 5.16.2.2: Формировать перечень зависимостей ПО
Формировать перечень зависимостей ПО.
Перечень зависимостей — это инвентарная ведомость продукта. Без него невозможно быстро ответить на вопрос «есть ли у нас уязвимый react-server-dom-webpack?» или «какие сервисы реально используют React Server Components на сервере?». А в реальном инциденте скорость ответа решает многое: пока команда ищет компонент, злоумышленники уже сканируют интернет и пробуют типовые признаки уязвимой конфигурации.
React2Shell хорошо показывает эту боль в современном JavaScript-стеке. На бумаге надо «обновить React». На практике надо понять, какие приложения используют Next.js App Router, какие пакеты react-server-dom-* попали транзитивно, какие сборки уже уехали в production, где RSC endpoint’ы доступны извне, а где риск ниже из-за сетевых ограничений. Если перечень зависимостей живёт только в lock-файлах по репозиториям, дежурный инженер начинает ночную перепись сервисов. Если перечень нормальный, у него уже есть список затронутых артефактов и владельцев.
Артефакт 5.16.3.2: что включать в перечень
ГОСТ задаёт минимум:
- Перечень модулей и компонентов заимствованного ПО с указанием версий
- Источник или поставщик этих модулей и компонентов
На практике полезно добавить больше полей, потому что иначе перечень есть, а работать с ним больно:
component: react-server-dom-webpack
version: 19.2.0
source: npm registry
usage: runtime (используется при выполнении приложения)
introduced_by: web-shop/package-lock.json (добавлен через)
transitive: да (транзитивная зависимость)
artifact: web-shop:2025.12.03
environment: production
attack_surface: да (входит в поверхность атаки)
feature_context: Next.js App Router, включены React Server Components
repository: frontend-platform
last_scanned_at: 2026-05-05T10:30:00Z
Это уже похоже на SBOM (Software Bill of Materials — ведомость состава ПО). ГОСТ не требует именно этого термина, но смысл близкий: у вас должен быть пригодный к проверке список того, что вошло в продукт. Не «где-то в lock-файле», а так, чтобы по нему можно было принять решение.
SBOM обычно показывают как скучную ведомость библиотек. Таблица, версии, лицензии, CVE. Всё правильно, но не цепляет. А теперь представьте другую сцену: в продакшене лежит исполняемый файл, Docker-контейнер и deb-пакет, вокруг них крутится eBPF/Tetragon и фиксирует, что реально запускается на хосте. В CI параллельно формируется CycloneDX SBOM во время сборки, дополняется метаданными, хешами артефактов и уезжает в хранилище. Потом SIEM берёт события с прода, сопоставляет их с SBOM и задаёт тот самый вопрос, который обычно прилетает ночью: «Где у нас на проде уязвимая версия библиотеки?»
Главная сила такой схемы в том, что SBOM перестаёт быть бумажкой для аудита. Он становится рабочей картой местности. CI знает, что мы собрали. Продакшен через телеметрию показывает, что реально исполняется. SIEM связывает одно с другим и помогает SOC не гадать по репозиториям, а искать конкретные сервисы, файлы, контейнеры и пакеты. Появилась новая уязвимость в сторонней зависимости — и вместо ручной переклички «у кого это есть?» команда получает маршрут: какой SBOM затронут, какой артефакт ушёл на прод, где он запущен и кому назначать исправление. Вот здесь SBOM из инвентаря превращается в инструмент охоты и реагирования. Тут уже начинается отдельная история для отдельной статьи.
Требование 5.16.2.3: Контролировать и актуализировать перечень зависимостей
Контролировать и актуализировать перечень зависимостей ПО в соответствии с регламентом композиционного анализа на предмет наличия известных уязвимостей.
Один раз составленный список быстро стареет. Сегодня у компонента нет известных CVE, завтра появляется бюллетень, послезавтра эксплойт попадает в публичный PoC. Поэтому перечень зависимостей надо не просто хранить, а регулярно сверять с базами уязвимостей и изменениями в сборке.
Артефакт 5.16.3.3: результаты контроля актуальности
ГОСТ ожидает, что результаты контроля включают:
- Описание процедуры контроля перечня зависимостей и его актуализации
- Описание инструментов контроля актуальности
- Журналы событий, связанных с контролем актуальности и обновлениями заимствованных компонентов, участвующих в сборке
Пример: когда перечень обновляется
События актуализации:
- Изменился package-lock.json / yarn.lock / poetry.lock / go.sum / pom.xml
- Обновился базовый контейнерный образ
- Добавлен новый SDK, плагин сборки или бинарный vendor-компонент
- Появился новый advisory по компоненту из перечня
- Перед релизной сборкой создаётся финальный snapshot зависимостей
Журнал:
- pipeline_id
- commit_sha
- artifact_version
- scanner_name и scanner_version
- vulnerability_db_timestamp
- количество findings по критичности
- ссылка на задачу исправления или risk acceptance
Тут важно не обмануть себя. Если сканер смотрит только прямые зависимости, но не видит транзитивные, вы получаете красивый отчёт и слепую зону. Если он проверяет исходники, но не проверяет контейнерный образ, базовый слой может принести старый openssl или glibc. Если он не сохраняет версию базы уязвимостей, через месяц сложно доказать, что на момент релиза проверка была актуальной.
Требование 5.16.2.4: Анализ заимствованных компонентов перед сборкой
Осуществлять анализ заимствованных компонентов, составляющих поверхность атаки, на предмет наличия известных уязвимостей при сборке (непосредственно перед сборкой) ПО.
Это контрольная точка перед тем, как артефакт станет релизом. Не «когда-нибудь ночью», не «раз в квартал», а при сборке или прямо перед ней. Особенно для компонентов, которые находятся на поверхности атаки: принимают внешние запросы, парсят пользовательские файлы, работают с сетевыми протоколами, обрабатывают сообщения, XML, JSON, архивы, изображения.
Артефакт 5.16.3.4: результаты анализа компонентов
Результаты должны содержать:
- Сведения о наличии или отсутствии известных уязвимостей в заимствованных компонентах
- Сведения о критичности выявленных уязвимостей
Пример: блокирующая политика в CI/CD
Этап pipeline: sca-release-gate
Область:
- package manager dependencies
- transitive dependencies
- container image layers
- embedded JAR/WAR/native binaries
Блокировка:
- Critical в компоненте поверхности атаки -> build failed
- High с публичным exploit или active exploitation -> build failed
- Medium -> задача в backlog с SLA
- Dev-only dependency -> анализ применимости, не автоматическое исключение
Исключения:
- только с owner, сроком действия, ссылкой на анализ применимости
- пересмотр при обновлении advisory или изменении архитектуры
Такой gate неприятен ровно до первой настоящей CVE. Потом все быстро понимают, зачем он нужен. В момент React2Shell команды с актуальным перечнем зависимостей могли сразу построить список приложений с уязвимыми react-server-dom-* и Server Components. Команды без перечня устраивали ночную перекличку: «У кого Next 15 или 16? App Router включён? А в контейнере какая версия React подтягивается?»
Требование 5.16.2.5: Корректирующие воздействия
Применять корректирующие воздействия по результатам анализа заимствованных компонентов на предмет наличия известных уязвимостей.
Найти уязвимость — половина работы. Вторая половина начинается там, где сканер уже замолчал: обновить, заменить, отключить опасную функцию, поставить компенсирующую меру, обратиться к поставщику, зафиксировать риск, выпустить срочный патч.
Артефакт 5.16.3.5: что фиксировать после исправления
ГОСТ разделяет подход для закрытого и открытого кода.
Для проприетарного ПО:
- Результаты анализа применимости и реализуемости уязвимости
- Результаты обращения к поставщику уязвимых модулей
- Результаты обновления компонентов по мере выхода обновлений от поставщика
Для ПО с открытыми исходными текстами:
- Результаты анализа применимости и реализуемости уязвимости
- Результаты попыток обновления зависимых компонентов
- Если обновиться нельзя — результаты применения собственного механизма исправления
Пример: карточка корректирующего действия
Находка: CVE-2025-55182
Компонент: react-server-dom-webpack
Версия: 19.2.0
Затронутые сервисы: web-shop, partner-portal
Поверхность атаки: да, публичные endpoint'ы Next.js App Router с включёнными RSC
Критичность: критическая
Решение:
- Обновить React / react-server-dom-* до исправленной версии
- Обновить затронутые версии фреймворка, если этого требует поставщик
- Пересобрать и заново развернуть затронутые сервисы
- Повторно запустить SCA и smoke-тесты
- Проверить access logs и телеметрию приложения на подозрительные RSC-запросы
Доказательства:
- ссылка на merge request
- ссылка на отчёт pipeline
- новый snapshot зависимостей
- версия релиза
- задача по инциденту / безопасности
Такой артефакт нужен не для красоты. Через полгода кто-то спросит: «Почему мы приняли это исключение?» или «Какая версия закрыла уязвимость?» И если ответ лежит не в памяти инженера, а в карточке, команда выигрывает время.
Отдельный риск: dependency confusion
Композиционный анализ — это не только CVE. Есть ещё вопрос источника зависимости. В 2021 году исследователь Алекс Бирсан описал dependency confusion: он находил имена внутренних пакетов компаний и публиковал пакеты с такими же именами в публичные репозитории вроде npm, PyPI и RubyGems. В ряде случаев сборочные системы подтягивали публичный пакет вместо внутреннего. В исследовании фигурировали Apple, Microsoft, PayPal, Netflix, Tesla, Uber, Shopify и другие организации; автор отдельно указывал, что работал в рамках разрешённых программ и получил bug bounty.
Почему это важно для 5.16? Потому что перечень зависимостей с полем «источник» — не бюрократия. Если пакет company-internal-auth-client внезапно пришёл из публичного npm, это не просто странность. Это сигнал о возможной атаке на цепочку поставки. Поэтому регламент должен описывать правила доверенных источников: private registry, pinning источников, запрет смешивания public/private без явного scope, проверку namespace и блокировку неожиданных поставщиков.
Как это работает вместе
Процесс 5.16 можно представить как контур из пяти шагов.
- 5.16.2.1 — регламент: кто отвечает за SCA, какие инструменты используются, какие находки блокируют сборку, как принимаются исключения.
- 5.16.2.2 — перечень зависимостей: список заимствованных компонентов, версии, источники, связь с продуктом и сборкой.
- 5.16.2.3 — актуализация: регулярная сверка перечня с базами уязвимостей и изменениями в сборке.
- 5.16.2.4 — анализ перед сборкой: финальный контроль компонентов, особенно тех, что входят в поверхность атаки.
- 5.16.2.5 — корректирующие действия: обновление, замена, патч, компенсирующие меры, обращение к поставщику, документированный риск.
На этапе разработки 5.16 работает не одиночным сканером, а в одной связке с другими процессами группы. Правила кодирования из 5.8 задают, какие заимствованные компоненты допустимы и как их подключать. Экспертиза кода из 5.9 помогает увидеть странные зависимости там, где автоматике не хватает контекста. Статический анализ из 5.10 и динамический анализ из 5.11 проверяют уже не только факт наличия библиотеки, а то, как она ведёт себя в коде и при выполнении. Процесс 5.15 закрывает секреты, которые часто живут рядом с зависимостями в CI, конфигурациях и пакетных менеджерах. А процесс 5.17 смотрит шире — не протащили ли через цепочку поставки вредоносный код или недекларированные возможности.
Архитектура из 5.6 и модель угроз с поверхностью атаки из 5.7: они дают контекст, где компонент реально используется и насколько он опасен. Планирование тоже не фон — без него SCA быстро превращается в «ещё один отчёт в pipeline», на который никто не выделил владельца, SLA и время на обновления.
Если коротко: SCA — это не сканер ради сканера. Это способ не быть заложником чужого кода, который вы уже поставили своим пользователям. Вы можете писать идеальный собственный код, но если в релиз попала известная уязвимая библиотека, атакующему всё равно, кто именно её написал. Он будет искать открытую дверь.
ГОСТ 5.16 как раз требует, чтобы у команды был фонарь, карта и процедура закрытия этой двери.