Claude Media
Claude CodeでOSSライセンスを一覧にしてGPL系の混入を点検する

Claude CodeでOSSライセンスを一覧にしてGPL系の混入を点検する

package-lock.jsonの依存ごとのライセンス表記をSPDX識別子で一覧にし、GPL系の混入をClaude Codeで点検する手順です。ORとANDの読み分けや、法務に回す線引きも載せています。

依存パッケージのライセンス点検は、lockfileから表記を取り出して一覧にし、GPL系が混じる行だけを人が読む流れに落とすと回ります。Claude Codeには一覧の作成と分類の下書きを任せ、最終判断は法務に渡します。この記事では、npmのpackage-lock.jsonを例に、SPDX識別子の読み方、点検スクリプト、Claude Codeへの任せ方、法務に回す線引きを順に書きます。

点検の流れとClaude Codeの受け持ち

Claude Codeはコードベースを読み、ファイルを編集し、コマンドを実行するコーディング支援ツールです。ライセンス点検では、この「読む」と「コマンドを実行する」の部分がそのまま使えます。

手順

点検の4段階

  1. 1

    取り出す

    lockfileから、依存ごとの名前・版・ライセンス表記を機械的に抜き出します。

  2. 2

    揃える

    表記をSPDX識別子と式に寄せ、旧表記や独自表記を洗い出します。

  3. 3

    絞る

    GPL系を含む行と、表記のない行だけを残します。

  4. 4

    渡す

    残った行に組み込み方と所見を添え、法務の確認依頼にします。

前半の2段階は決まった規則で処理できるので、スクリプトに寄せます。Claude Codeに向くのは後半で、残った行の依存経路をたどる、パッケージのREADMEやLICENSEファイルを読む、確認依頼の文面にまとめる、といった作業です。ライセンスが適合するかどうかの判断は、Claude Codeにも担当させません。

SPDX識別子とライセンス式の読み方

ライセンス表記を揃える基準になるのが、SPDX License Listです。ライセンスごとに短い識別子を割り当てた一覧で、各行には正式名のほか、FSFがFree/Libreとしているか、OSIが承認しているかの列が付いています。MITはMIT、Apache License 2.0はApache-2.0、Mozilla Public License 2.0はMPL-2.0です。

GPL系は「only」と「or-later」で別の識別子になる

GPLの識別子は、版と「その版のみ」か「それ以降も可」かで分かれます。

識別子名称
GPL-2.0-only名称GNU General Public License v2.0 only
GPL-3.0-only名称GNU General Public License v3.0 only
GPL-3.0-or-later名称GNU General Public License v3.0 or later
LGPL-3.0-only名称GNU Lesser General Public License v3.0 only
AGPL-3.0-only名称GNU Affero General Public License v3.0 only

SPDXの3.0で、GNU系の識別子は「この版のみ」「それ以降も可」を明示する形に置き換わりました。古いGPL-3.0やGPL-2.0、AGPL-3.0、LGPL-2.1は、3.0で非推奨になった識別子です。lockfileにこうした旧表記が残っていても、点検では「GPL系」として拾います。only系なのかor-later系なのかは、パッケージのLICENSEファイルを読まないと決まりません。

ORは選択、ANDは全部、WITHは例外付き

1つの依存に複数のライセンスが書かれているときは、SPDXのライセンス式として読みます。演算子は大文字で書く決まりで、小文字は演算子として扱われません。

  • MIT OR GPL-3.0-or-later: どちらかを選べる。選択肢を持つ側が使う条件を決める
  • LGPL-2.1-only AND MIT: 両方に同時に従う必要がある
  • GPL-2.0-or-later WITH Bison-exception-2.2: 例外条項付きのGPL
  • CDDL-1.0+: その版か、それ以降の版。+は識別子の直後に空白なしで付ける

点検で見落としやすいのは、GPL系の文字列が含まれるのに実際には選ばなくてよいORの行です。逆に、ANDの片側にGPL系があれば、その行は選択の余地がありません。一覧にするときは、この2つを別の分類にしておくと、読む順番を付けやすくなります。

npmのpackage.jsonとlockfileに入るライセンス表記

npmのpackage.jsonでは、licenseにSPDX識別子を書く運用が案内されています。複数ライセンスはSPDXのライセンス式の文字列で書き、例として(ISC OR GPL-3.0)が挙がっています。SPDXに識別子がない独自ライセンスは、SEE LICENSE IN <filename>という文字列で、同梱のファイルを指す書き方です。過去に使われたlicenses配列やオブジェクト形式は、有効なメタデータではないと説明されています。

つまり、lockfileから取れる表記は次の4種類に分かれます。

  1. 単一の識別子(MIT)
  2. ライセンス式((MIT OR GPL-3.0-or-later))
  3. SEE LICENSE IN ...の独自表記
  4. 表記なし、または旧形式

3と4は機械では判定できないので、必ず人が読む行に回します。

lockfileから一覧を作る

lockfileVersion 3のpackage-lock.jsonは、packagesの各エントリにlicenseを持ちます。npm 10.8.2でminimistとjszipだけを入れた小さなプロジェクトでpackagesを読むと、14パッケージ分の表記が取れました。内訳はMITが11件、ISCが1件、(MIT AND Zlib)が1件、(MIT OR GPL-3.0-or-later)が1件です。GPLの文字列が出てきたのは、jszipの1行だけでした。

次のスクリプトは、このlicenseを集計して、GPL系・旧表記・独自表記・表記なしの行だけを表にします。Node.js 20で動かしています。

// scripts/license-inventory.mjs
import { readFileSync } from "node:fs";
 
const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
const OLD_IDS = new Set(["GPL-2.0", "GPL-3.0", "LGPL-2.1", "LGPL-3.0", "AGPL-3.0"]);
 
function classify(license) {
  if (!license) return "表記なし";
  if (license.startsWith("SEE LICENSE")) return "独自ライセンス";
  const ids = license.match(/[A-Za-z0-9.+-]+/g) ?? [];
  if (ids.some((id) => OLD_IDS.has(id))) return "旧表記のGPL系";
  if (/GPL/.test(license)) return /\sOR\s/.test(license) ? "GPL系を含むOR" : "GPL系";
  return "ok";
}
 
const rows = Object.entries(lock.packages)
  .filter(([path]) => path !== "")
  .map(([path, p]) => ({
    name: path.replace(/^.*node_modules\//, ""),
    version: p.version,
    license: typeof p.license === "string" ? p.license : "",
    dev: p.dev === true,
  }))
  .map((r) => ({ ...r, status: classify(r.license) }));
 
const counts = new Map();
for (const r of rows) counts.set(r.license || "(なし)", (counts.get(r.license || "(なし)") ?? 0) + 1);
for (const [lic, n] of counts) console.log(`${n}\t${lic}`);
 
console.log("\n| パッケージ | 版 | ライセンス | devのみ | 分類 |\n|---|---|---|---|---|");
for (const r of rows.filter((r) => r.status !== "ok")) {
  console.log(`| ${r.name} | ${r.version} | ${r.license || "-"} | ${r.dev ? "はい" : "いいえ"} | ${r.status} |`);
}

実行すると、上の小さなプロジェクトでは次の出力になります。

node scripts/license-inventory.mjs
11	MIT
1	ISC
1	(MIT OR GPL-3.0-or-later)
1	(MIT AND Zlib)
 
| パッケージ | 版 | ライセンス | devのみ | 分類 |
|---|---|---|---|---|
| jszip | 3.10.1 | (MIT OR GPL-3.0-or-later) | いいえ | GPL系を含むOR |

jszipはMIT OR GPL-3.0-or-laterなので、MITを選べる行です。GPL系の文字列が出たからといって、ただちにGPLの義務を負うわけではありません。ただし、どちらを選ぶかを記録に残す必要はあります。この記録が、後で「どの条件で使っているか」を説明する材料になります。

lockfileに頼らず確かめたいときは、license-checker系のツールも使えます。license-checker-rseidelsohnを--summary付きで同じプロジェクトに実行すると、ライセンスごとの件数がMIT: 11、ISC: 2、(MIT OR GPL-3.0-or-later): 1、(MIT AND Zlib): 1の形で出ました。ISCがlockfile側より1件多いのは、このツールがプロジェクト自身を数えに含めているためで、依存の数は一致しています。

Claude Codeに任せる範囲を設定で絞る

点検のためにClaude Codeへ与える権限は、読み取りとスクリプト実行だけで足ります。インストールやネットワーク取得を許す必要はありません。権限ルールは.claude/settings.jsonのpermissionsに書き、評価は拒否、確認、許可の順です。最初に当たった規則で結果が決まります。

{
  "permissions": {
    "allow": [
      "Read(package-lock.json)",
      "Bash(node scripts/license-inventory.mjs)",
      "Bash(npm ls *)"
    ],
    "deny": [
      "Bash(npm install *)",
      "Bash(git push *)"
    ]
  }
}

Bash(npm run build)のように完全一致で書くと、そのコマンドだけが対象になります。*を付けると、後ろに引数が続く形も含められます。拒否ルールは、Claude Codeが普段出す形のコマンドを止めるためのものです。同じプログラムを別の書き方で呼ぶ場合までは防げないので、セキュリティ境界として頼らず、権限の実際の動きは/permissionsの管理で確認しておくと安心です。

CLAUDE.mdに判定の規約を書く

分類の規約はCLAUDE.mdに置いておくと、会話のたびに言い直さずに済みます。例えば次のような断片です。

## ライセンス点検の規約
- 一覧は scripts/license-inventory.mjs の出力を正とし、手で書き換えない
- ライセンス式は SPDX 式として読む。OR は選択可、AND は全部適用
- GPL / LGPL / AGPL を含む行、表記なし、SEE LICENSE IN の行は「要確認」に入れる
- 要確認の行には、依存経路(npm ls <名前>)と、配布物に入るかの所見を添える
- 適合・不適合は判断しない。所見は事実の列挙にとどめ、結論欄は空にする

CLAUDE.mdは、@path/to/importの書き方で別ファイルを取り込めます。社内のライセンス方針書を取り込む手もありますが、取り込んだファイルも起動時にコンテキストへ載るので、長い規程を丸ごと読ませるのは避けたほうが無難です。CLAUDE.mdに規約を書くコツは、pytestの規約をCLAUDE.mdに教える例と同じで、実行コマンドと判定の線引きを具体的に書くことです。

依存経路をたどらせる

GPL系の行が見つかったら、その依存を誰が持ち込んでいるかを調べます。Claude Codeに次のように頼むと、npm lsの出力を読んで経路にまとめてくれます。

scripts/license-inventory.mjs を実行して、GPL系を含む行ごとに
npm ls <パッケージ名> --all で依存経路を調べ、
直接依存か間接依存か、devDependenciesだけか、を表にしてください。
適合かどうかの判断は書かないでください。

表のうち、直接依存か間接依存かの区別と、devのみかどうかは、後の「組み込み方別の確認」で使います。

組み込み方別に確認する

ライセンスの扱いは、依存を成果物にどう組み込むかで変わることがあります。この点検でも、行ごとに組み込み方を添えます。ただし、どの組み込み方でどんな義務が生じるかは、各ライセンスの本文と社内の規程で決まる事項なので、ここでは分類の列だけを持ちます。

組み込み方一覧に添える所見
配布する製品に含める一覧に添える所見配布物に含まれるファイルか(バンドル、同梱、コンテナイメージ)
社内サービスとして動かす一覧に添える所見外部に提供するか、社内利用か
ネットワーク越しに提供する一覧に添える所見SaaSとして外部ユーザーに機能を出すか
ビルド・テストにだけ使う一覧に添える所見devのみか、成果物に混ざらないか

lockfileのdev: trueは、開発時の依存であることを示す手がかりになります。ただし、devの依存が成果物に混ざるかどうかは、バンドラーの設定やDockerfileの作りで決まります。lockfileの印だけで「成果物に入らない」と書き切らず、ビルド成果物の中身を確かめた結果を所見に書きます。AGPLのようにネットワーク越しの提供が論点になるライセンスもあるので、サービス形態の列は残しておくと役に立ちます。

法務に回す線引き

Claude Codeに任せるのは「事実の整理」までです。適合の判断と、例外を認めるかどうかは法務や責任者に回します。回す条件を先に決めておくと、毎回の相談が早くなります。

線引き

法務に回す行の目安

  • GPL系が単独で書かれている

    GPL、LGPL、AGPLのみ、またはANDで結ばれている行。選択の余地がありません。

  • 独自表記・表記なし

    SEE LICENSE IN、旧形式、空欄の行。ライセンス本文を人が読む必要があります。

  • ORに選択肢があるのに記録がない

    MIT OR GPL-3.0-or-laterのような行。どちらを選んだかを記録に残します。

  • 旧表記のGPL系

    GPL-3.0などの非推奨識別子。onlyかor-laterかをLICENSEファイルで確かめます。

法務への依頼には、パッケージ名と版、ライセンス表記、依存経路、組み込み方、成果物への影響の所見を1行にまとめて渡します。Claude Codeには、この依頼文の下書きまでを頼めます。結論の欄は空白にして、法務の回答を人が書き込む形にしておくと、判断者が誰かが記録に残ります。

定期的に回す

lockfileは依存の更新で変わるので、点検は1回で終わりません。claude -pを使うと、人が対話せずにスクリプトを呼び出して結果を整形できます。権限は--allowedToolsで絞ります。

claude -p "scripts/license-inventory.mjs を実行し、要確認の行だけを\
 JSON配列にしてください" \
  --allowedTools "Read,Bash(node scripts/license-inventory.mjs)" \
  --output-format json

claude -pは、CLAUDE.mdなどを含めて対話セッションと同じコンテキストを読み込みます。起動を軽くしたいときは--bareを付けると、自動検出を省略できますが、その場合はCLAUDE.mdの規約も読まれません。規約を使いたいなら付けずに回すか、プロンプト側に規約を書きます。--allowedToolsの詳しい書き方は、-pモードの基本にあります。

更新PRの検証と組み合わせる場合は、RenovateとDependabotのPRを検証する運用に、この点検を1ステップとして足す構成が考えられます。新しい依存が増えた更新PRだけで、要確認の行が増えていないかを比べます。

点検でつまずきやすい点

ライセンス表記が古い、または実態と合わない

package.jsonのlicenseは、パッケージの作者が書いた申告です。LICENSEファイルの内容と食い違うことがあり、スクリプトの結果だけで確定させることはできません。GPL系が疑われる行は、LICENSEファイルの本文を人が読んで確かめます。

lockfileにlicenseが入っていない

licenseの欄がない行は、スクリプトではすべて「表記なし」に分類されます。件数が急に増えたら、lockfileを再生成するか、license-checker系のツールで補います。

npm以外のエコシステム

PythonやGo、Rustなどにも、それぞれのメタデータにライセンス欄があります。考え方は同じで、取り出し方のスクリプトだけ差し替え、SPDX識別子とライセンス式の読み方は共通で使えます。Rのように別の仕組みで依存を固定する例は、Claude CodeでRの解析を進める記事で扱っています。

まとめ

一覧の作成と絞り込みはスクリプトに寄せ、Claude Codeには依存経路の調査と依頼文の下書きを任せます。適合の判断は、事実を並べた表を受け取った法務が行います。この分担を、CLAUDE.mdの規約とpermissionsの設定に書き込んでおくと、依存が増えても同じ手順で点検を繰り返せます。

この記事を共有:XはてブLinkedIn