Пиков / КАПО
Как читать авторский курс и нормативные материалы

Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.

П11. ScanCode для лицензий

Зачем эта практика Метаданные пакета говорят «лицензия проекта MIT», а внутри дерева может лежать файл под GPL — посборочный детектор находит лицензию по тексту в файлах, а не по заявлению в package.json. ScanCode даёт два разных слоя: detected_license_expression (что распознал движок) и место, где это найдено. Задача практики — отделить detected (машинная находка с уровнем уверенности) от concluded (вердикт, который выносит человек), и научиться читать spurious-совпадения.

Цель

Запустить ScanCode по согласованному дереву исходников и получить машинный JSON, в котором по каждому файлу видны: распознанное SPDX-выражение, уровень уверенности (score), координаты совпадения (start_line/end_line) и фрагмент текста-улики. Проверяемый результат — выгрузка, где найдено минимум одно GPL/LGPL-совпадение внутри проекта, заявленного как permissive, либо обоснованный вывод, что таких нет.

Команда или действие

# Версия движка фиксируется в отчёте (нужно для воспроизводимости).
# Практика рассчитана на ScanCode 32.x: с этой версии поле называется
# detected_license_expression. На 31.x JSON-ключи другие (licenses[]),
# и jq-срез ниже придётся править.
scancode --version

# Полное сканирование лицензий с текстом-уликой и классификацией файлов
scancode \
  --license \
  --license-text \
  --classify \
  --info \
  --processes 4 \
  --json-pp scancode-licenses.json \
  ./vendor

# Быстрый срез: какие выражения и с какой уверенностью нашлись
jq -r '.files[]
  | select(.detected_license_expression != null)
  | "\(.detected_license_expression_spdx)\t\(.path)"' \
  scancode-licenses.json | sort | uniq -c | sort -rn

Низкий score — не мусор, а очередь на ручной просмотр Совпадения с score ниже ~80 часто это упоминание лицензии в README или changelog, а не сам лицензионный текст. Не отбрасывайте их автоматически: именно там прячется «в одном файле GPL-хедер» при общем MIT. Фильтруйте через --license-score 80 только когда сознательно сокращаете шум для отчёта.

Что вы должны увидеть

Структура --json-pp (сокращённый скелет одного файла):

{
  "headers": [{ "tool_name": "scancode-toolkit", "tool_version": "32.x" }],
  "files": [
    {
      "path": "vendor/somelib/parser.c",
      "type": "file",
      "detected_license_expression": "gpl-2.0",
      "detected_license_expression_spdx": "GPL-2.0-only",
      "license_detections": [
        {
          "license_expression_spdx": "GPL-2.0-only",
          "matches": [
            {
              "score": 99.0,
              "start_line": 3,
              "end_line": 18,
              "matched_text": "This program is free software; you can redistribute..."
            }
          ]
        }
      ]
    }
  ]
}

Ориентир по объёму: на среднем vendor-дереве из 30–50 пакетов это сотни файлов, из которых лицензионные совпадения дают десятки уникальных выражений. Срез через jq должен показать таблицу вида:

  142 MIT   vendor/...
   17 Apache-2.0    vendor/...
    3 GPL-2.0-only  vendor/somelib/...
    1 LGPL-3.0-or-later vendor/otherlib/...

Ключевой момент: строки GPL-2.0-only / LGPL-3.0-or-later в проекте, который в package.json объявлен как MIT, — это и есть та самая находка «лицензия файла ≠ лицензия проекта». ScanCode выдал detected; перевод в concluded (блокер / условно / ложное срабатывание) делается уже руками и не живёт в этом JSON.

Если не получилось

Типичные ошибки - scancode: command not found — Toolkit не в PATH. Ставится через pip install scancode-toolkit в venv (Python 3.9+); проверьте which scancode. Не путать с лёгким scancode-toolkit-mini без полного индекса лицензий. - JSON пустой / "files": [] — указали несуществующий путь или каталог без прав чтения. ScanCode не падает, а молча сканирует пустоту; сверьте путь в конце команды и наличие файлов. - Сканирование висит/упало по памяти — натравили на node_modules целиком или на бинарные артефакты. Сканируйте согласованный фрагмент (./vendor, конкретный пакет), исключайте мусор через --ignore "*.min.js" --ignore "node_modules/.cache/*". - Везде score около 100, но выражения подозрительно однообразны — сканировали уже собранный дистрибутив с вырезанными хедерами вместо исходников. Лицензионные улики живут в исходном дереве, не в минифицированном бандле.

Критерий зачёта

Показать преподавателю: (1) scancode-licenses.json с зафиксированной версией движка в headers; (2) jq-срез с таблицей выражений; (3) минимум один разобранный кейс «detected ≠ concluded» — файл с copyleft-находкой внутри permissive-проекта, с вашим устным выводом, блокер это, ложное срабатывание или требует заключения. Голое число findings без интерпретации не засчитывается.

Нормативная связка