Описание того что у нас стоит для защиты от supply chain атак (структурный контекст — supply-chain-security) на стороне разработчика. Это слой защиты на dev-машине — отдельный от per-repo .npmrc и от org-level guards у DevOps. Страница описывает текущее состояние и зачем; готовые copy-paste конфиги и пошаговые setup-скрипты — в security-templates репозитории (TBD создать).
Зачем глобально, а не только в репо
Любой pnpm install / pip install потенциально запускает malicious-код на машине разработчика — даже если это pet-project вне работы. Краденые credentials с pet-проекта (~/.config/gcloud, ~/.ssh, GitHub OAuth, npm publish-токен) дают атакующему доступ к рабочей инфре с тем же успехом, как если бы заразился рабочий монорепо. Это значит pre-install gate должен стоять глобально на dev-машине, а не только в .npmrc рабочих репозиториев.
Альтернатива — force через корпоративный proxy (GAR virtual repos). Закрывает то же самое server-side, но с trade-off’ами по нескольким осям: privacy (компания видит весь npm-трафик разработчика, включая нерабочий), VPN-зависимость (без gcloud-auth установка не работает — проблема для удалённых сценариев или новой машины без сетапа), single point of failure (если GAR падает — никто не может ставить пакеты), плюс per-developer setup (gcloud auth + helper-плагин для refresh 60-минутного npm-токена). Стоимость денежная тривиальна ($3-6/мес для нашего объёма) — основные минусы операционные, не финансовые. По сумме trade-off’ов глобально не форсим — оставляем настройку на каждом разработчике и проверяем через self-audit.
npm / pnpm
Глобальный ~/.npmrc содержит ignore-scripts=true, minimum-release-age=2880 (или 1440), audit=true, frozen-lockfile=true.
ignore-scripts блокирует preinstall/postinstall lifecycle — главный канал заражения для Shai-Hulud-класса атак (в ноябре 2025 малварь пришла именно через preinstall @postman/tunnel-agent). minimum-release-age отказывается устанавливать версии моложе 2 дней — большинство malicious-версий yank’аются из registry за часы. audit=true запускает npm audit на каждый install. frozen-lockfile=true гарантирует lock-file integrity.
Глобальный ignore-scripts=true иногда ломает пакеты с native build (sharp, esbuild) — это не повод его выключать; проблемные пакеты добавляются в onlyBuiltDependencies allowlist в package.json конкретного репо.
В монорепо .npmrc каждого репозитория дублирует глобальные настройки — защищает случай когда новый разработчик с дефолтным сетапом или CI runner делает install. Включено в bloodgpt-frontend (PR #276) и bloodgpt-for-business (PR #25); остальные репозитории — audit pending (это open вопрос в supply-chain-security).
Python (pip / uv)
У pip и uv механика отличается от pnpm. Pnpm-овский minimum-release-age — это подвижное окно «не старее N минут от сегодня». В pip эквивалент — флаг --uploaded-prior-to <дата> (с pip 26.0, январь 2026), в uv — exclude-newer в ~/.config/uv/uv.toml. Это фиксированный момент, не плавающее окно — нужно подкручивать вручную по мере того как safe-известная точка двигается вперёд. Обычно держим её на 2-3 дня позади текущего дня, обновляем раз в неделю. Семантика та же: установка отказывается видеть свежие версии, опубликованные после указанного момента.
Формат exclude-newer — RFC3339 timestamp с time-компонентом, не просто дата. Голая дата валит uv ошибкой failed to find time component. Правильно:
# ~/.config/uv/uv.toml
exclude-newer = "2026-05-13T00:00:00Z"Hash-pinning для Python — pip-compile --generate-hashes генерирует requirements.txt с блоками --hash=sha256:..., и pip install -r валится если хэш не сошёлся. Это сильнее чем release-age: атакующий не может подменить версию пакета на лету. Делается per-project в venv, не глобально. Глобальный ~/.config/pip/pip.conf с require-hashes = true практически неприменим — ломает любой ad-hoc pip install foo без hashes (требует hashes явно для каждой установки). Date-pin в pip.conf положить тоже нельзя — --uploaded-prior-to это CLI-флаг с pip 26.0, не settings-option. То есть глобальный pip defense на dev-машине почти отсутствует; основной защитный слой Python — ~/.config/uv/uv.toml с exclude-newer (для кода на uv) плюс per-project hash-pinning в проектных requirements.txt.
Покрывает Python-стек на dev-машине — normalization_pipeline, loinc_harmonization_service, плюс любые ad-hoc Python-скрипты. Per-project hardening самих этих репо (uv.toml exclude-newer / requirements.txt с hashes) пока не сделано — open вопрос ниже.
Сканеры
В работе. Состав сканеров (OSV-Scanner / GuardDog / pip-audit), распределение «локально vs в CI» и shell-alias-обёртки ещё не зафиксированы — описание ниже draft, не следовать как руководству.
Сканер пакетов — это утилита которая проходит по package.json / pnpm-lock.yaml / requirements.txt и проверяет каждый пакет на известные проблемы. На локальной машине запускается вручную или через shell-alias перед install. У нас три параллельных сканера, каждый ловит свой класс проблем — назначение каждого описано в supply-chain-defense-stack:
OSV-Scanner от Google — мульти-эко (npm/PyPI/Go/Maven/…), ищет known-CVE в OSV.dev базе. Используется через shell-alias pnpm-safe или pip-safe — запускает scan перед install. Низкий FP за счёт ecosystem-specific matching.
GuardDog от Datadog — статический анализ кода пакета (Semgrep+Yara rules), ищет malicious-behavior паттерны (install-scripts, exec-base64, обфускация). Ловит свежую малварь до того как она попадёт в advisory. Предпочтительно держать в CI, а не запускать локально на untrusted package output — есть CVE-2026-44972 (terminal escape injection из malicious package content), CI это buffer от прямого терминала разработчика.
pip-audit от PyPA — official Python-tool, defensive depth для Python-стека поверх OSV.
Когда подходит Docker-изоляция
Рекомендация для одного класса ситуаций: знакомство с новой библиотекой или одноразовый скрипт вне рабочего репо. Все эти случаи объединяет то что пакет малознакомый, проверять через сканеры заранее не охота, а ставить на хост машины — потенциально fatal. Решение: запускать в Docker:
docker run --rm -it -v $PWD:/app -w /app node:24 bash
Малварь не дотянется до ~/.ssh, ~/.config/gcloud, токенов хоста. Тяжелее (нужно сначала запустить контейнер), но единственный надёжный isolation без полного proxy. Для рабочих репозиториев в монорепо не нужно — там .npmrc уже даёт основную защиту.
Audit состояния машины
Скрипт audit-local.sh (TBD в templates repo) проверяет наличие настроек в ~/.npmrc / pip.conf / uv.toml, установлены ли сканеры, и сканирует на IoC Shai-Hulud (см. security-incident-response). Запускается раз в квартал или после смены машины.
Plaintext npm publish-токены
Класс gotcha обнаруженный в audit-проходе 2026-05-13. В ~/.npmrc у некоторых разработчиков лежит publish-token к npmjs.com в plain text (строка вида //registry.npmjs.org/:_authToken=npm_xxx). Малварь после заражения первым делом грабит credentials из home-директории — этот токен идёт в их добычу. Атакующий с украденным publish-token публикует malicious версии пакетов от имени maintainer-а — это ровно тот вектор которым началась цепочка Shai-Hulud (зараженный пакет от своего maintainer-а после компрометации его токена).
Что делать. Если автоматизация на машине публикует npm-пакеты — заменить широкий long-lived token на granular token с TTL ≤90 дней через npmjs.com → Settings → Access Tokens → Generate New Token (Granular). Если ручная публикация — удалить строку из ~/.npmrc, при следующем npm publish ввести 2FA одноразово. Если публикация не делается вообще — удалить строку.
Открытые вопросы
- Когда появится
security-templatesrepo — добавить ссылки наsetup-dev-machine.sh(one-shot install) иaudit-local.sh(self-check) - Раскатать
.npmrcс release-age + ignore-scripts на остальные репо монорепо (текущий статус — есть вbloodgpt-frontendиbloodgpt-for-business, в остальных audit pending) - Plaintext publish-токены в
~/.npmrc— это policy-вопрос (проверка через self-audit достаточна) или нужно поднимать GAR proxy чтобы npm-credentials держал не разработчик, а gcloud helper - Per-project Python hardening в
loinc_harmonization_service(естьpyproject.toml— добавить[tool.uv] exclude-newer) иnormalization_pipeline(наrequirements.txtбез hashes — мигрировать наpip-compile --generate-hashesили на uv)
Связано
- supply-chain-security описывает где запускается малварь и где ставится защита целиком — эта страница про слой dev-машины
- supply-chain-defense-stack описывает почему именно эти инструменты выбраны
- security-incident-response описывает что делать если что-то проникло