sourcemapのsourcesContentは、どのcommitのsourceか特定できるか。Git historyと照合するsourcemap-lineage
npmで公開されているJSファイルには、しばしば .js.map が付属している。中身を見ると sourcesContent というフィールドに、ビルド前のsource全文がそのまま入っていることがある。これを見て、「このartifactが、手元にcloneしたGit historyのどのあたりから来たのか分かるのではないか」と思ったのが出発点だった。
sourcesContentだけでは、commitは分からない
sourcesContent は「ビルド時にそのsource fileがどんな中身だったか」を教えてくれる。しかしそれだけでは「どのcommitのsourceか」までは分からない。同じ内容のfileが複数のcommitに存在していれば、sourcesContent単体ではそのどれか一つに絞り込む手段がない。
これを補うために作ったのが sourcemap-lineage という、read-onlyのCLI toolになる。
Git historyと照合する
sourcemap-lineageがやっていることは単純で、artifactのsourcemapから sourcesContent を取り出し、手元にcloneしたGit repositoryの各commitの該当fileと比較するだけ。使っているgit操作も git log、git ls-tree、git cat-file のみで、履歴の中身を読むだけの操作に限られる。
node sourcemap-lineage.cjs dist/app.js --repo /path/to/repo
recoverしたsourceの内容が、あるcommit時点で追跡されているfileのblobと一致すれば、そのcommitは「候補」になる。一致しなければ、そのcommitは候補から外れる。
同じsourceのcommitは区別できない
ここで問題になるのが、対象fileの中身が変わっていないcommitが複数存在する場合だ。たとえば「READMEだけ直したcommit」は、src/以下のfileの中身をまったく変えていない。sourcemap-lineageが見ているのは追跡対象fileのblobの一致なので、この2つのcommitは区別がつかない。
ここで、無理に「どちらか一つ」を選ばないようにした。sourcemap-lineageは、blobが一致するcommitをまとめて一つの候補groupとして報告する。1個のcommitに絞り込めたように見せかけるのではなく、「区別できない」という事実をそのまま出力に残す設計にしている。
検証済みの実例: Immer 10.1.1
npmで公開されている immer@10.1.1 の dist/immer.mjs を対象に、公開情報だけで再現できる形で検証した。
- artifact:
dist/immer.mjs(SHA-256:251bf957...2fc49a) - npm registryのgitHeadメタデータ(今回の照合対象の正解として使うが、tool自体には渡していない):
e2d222bd4fb26abded04075c936290715e9ee335 - 公開GitHub repository: immerjs/immer
sourcemap-lineageを実行すると、12個のsourceすべてがGit historyの特定のblobと完全一致した。
2 commitのgroupまで絞れた
結果は次の通りだった。
- sourceGit: EXACT(12/12 exact match)
- 候補commitは2個のgroup(gitHeadに記録されていた正解commitは、そのgroupに含まれていた)
- Classification: AMBIGUOUS / Evidence level: STRONG_CANDIDATE
- dirtyBuild: NOT_INDICATED
「12個全部のsourceが完全一致」という強い証拠があっても、それだけでcommitを1個に確定させることはできない。この2 commitはsrc/以下のtracked fileが完全に同一で、artifactの側から見分ける手段がないからだ。
しかしprovenance proofではない
ここが一番大事な点になる。sourcemap-lineageが示しているのは「recoverしたsourceの内容が、Git historyのこのあたりのcommitと一致する」という事実だけであって、「このartifactが実際にどのmachine・どのbuild環境で作られたか」を証明するものではない。SLSAのようなbuild provenance、tamper-proofなattestation、exact single-commitの断定とは別物であり、実行結果には毎回 Artifact-level attribution: NOT_ESTABLISHED と明示している。
Immerのケースでも、AMBIGUOUS と STRONG_CANDIDATE が同時に出ているのは矛盾ではない。「source側の一致は強いが、それだけでは1つのcommitに決められない」という状態をそのまま表している。
Reduxでは何も分からないままだった
もう一つ、逆のケースも記録しておきたい。redux@5.0.1 の dist/redux.browser.mjs を同じ手順で調べたところ、sourcemapに含まれる10個のsourceのうち、公開repositoryのどのcommitのblobとも一致しなかった。
この場合、sourcemap-lineageは無理に「一番近そうなcommit」を候補として出すことをしない。Classification: UNKNOWN として、候補commitもsource差分も一切表示せずに終わる。中途半端な一致から誤った候補を出すより、何も分からない状態を「分からない」とそのまま返す方を選んでいる。
両方のscriptは、公開されているnpm packageとGitHub repositoryだけで再現できる(real-cases/ 以下)。
GitHub
github.com/iwadjp/sourcemap-lineage
これまで作った他のtoolについては、こちらにまとめている。