
Изолированная лаборатория регрессионного тестирования и контроля безопасности для CVE-2026-59891 в @sigstore/oci
# CVE-2026-59891 Regression & Control Lab
Изолированная лаборатория и аудит в режиме «только чтение» для сравнения реально уязвимой и исправленной версий `@sigstore/oci` с одинаковыми входными данными, а также для проверки условий эксплуатации в различных окружениях.
> CVE Reporter этого проекта —
> [gyubin02](https://github.com/gyubin02).
> Краткое описание для найма и собеседований:
> [Пример автоматизации управления уязвимостью CVE-2026-59891](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/docs/portfolio-case-study.ko.md)
## Что проверяется
Суть CVE-2026-59891 — путаница с hostname при выборе учётных данных Registry.
- `0.7.0`: из-за того, что `cr.io` содержится в строке `ghcr.io`, могут быть выбраны учётные данные `ghcr.io`.
- `0.7.1`: после нормализации hostname выбираются только точно совпадающие учётные данные.
Этот репозиторий сначала автоматически проверяет два сценария выбора учётных данных.
1. **collision**: есть только учётные данные `ghcr.io`, а целевой хост — `cr.io`
2. **exact**: есть учётные данные `cr.io`, точно совпадающие с целевым хостом
Уязвимая версия должна ошибочно выбрать учётные данные в первом сценарии, а исправленная — отклонить их. Обе версии должны успешно пройти второй, штатный сценарий.
Дополнительно добавлено динамическое сравнение с использованием одноразового mock Registry на `127.0.0.1` вместо реального внешнего Registry. Уязвимая версия 0.7.0 выполняет 2 локальных запроса, и во втором запросе после authentication challenge наблюдается синтезированный заголовок Authorization. Исправленная версия 0.7.1 отклоняет запрос на этапе выбора учётных данных, поэтому количество запросов равно 0. В результатах остаются только факт наблюдения и количество запросов, а не значения заголовков.
## CLI проверки экспозиции проекта
`cve-2026-59891-audit` читает **только metadata** файла `package-lock.json` целевого проекта и явно указанного Docker config и различает следующее:
- расположение и версию затронутого `@sigstore/oci`
- наличие уязвимой версии и фактическое выполнение условий экспозиции
- конфликт подстрок Registry и фактический объект выбора с учётом порядка ключей JSON
- приоритеты для конкретного окружения, меры по устранению и процедуру повторного тестирования
- JSON- и Markdown-артефакты с удалёнными hostname Registry и абсолютными путями
Запуск на синтетическом фикстуре:
```bash
npm run audit:demo
```
Проверка другого Node.js-проекта:
```bash
npm run audit:project -- \
--project ../target-project \
--docker-config ../review-copy/config.json \
--image cr.io/example/demo \
--destination-trust unknown \
--format markdown \
--output reports/cve-2026-59891.md
```
Для `--destination-trust` нужно обязательно указать одно из следующих значений:
- `trusted`: подтверждено, что destination ограничен кодом и allowlist
- `untrusted`: внешние входные данные или входные данные workflow влияют на destination
- `unknown`: пока не подтверждено
Вердикт делится на `exposure_conditions_met`, `potential_exposure`, `affected_component_only`, `not_detected` и `indeterminate`. `not_detected` — это результат только для зафиксированного диапазона входных данных и не означает «безопасно» или «соответствие требованиям».
## Меры безопасности
- Регрессионная лаборатория не читает и не изменяет реальный `~/.docker/config.json`.
- Используются только изолированный выделенный `HOME` и ничего не значащие `lab-user` / `LAB_ONLY_FAKE_TOKEN`.
- Пакет вызывается только при точном совпадении `HOME`, лабораторной метки и фейкового значения.
- Существующие регрессионные probe выбора перекрывают все HTTP(S), TCP, TLS, DNS и вызовы `fetch` как tripwire.
- На сетевой поверхности Node, используемой динамическими probe, разрешены только HTTP-запросы к точному порту `127.0.0.1`, назначенному ОС. Внешние HTTP и HTTP на других портах, DNS resolver, TLS, UDP, глобальные `fetch` и WebSocket блокируются.
- Дочерним процессам не наследуются токены текущей сессии и переменные окружения Docker.
- Тестируется, что в stdout/stderr и в результатах аудита не сохраняется даже фейковый заголовок — только boolean и количество запросов.
- CLI проверки не может опустить `--docker-config` и не выводит файлы из `HOME` или `DOCKER_CONFIG`.
- CLI не декодирует, не хэширует и не выводит значения учётных данных, не запускает пакеты целевого проекта, Docker или credential helper и не подключается к сети.
- При обнаружении symlink, дублирующихся ключей JSON, неподдерживаемого lockfile или имеющего более высокий приоритет `npm-shrinkwrap.json` работа завершается с ошибкой, а не объявляет результат безопасным.
Поскольку уязвимый пакет устанавливается намеренно, публикация npm-пакета запрещена через `"private": true`. Его нельзя использовать как зависимость продуктового кода.
По этой причине сообщение `npm audit` о версии 0.7.0 — ожидаемый результат; границы и причины исключения задокументированы в [`SECURITY.md`](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/SECURITY.md).
## Запуск
Требования: Node.js 22.22.2+, 24.15.0+ или 26 и выше. Рекомендуемая версия зафиксирована в `.nvmrc`.
```bash
npm ci --ignore-scripts
npm test
npm run demo
npm run dynamic:demo
npm run audit:fixture
npm run audit:demo
npm run evidence
```
Ключевые ожидаемые результаты `npm run demo`:
```text
0.7.0 collision credential-selected
0.7.1 collision credential-rejected
0.7.0 exact credential-selected
0.7.1 exact credential-selected
Regression result: PASS
```
Ключевые ожидаемые результаты `npm run dynamic:demo`:
```json
{
"vulnerable": {
"packageVersion": "0.7.0",
"credentialSelection": "credential-selected",
"requestCount": 2,
"authorizationObserved": true,
"digestVerified": true,
"networkPolicy": "exact-loopback-only"
},
"fixed": {
"packageVersion": "0.7.1",
"credentialSelection": "credential-rejected",
"requestCount": 0,
"authorizationObserved": false,
"digestVerified": false,
"networkPolicy": "exact-loopback-only"
},
"regressionResult": "PASS"
}
```
Всего автоматизированных тестов — 34. `npm run evidence` создаёт в сумме 7 файлов: 6 проверяемых артефактов и `SHA256SUMS`, подтверждающий их целостность.
## Какие компетенции демонстрирует этот проект
- Объяснение условий возникновения реальной CVE на уровне кода и безопасное воспроизведение
- Проектирование регрессионных тестов, сравнивающих уязвимую и исправленную версии в одинаковых условиях
- Разработка loopback-only теста, динамически сравнивающего даже локальную передачу синтетических учётных данных
- Оценка реальных предпосылок атаки в окружении отдельно от публичного CVSS
- Статическая идентификация package-lock v1·v2·v3, alias и вложенных зависимостей
- Автоматическая генерация JSON- и Markdown-артефактов аудита без секретов
- Управление уязвимостью по полному циклу: обнаружение → анализ влияния → проверка исправления → контроль
- Fail-safe тестирование безопасности, не затрагивающее реальные учётные данные
## Источники и область применения
- [GitHub Security Advisory GHSA-pf56-329r-95rw](https://github.com/sigstore/sigstore-js/security/advisories/GHSA-pf56-329r-95rw)
- [NVD CVE-2026-59891](https://nvd.nist.gov/vuln/detail/CVE-2026-59891)
Этот проект — ограниченная контролируемая оценка в учебных целях. Результаты loopback демонстрируют только разницу в поведении в синтетическом окружении и не подтверждают экспозицию учётных данных, компрометацию или эффективность контроля в реальной корпоративной среде. Они не означают сертификацию CISA, ISMS-P или соответствие требованиям какой-либо конкретной организации. Node API guard — это защитный слой для наблюдаемых путей выполнения заблокированных зависимостей, а не network namespace или межсетевой экран на уровне ОС.
Подробные документы об области применения, тестах, рисках и контролях находятся в [`docs/`](https://github.com/gyubin02/cve-2026-59891-control-lab/blob/main/docs), а `npm run evidence` создаёт воспроизводимые артефакты выполнения и SHA-256 manifest.