← 記事ページへ戻る ishizakahiroshi.com
対象: intakelog

システム構成と導入動線

2026-08-05 / 設計 実装前 / npm 名の予約と公開リポジトリの作成まで完了

記事: lockfile では「いつ入れたか」が分からない / リポジトリ: ishizakahiroshi/intakelog

intakelog は「開発マシンに何を・いつ・どこから取り込んだか」を記録する CLI。脆弱性の判定は自分でやらず osv-scanner に任せ、自分は台帳経路の横断収集レポートに専念する。この資料は、そのシステム構成・利用者が使い始めるまでの流れ・出力のイメージをまとめたもの。

1. 何を解決するのか

lockfile と SBOM が答えられるのは「何が入っているか」だけ。サプライチェーン事案が起きた日に実際に必要になる情報は、そこには入っていない。

知りたいことlockfile / SBOMintakelog
今どの版が入っているか答えられる扱わない(重複させない)
いつ入れたか持っていない台帳に記録
どこから来たか(実 URL・ハッシュ)レジストリ経由のみ部分的台帳に記録
なぜ入れたか持っていない台帳に記録
npm 以外(OS のパッケージ管理・clone・シェル一行)対象外台帳に記録

きっかけ

2026 年 8 月 4 日の npm 大規模侵害(keyv / cacheable 系)で、「攻撃開始時刻より後に install したか」を調べる必要が出た。だが手元にその記録は無く、node_modules の更新時刻から推定するしかなかった。推定ではなく事実で答えられる状態を作るのが、このツールの出発点。

2. システム構成

取り込み経路(すべて監視対象) npm / pnpm / bun scoop / winget pip / cargo / go git clone curl | sh intakelog(収集) 即時記録 入れたその場で 1 行(理由まで残る) スナップショット差分 手で入れた分を後から補完(理由は残らない) 取り込み台帳(CSV・append-only) 日時 / 名前 / 版 / 入手元 / ハッシュ / 対象 / 理由 / 主体 intakelog(レポート生成) 台帳 + 照合結果を 1 枚にまとめる 未検査は必ず件数を出す osv-scanner(外部ツール) OSV オフライン DB で照合 依存構成を外部へ送らない 委譲 レポート(単一 HTML・ローカル) サーバー不要・ホスティングしない
取り込み経路を横断して台帳へ集め、脆弱性の判定だけ osv-scanner に委譲する。出力はローカルの HTML 1 枚で、外部に送信するものは無い。

各要素の役割

要素役割備考
即時記録取り込んだその場で台帳に 1 行足す理由(なぜ入れたか)が残るのはこの経路だけ
スナップショット差分各パッケージ管理の現状を台帳と突き合わせ、差分を追記手で入れた分を後から拾う。理由は残らない
取り込み台帳append-only の CSV。これが唯一の正典人が grep でき、差分がそのまま履歴になる
osv-scanner脆弱性・悪意あるパッケージの照合外部ツール。intakelog は再実装しない
レポート台帳と照合結果を 1 枚の HTML に合成依存なし・self-contained

なぜ 2 経路あるのか

即時記録だけでは、人が手で入れた分がまるごと落ちる。しかも落ちたことに気づけないので、「台帳がある」と信じてしまうぶん、台帳が無い状態より危険になる。だからスナップショット差分を必ず併走させて、抜けを構造的に埋める。逆にスナップショットだけでは「なぜ入れたか」が永久に残らないので、両方要る。

3. 利用者の導入までの動線

前提: osv-scannerPATH に必要(レポートの照合で使う)。見つからない場合はインストール方法を案内して止まる。無い状態で「検出ゼロ」を返すことはしない。

  1. まず試す
    npx intakelog scan — 何もインストールせずに、手元の状態を 1 回読むだけ。書き込みは行わない。
  2. 常用するなら入れる
    npm i -g intakelog — 繰り返し使うツールなので、常用はグローバル導入を推奨。
  3. 台帳を作る
    intakelog init — 台帳ファイルの置き場を決め、現在の状態を「初回スナップショット」として書き込む。
    この時点で、各パッケージ管理が自分で持っているインストール日時(例: scoop の更新時刻)から過去の導入日時を復元できる範囲は復元する。分からないものは空のままにする。
  4. 以後は取り込みのたびに記録する
    intakelog add <name> --why "理由" で明示的に、または intakelog sync で差分をまとめて。
    AI エージェントに作業させている場合は、エージェント側のルールに「取り込んだら 1 行足す」を入れておくと理由まで残る。
  5. 状態を見る
    intakelog report — osv-scanner を呼んで照合し、単一 HTML を出力してパスを表示する。
  6. 事案が起きた日に使う
    intakelog since 2026-08-04T09:00Z — 指定時刻以降に取り込んだものだけを一覧する。ここが本番。

4. システムイメージ

CLI の出力

下記は表示イメージ(合成データ)。実際の値ではない。

$ intakelog report

==> 台帳: 132 行(最終更新 2026-08-05 19:46)
==> 照合: osv-scanner 2.4.0 / オフライン DB(外部送信なし)
    lockfile 58 本 ... 48 本を走査

!! 走査できなかった: 10 本 — この範囲は未検査(異常なしではない)
   ./services/api/go.sum        理由: Go は go.mod を渡すこと
   ...

  悪意あるパッケージ (MAL) : 0
  CRITICAL                 : 2
  未検査の lockfile        : 10

→ ./intakelog-report.html

この出力で一番大事なのは「走査できなかった 10 本」の行。ここを黙って捨てると、出力は「異常なし」に見えてしまう。実際、試作段階でこの取りこぼしを 3 回踏んだ(エコシステムごとの DB 未取得・Go のファイル名の取り違え・引数の渡し方の誤り)。いずれも「0 件」と「未検査」が画面上で見分けられない形で出ていた。

レポート画面

サプライチェーン状態レポート 生成 2026-08-05 20:31 未検査の範囲があります(異常なしではありません) [未走査 lockfile の一覧 / 監視が無効なリポジトリの一覧] 悪意あるパッケージ 侵入痕跡 CRITICAL 未検査 台帳の行数 [n][n][n] [n][n] 脆弱性一覧(絞り込み・並べ替え可) [深刻度] [エコシステム] [パッケージ@版] [ID] [対象] [深刻度] [エコシステム] [パッケージ@版] [ID] [対象] [深刻度] [エコシステム] [パッケージ@版] [ID] [対象] 取り込みタイムライン [日時] [経路] [名前@版] [理由] [日時] [経路] [名前@版] [理由] [日時] [経路] [名前@版] [理由]
角括弧は実データが入る場所。未検査の警告帯を、件数カードよりに置くのが要点(数字だけ見て安心されないように)。

5. 台帳のかたち

date,kind,name,version,source,integrity,project,why,actor
中身取れないとき
date取り込んだ日時(タイムゾーン付き)空にする。推測で埋めない
kind経路(npm / scoop / winget / pip / cargo / go / git / shell …)
name / version名前と版。git は短いコミット ID
source実 URL(レジストリ名だけで済ませない)
integrityハッシュ・コミット ID
project入れた先。グローバルなら空
why導入理由 1 行差分検出のときは自動文言
actor人か、どのエージェントか

形式を CSV にしたのは、人が grep でき、git の差分がそのまま履歴として読めるから。データベースを持たない。

6. 設計原則

スキャナを自前実装しない

照合は osv-scanner に委譲する。セキュリティツールの偽陰性は「異常なし」という誤った確信を作るので、無いより悪い。その責任は上流の専門ツールに置く。

走査できなかったものを黙って捨てない

未検査・取得失敗は必ず件数を出す。黙って捨てると出力が「異常なし」に化ける。試作で 3 回踏んだ失敗の共通点がこれ。

append-only

既存行を書き換えない・並べ替えない。訂正は新しい行を足して理由を書く。履歴が書き換わらないことが台帳の価値。

推測で日時を埋めない

取れない値は空にする。推定値を事実として記録するのが、このツールで最も害の大きい振る舞い。

実行時依存ゼロ

サプライチェーンを見る道具が、自分のサプライチェーンを抱えない。TypeScript の標準ライブラリだけで書く。

オフラインが既定

照合はローカル DB で行う。非公開プロジェクトの依存構成を外部の API に送らない。オンライン照会は明示指定したときだけ。

7. やらないこと

8. 現在地とこれから

状態内容
完了名前の調査(商標・レジストリ・ドメイン)と npm 名の予約
完了公開リポジトリの作成、ライセンス、秘匿情報スキャンの配線
台帳フォーマットの確定と、収集部(各パッケージ管理の読み取り)の実装
未着手レポート生成、CI、クロスプラットフォーム検証、初回リリース

9. 決まったこと

この構成のレビューで、次の 3 点が確定した。ここが実装の前提になる。

スキャナは自前実装せず osv-scanner に委譲する
この方針のまま進める。プロトタイプの段階で偽陰性を 3 回踏んでおり、セキュリティツールの見逃しは「異常なし」という誤った確信を作るため、その責任は上流の専門ツールに置く。
台帳の形式は CSV
人が grep でき、git の差分がそのまま履歴として読める形を優先する。構造化より可読性と可搬性を取る。
先に公開して、それから実装する
npm の名前を予約し、リポジトリを立て、経緯を記事にするところまでを先に済ませた。実装はこれから。