Проверка лицензий npm‑зависимостей: практический разбор license‑checker

Коротко: чтобы держать открытые лицензии под контролем, достаточно поставить прозрачную политику и подключить проверку зависимостей к сборке; удобную отправную точку даёт Как проверить лицензии всех npm-зависимостей: обзор инструмента license-checker, откуда понятен и порядок действий, и типичные ловушки, которых лучше избежать.

В экосистеме JavaScript зависимость растёт как лоза: тянется от знакомых пакетов к десяткам незаметных, пока не оплетёт весь проект. Каждый новый листок несёт свою лицензию, а значит — и юридические обязанности. Разобраться, кто что требует, — задача не только юристов. Это ремесло инженеров сборки, девопсов, техлидов, которые принимают решения под давлением дедлайна и репутационных рисков.

Когда счёт идёт на сотни пакетов, ручной разбор похож на попытку перечесть звёзды. Нужен инструмент, который прочтёт package.json, заглянет в node_modules и вынесет ясный вердикт: какие лицензии в игре, совместим ли выбранный стек с политикой компании, где белые пятна и как их закрыть без истерики и накладных расходов. Таким «фонарём» на практике становится license-checker — не единственный, но удивительно честный и послушный в нужных руках.

Зачем контролировать лицензии в JavaScript‑проектах

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

Открытые лицензии устроены как дорожные знаки: одни разрешают ехать быстро (MIT, ISC), другие обязывают включить фары и предъявить права (Apache‑2.0 с NOTICE), третьи требуют перевезти часть груза в общий доступ (GPL‑семейство, AGPL). В браузерной и серверной части JavaScript различия особенно чувствительны: фронтенд складывается в бандлы, которые уходят на сторону пользователя, бэкенд остаётся на серверах компании, но может подпадать под сетевые оговорки. Оттенки важны: динамическая подгрузка модулей через import() не освобождает от условий copyleft, а статическая линковка в бандлере может трактоваться как создание производного произведения. Игнорировать эту геометрию опасно: крупные релизы умеют тормозить не баги, а неувязанные в последний момент уведомления о сторонних компонентах. Когда контур прозрачный, команда спокойно фокусируется на ценности продукта, а не на пожаротушении вокруг лицензий.

Что умеет license‑checker и когда он уместен

License‑checker сканирует дерево зависимостей Node.js, извлекает данные о лицензиях из package.json и файлов в пакете и выдаёт отчёт в JSON, CSV или человекочитаемом виде. Его сила — в скорости, предсказуемости и простых правилах допуска.

Инструмент работает поверх реального содержимого node_modules, опирается на lock‑файлы постольку-поскольку и не пытается «угадывать» будущее. Он читает поле license, ищет LICENSE, NOTICE, COPYING, поддерживает SPDX‑выражения, умеет проваливаться в каждую транзитивную зависимость и присваивает статусы «неизвестно», если встречает нестандартные формулировки. На практике этого достаточно, чтобы за минуты составить карту рисков перед релизом, прервать пайплайн при нарушении политики или сгенерировать Third‑Party Notices для поставки. У license‑checker есть границы: Yarn PnP без node_modules ему чужд, тонкие юридические интерпретации он не делает, а качество отчёта прямо связано с аккуратностью экосистемы пакетов. Именно поэтому инструмент хорошо работает как первый слой обороны — быстрый скрининг, который вовремя сигнализирует и даёт фактуру для диалога с юристами.

Быстрый старт: как получить сводку по зависимостям

Чтобы собрать отчёт, достаточно запустить проверку из корня проекта и сфокусироваться на production‑зависимостях, а результат сохранить в файл или отдать в CI. Дальше — настройки под политику и инфраструктуру.

Практика показывает: полезно начать с малого — получить список лицензий и понять, где «дыры». Затем постепенно затягивать гайки: добавить фильтр только разрешённых лицензий, исключить dev‑пакеты, ввести падение сборки при нарушениях. Вот минимальный маршрут, который отрабатывает почти всегда:

  • Запустить license‑checker через npx, чтобы не тащить его в зависимости проекта.
  • Собрать JSON‑отчёт по production‑зависимостям и посмотреть долю «неизвестно».
  • Добавить список разрешённых лицензий и оценить, где возможны быстрые замены.
  • Сохранить отчёт артефактом сборки — пригодится для аудита и повторяемости.
npx license-checker --production --json > licenses.json
npx license-checker --production --summary
npx license-checker --production --onlyAllow="MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0"

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

Флаг Что делает Когда применять
—production Исключает devDependencies Релизы, серверная поставка, минимизация шума
—json / —csv Вывод в JSON или CSV Интеграция с пайплайнами и BI‑табло
—summary Агрегирует по типам лицензий Быстрая оценка профиля рисков
—onlyAllow=»A;B» Разрешает только указанные лицензии Жёсткая политика допуска в CI
—failOn «GPL;AGPL» Проваливает сборку при найденных типах Ранний стоп нежелательных пакетов
—start path Сканирует поддерево проекта Монорепозитории, workspaces
—relativeLicensePath Показывает путь к файлам лицензий Сбор Third‑Party Notices
—excludePackages «pkg@ver» Исключает конкретный пакет Временные обходы, форки
—unknown Показывает только неизвестные лицензии Приоритезация ручной проверки

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

Политики допуска: как запретить нежелательные лицензии

Политика строится на трёх корзинах: разрешено, допустимо с условиями, запрещено. License‑checker поможет автоматизировать первую и третью, а вторую — подсветит для ручной обработки.

Рабочая политика не пытается объять все лицензии мира; она описывает правила, которые реально применимы к продукту и модели поставки. Часто за основу берут «зелёный список» — MIT, BSD‑семейство, Apache‑2.0 — и «красную зону» — GPL‑3.0, AGPL‑3.0 для проприетарной поставки. Между ними остаётся «жёлтая полоса»: MPL‑2.0, LGPL‑2.1/3.0, а также нестандартные лицензии отдельных авторов. Эти случаи требуют внимания: иногда достаточно добавить NOTICE и ссылку на исходники изменённых файлов, иногда — отказаться от конкретной версии пакета. License‑checker позволяет выразить базовые правила прямо в команде или конфиге CI: сигнализировать при «красных» лицензиях и пропускать «зелёные». Для «жёлтых» удобно собирать отчёты и обсуждать их в процессе технического ревью.

# Жёсткое правило допуска
npx license-checker --production --onlyAllow="MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0"

# Мягкое правило: валим пайплайн при GPL/AGPL, остальное — в отчёт
npx license-checker --production --failOn "GPL;AGPL" --json > licenses.json

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

Лицензия Тип Ключевые обязательства Комментарий к применимости
MIT, ISC Пермиссивные Сохранить уведомление об авторстве Безопасная база для проприетарных продуктов
BSD‑2/3‑Clause Пермиссивные Уведомление, без одобрения автора в рекламе Широко совместимы, минимум трений
Apache‑2.0 Пермиссивная NOTICE, патентная оговорка Нужен сбор NOTICE, особенно в фронтенде
MPL‑2.0 Слабый copyleft Раскрыть изменения в затронутых файлах Условия локальны на файл, часто приемлемо
LGPL‑2.1/3.0 Слабый copyleft Возможность замены библиотеки Трактовки в JS зависят от способа включения
GPL‑3.0 Сильный copyleft Раскрытие производного произведения Обычно несовместимо с закрытой поставкой
AGPL‑3.0 Сетевой copyleft Раскрытие кода при предоставлении сервиса Риск для SaaS, требует замены

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

Тонкости сканирования: монорепозитории, lock‑файлы и PnP

Чем сложнее структура проекта, тем важнее корректно выбрать точку старта и среду, в которой license‑checker видит реальное дерево. Ошибки обычно лечатся правилом «сканировать то, что деплоится».

В монорепозиториях с workspaces картина расползается, если запускать проверку из корня. Вернее запускать из папки приложения, которое реально попадает на прод, — через флаг —start. С lock‑файлами всё проще: license‑checker читает не их, а node_modules; поэтому перед сканированием важно синхронизировать зависимости (npm ci, pnpm install —frozen-lockfile). Yarn PnP ломает предпосылку: без папки node_modules инструменту не к чему привязаться; выход — временно материализовать зависимости (yarn unplug) или использовать альтернативы, дружелюбные к PnP. С браузерными бандлами стоит помнить, что devDependencies не всегда безобидны: часть утилит встраивает рантайм‑код в сборку. В таких случаях полезно дополнительно проверить итоговый бандл и его sourcemap на предмет вшитых лицензий и текстов NOTICE.

Симптомы, причины и быстрые исправления удобно свести в таблицу — она экономит время во время расследований.

Симптом Вероятная причина Решение
Много «UNKNOWN» в отчёте Кастомные лицензии, старые пакеты Включить —relativeLicensePath, вручную проверить LICENSE, рассмотреть замену пакета
Ноль зависимостей в выводе Запуск не из той папки, пустой node_modules Сделать npm ci / pnpm install, задать —start до нужного приложения
Yarn PnP: инструмент не видит пакеты Нет физического node_modules Временно отключить PnP для шага сканирования или использовать альтернативный сканер
Dev‑пакет протащил код в прод Плагины бандлера, рантайм генераторов Проверить бандл и sourcemap, расширить сканирование без —production
Разные отчёты локально и в CI Неповторяемая установка пакетов Использовать npm ci / pnpm install —frozen-lockfile, зафиксировать Node и менеджер пакетов

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

Интеграция в CI/CD: как сделать проверку непрерывной

Встраивание license‑checker в пайплайн сводится к трем шагам: детерминированная установка, генерация отчёта и автоматическое решение по политике. Остальное — удобства и прозрачность.

Хорошо настроенный этап лицензий не грузит разработчиков, а ловит проблемы на уровне pull request. Полезно завести «светофор»: зелёный — всё чисто, жёлтый — замечания, красный — блокировка. Такой подход позволяет выдержать ритм релизов, не превращая комплаенс в бюрократию. Ключ к успеху — сделать результаты видимыми: прикладывать артефакты, выводить сводку в комментарии к PR, держать матрицу политик в репозитории рядом с кодом.

  • Фиксировать версии Node и менеджера пакетов с помощью .nvmrc/Volta и lock‑файла.
  • Устанавливать зависимости командами npm ci / pnpm install —frozen-lockfile.
  • Запускать license‑checker с —json и правилами допусков.
  • Падать при нарушении политики; артефактами сохранять полный отчёт и NOTICE.
  • Публиковать краткую сводку как комментарий к pull request.
# Пример шага в GitHub Actions
- name: Install
  run: npm ci
- name: Licenses
  run: |
    npx license-checker --production --json --relativeLicensePath > licenses.json
    npx license-checker --production --onlyAllow="MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0"
- name: Upload report
  uses: actions/upload-artifact@v4
  with:
    name: licenses
    path: licenses.json

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

Альтернативы и дополнения к license‑checker

Ни один инструмент не закрывает всё. License‑checker хорош для локальной экосистемы Node.js и простого CI. Для многоязычных монореп и глубокого юридического анализа лучше подключать специализированные SCA‑решения и сборку SBOM.

Выбор обычно идёт по трём осям: глубина анализа, покрытие экосистем и стоимость владения. Пара лёгких утилит закрывает 80% задач без абонентской платы, но за оставшиеся 20% иногда приходится платить — временем или подпиской. Сравнение ниже помогает увидеть, где сильные и слабые стороны разных подходов.

Инструмент Класс Сильные стороны Ограничения
license‑checker CLI для Node.js Простота, скорость, гибкие правила допуска Нет поддержки Yarn PnP, только экосистема JS
pnpm licenses list Встроенная команда Знает структуру pnpm, быстрый вывод Меньше форматов вывода, экосистема pnpm
Snyk License SaaS SCA Политики, базы уязвимостей, интеграции Платная модель, данные вне периметра
OSS Review Toolkit (ORT) Фреймворк OSS‑комплаенса Глубина анализа, SBOM, многоязычность Сложность внедрения и поддержки
FOSSA, Black Duck, Mend Корпоративные SCA Политики, отчётность, аудит в масштабе Стоимость и операционные накладные
CycloneDX, SPDX SBOM Форматы перечня компонентов Стандартизованный экспорт, трассируемость Нужны генераторы и процессы потребления

Хорошая практика — сочетать инструменты. License‑checker отвечает за быстрый контроль на уровне проекта, а SBOM и SCA‑платформа — за обзор по всей организации, историю и отчётность для аудиторов.

Практический кейс: путь от хаоса к прозрачности

У проекта несколько фронтендов, общий дизайн‑кит и Node.js‑сервис, всё живёт в монорепозитории. Релиз упёрся в тревожный список: в отчёте юристов — AGPL‑пакет в бандле и пара «неизвестных» библиотек.

Картину восстановили за день. Сначала включили детерминированную установку зависимостей, вывели license‑checker на каждый PR с —production и —json, добавили —start для приложений, которые реально деплоятся. Сводка показала: AGPL тянул экспериментальный плагин для генератора документации, который каким‑то образом прописался в цепочке сборки фронтенда. Заменили на MIT‑аналог, а плагин отправили назад в dev‑ветку. «Неизвестные» оказались форками внутренних пакетов без лицензии в package.json: дописали SPDX‑идентификаторы и добавили LICENSE в репозитории. Параллельно собрали NOTICE‑файл из путей, которые license‑checker отдал через —relativeLicensePath, и зафиксировали процесс в CI. На выходе — чистый отчёт, артефакты для аудита, понятный How To для новых компонент. Без надрыва и с прибавкой уверенности перед следующим релизом.

FAQ

Как понять, достаточно ли для проекта одного license‑checker?

Если стек ограничивается JavaScript/TypeScript, инфраструктура проста, а отчёт нужен для инженерного контроля — хватает. Когда появляются несколько языков, жёсткие требования к отчётности и история по всем поставкам, лучше добавить SBOM и SCA‑платформу.

На практике решение видно по симптомам. Если команда регулярно принимает решения по конкретным пакетам и не спорит о трактовках, CLI‑инструмент даёт нужную скорость. Когда же возникают вопросы к наследованию, патентным оговоркам, требуется централизованная политика для десятков репозиториев и конвейеров — нужен системный слой поверх. Он дороже, но экономит время на спорах и ручном сведении отчётов.

Можно ли полагаться на поле license в package.json без дополнительных проверок?

Нет, только как на ориентир. Корректно сверять поле license с фактическим содержимым LICENSE/NOTICE и учитывать SPDX‑выражения и исключения. License‑checker как раз поднимает пути к файлам и помогает обнаружить расхождения.

В экосистеме встречаются пакеты со старыми формулировками, дуальными лицензиями или собственными текстами. Бывает и обратная ситуация: в package.json стоит MIT, а в репозитории — нестандартная оговорка. Поэтому минимальный due diligence включает просмотр исходных файлов лицензий, а в спорных случаях — диалог с мейнтейнерами или замены.

Как обработать монорепозиторий с несколькими приложениями и общими пакетами?

Сканировать каждое приложение отдельно через —start и фиксировать установки зависимостей на уровне workspaces. Это даст отчёты, привязанные к реальной поставке, а не к абстрактному корню монорепозитория.

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

Что делать с «неизвестными» лицензиями в отчёте?

Сфокусировать их в отдельный список (—unknown), открыть файлы LICENSE/NOTICE и проверить репозиторий пакета. Дальше — три пути: заменить пакет, согласовать исключение с обязательствами или поддержать форк с понятной лицензией.

Иногда помогает поднять версию до свежей мажорной — авторы могли уже перейти на SPDX‑идентификатор. Если пакет критичен и альтернатив нет, документируют условия в политике, добавляют уведомления и фиксируют риски для последующей ревизии.

Как собирать Third‑Party Notices для поставки?

Включить —relativeLicensePath, собрать пути к LICENSE/NOTICE и сгенерировать сводный файл с указанием лицензий и авторов. Для Apache‑2.0 важно не забыть оригинальные NOTICE‑тексты.

Подход работает и для фронтенда, и для бэкенда. Для бандлов удобно автоматизировать сбор: парсить отчет license‑checker, читать файлы по путям, добавлять заголовки пакетов и складывать в артефакт сборки. Это сокращает ручной труд и делает поставки повторяемыми.

Почему результаты локально и в CI могут различаться и как этого избежать?

Из‑за недетерминированной установки зависимостей и разницы окружений. Лекарство — npm ci / pnpm install —frozen-lockfile, фиксация версии Node и менеджера пакетов, артефакты кэша.

Ещё одна причина — разные точки старта. Локально сканируется нужное приложение, а в CI — корень. Правильная стратегия — одинаковые команды и параметры везде, плюс проверка, что в репозитории присутствуют lock‑файлы всех рабочих пространств.

Что делать, если используется Yarn PnP?

License‑checker не поддерживает PnP напрямую. Можно временно материализовать node_modules (unplug), применить альтернативные утилиты или генерацию SBOM, совместимую с PnP, и уже её анализировать.

Решение зависит от требований. Для быстрого скрининга удобнее краткий отход от PnP на время шага проверки. Если политика компании не допускает такого, тогда лучше выбрать инструмент, нативно понимающий PnP, или строить SBOM средствами Yarn и анализировать его сторонним ПО.

Выводы и короткий How To

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

Чтобы ритм не сбивался, полезно закрепить минимальный обряд: детерминированная установка, сканирование того, что реально идёт на прод, и автоматическое решение по политике. Надстройки — SBOM, централизованные SCA — подоспеют, когда масштаб начнёт требовать истории и сквозной отчётности.

How To — действие по делу:

  1. Зафиксировать версии Node и менеджера пакетов, добавить npm ci / pnpm install —frozen-lockfile в CI.
  2. Определить политику: разрешённые, условно допустимые и запрещённые лицензии; сохранить её в репозитории.
  3. Запустить npx license-checker из папки приложения (—start при монорепо) с флагами —production, —json, —relativeLicensePath.
  4. Включить —onlyAllow для зелёного списка и/или —failOn для красного; настроить падение пайплайна при нарушениях.
  5. Сохранить отчёт и сгенерировать NOTICE; добавить краткую сводку в комментарий к pull request.
  6. Раз в месяц пересматривать политику и обновлять зависимости, закрывая «жёлтую зону» заменами.