Files
maestro/docs/security/dependency-egress-inventory.md
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

19 KiB

依存パッケージの外向き通信インベントリ

調査日: 2026-09-08 基準コミット: cf2bc52d6725da17b207202bdc6eaf7ac9f0cab7

この文書は、root と UI の lockfile を全件機械集計し、展開済み依存のランタイム JavaScript/JSON をテレメトリ関連語彙で絞り込んだ初段の棚卸しである。通常利用時に依存が自動収集・送信する telemetry、analytics、crash report、usage report を主対象にする。利用者が明示した API 接続、WebFetch、通知、MCP、OAuth、LLM 接続などは対象外である。悪意ある文書や入力で通信を起こす攻撃経路も、この文書では扱わない。

結論

確認した依存候補から、通常利用時に自動送信される telemetry、analytics、crash report は見つからなかった。@opentelemetry/api と @univerjs/telemetry は lockfile に存在するが、前者には exporter、後者には送信実装がなく、アプリにも送信 provider の登録がない。

以前、通常 UI 操作で発生していた Google Fonts、jsDelivr の Monaco、unpkg の Leaflet 資産取得は、現在の変更でローカル npm 資産へ切り替えた。地図を開いた際の OpenStreetMap タイル取得は残る。これは telemetry ではないが、通常 UI 操作で第三者へ出る通信なので記録する。

この結論は下記の「確認済み」の経路に限る。語彙走査にヒットした全ファイルを手作業で監査したという意味ではなく、動的な全通信先監視や全依存の制御フロー監査も実施していない。

Lockfile 集計

対象は lockfile v3 の packages である。空キーの root record、link: true のローカル workspace link、その link の実体を表す node_modules/ 外の workspace record を各 lockfile の package 件数から除外した。dev !== true を production install に含まれる record、dev === true を development-only record とした。同一パッケージの複数版やネスト配置は別 record として数える。

対象 直接 production 宣言 直接 development 宣言 package records production に含む development-only 推移依存 records 推移 production 推移 development-only 一意な名前 一意な名前+版
root package-lock.json 44 22 633 446 187 568 400 168 551 598
UI ui/package-lock.json 44 12 497 436 61 442 389 53 407 444

2 lockfile の合計は 1,130 package records で、同じ名前+版を両方の lockfile 間でも重複除外すると 984 組である。ローカル workspace link の @aao/answer-worker-contract とその実体 record は表から除外した。

集計時の SHA-256 は次の通り。

  • package-lock.json: c3a1e69f7160b1bcc29969d249ab7b9f34b7ea55b725409a7a3efb6dc0d8dad5
  • ui/package-lock.json: 4c489f8f91a933a3cff6223b91a76dee570d29504c704139b47dbd4c57a49f2d

直接依存の数は manifest の宣言数であり、package record の分類とは別軸である。例えば development に直接宣言されたパッケージが production 推移依存としても必要な場合、その record の dev は true にならない。

通常 runtime の外向き経路

状態 依存・経路 判定と根拠
確認済み・外向き通信あり OpenStreetMap tiles 地図詳細を表示し、場所が 1 件以上ある場合に https://tile.openstreetmap.org/{z}/{x}/{y}.png を取得する。タイル座標と通常のブラウザ要求メタデータが送られる。MapPlacesDetail.tsx
確認済み・ローカル化 Leaflet 1.9.4 JS、CSS、marker 画像を npm 依存から動的 import する。以前の unpkg script/CSS は使わない。MapPlacesDetail.tsx
確認済み・ローカル化 Monaco Editor 0.55.1 Monaco 本体と各 worker を npm 依存から import し、loader.config({ monaco }) へ渡す。@monaco-editor/loader の既定 jsDelivr 経路は使わない。monaco-loader.ts
確認済み・ローカル化 IBM Plex / JetBrains Mono Fontsource の CSS とフォントファイルをビルド資産に含める。以前の Google Fonts CSS は使わない。index.css
確認済み・送信なし prom-client / @opentelemetry/api @opentelemetry/api 1.9.1 は prom-client 15.1.3 の推移依存で、lockfile に SDK/exporter/instrumentation package はない。アプリは process metrics をローカル registry に収集し、受信した /metrics scrape に返す pull 型である。registry.ts metrics-subsystem.ts
確認済み・送信なし @univerjs/telemetry 0.25.1 展開済み package は ITelemetryService identifier のみを公開する。@univerjs/sheets-ui は sheetId、unitId、描画時間統計を optional service へ capture できるが、アプリの登録プラグインに telemetry provider はない。UniverGrid.tsx
確認済み・ローカル処理 Mermaid / PDF Mermaid は npm bundle を直接 import し、server renderer も vendor/ または ui/node_modules の bundle を読む。FilePreviewContent.tsx mermaid.ts PDF はローカルファイルの Buffer を PDFParse に渡す。office.ts

アプリ機能による通常の外向き通信

以下は自動 telemetry ではなく、設定済み機能を動かすための通信である。依存 package の存在だけでは発生せず、表の条件を満たしたときに有効になる。

機能 宛先と有効化条件 送信する主なデータ 状態
LLM 呼び出し 設定した OpenAI 互換 endpoint。タスクがモデルを呼ぶと POST <base>/chat/completions。openai-compat.ts API key、会話 messages、model、tools、生成設定 確認済み
設定画面の model 一覧取得 model 設定画面で endpoint が入力されると、ブラウザから直接 GET <endpoint>/models。literal API key は Bearer header に入る。env_ref / masked key は取得を行わず、key を cleared にした場合は無認証で取得する。ModelSelect.tsx 設定中の endpoint、Accept header、literal の場合は API key 確認済み
LLM health / capacity probe 設定した direct worker の /slots、/health、/metrics、proxy worker の /health。稼働中は約 5 秒ごと。direct probe は worker API key を送らず、proxy /health は設定された key を送る。backend-probes.ts backend-probes.ts GET、Accept、proxy の場合は Authorization。応答から稼働状態・slot・throughput を読む 確認済み
OAuth と avatar 管理者が Google または Gitea provider を有効にし、利用者がログインした場合。Gitea は設定 base URL の authorize/token/user/orgs、Google は Passport strategy の provider endpoint を使う。auth.ts provider が返した avatar URL は画面の <img src> からブラウザが取得する。UserAccountMenu.tsx OAuth client情報、authorization code/token、user profile/org照会。avatar取得時はURLと通常のbrowser metadata 確認済み
Web Push 利用者が通知を有効にし push subscription がある場合、その subscription endpoint。push-service.ts 既定はevent種別、task ID、汎用文言。includeDetails opt-in時はtask titleとpiece/summaryも含む。VAPID署名とsubscription keysを使用 確認済み
Space webhook workspaceでDiscord/Slack/Teams webhookを登録・有効化し、対象eventが発生した場合、登録したHTTPS URL。webhook-service.ts webhook-service.ts event、task ID、task URL。includeDetails 設定によりtask title、piece、space名。JSON POST 確認済み
MCP 暗号化キーを設定し、HTTP MCP serverを登録して接続・tool list・tool callを行う場合、登録した server URL。Bearer または指定headerへtokenを付ける。client-factory.ts tool-executor.ts MCP初期化/capability、tool名と引数、認証token 確認済み
起動時の不足資産補完 scripts/server.sh start は毎回 prepare.sh を実行する。Chromium が無い場合は Playwright CDN から取得し、共有library不足時は playwright install-deps がOS package repositoryを使い得る。server.sh prepare.sh Playwright/npm/OS標準のdownload request metadata。アプリのtask内容は送らない 確認済み・条件付き

npm ci、Docker build、setup.sh、upgrade.sh に伴う registry、Git、OS package、Playwright browser の取得は管理者が明示的に行う install/build/update 通信であり、通常 runtime の telemetry 判定には含めない。

ローカル UI 資産の回帰検査

次の検査をリポジトリ root で再現できる。

npm run security:ui-assets

check-local-ui-assets.mjs は実際の MonacoFileEditor、MapPlacesDetail、index.css を一時 Vite アプリへ読み込み、production build を Chromium で実行する。2026-09-08 の検査は exit 0 で成功し、3 font family のローカル読込、Monaco の編集・保存と same-origin worker、Leaflet の 2 marker の表示・画像 decode を確認した。OpenStreetMap の tile request は検査内でローカル応答し、それ以外の cross-origin request と不要な WebSocket 接続は実接続前に遮断して失敗にする。画面表示後に Google Fonts を意図的に追加する negative fixture で、遅れて発生する要求も検出した。

フォントと地図タイルの要求件数は描画条件で変わるため、各実行の集計をコマンド末尾へ出力する。

この動的検査の範囲は上記 UI component と資産に限る。アプリ全画面の操作や server workload を動かして全通信を監視する検査ではない。

テレメトリ語彙の候補確認

展開済み依存の .js、.cjs、.mjs、.json を走査し、source map は除外した。使用した語彙は telemetr、analytics、sentry、datadog、newrelic、mixpanel、amplitude、posthog、rollbar、bugsnag、phone.?home、usage.?report、update.?check、license.?check、opentelemetry である。

対象 展開済み lock records lock と同一版 不一致 未展開 走査ファイル 走査量
root 550 / 633 550 0 83 9,879 130,972,307 bytes
UI 469 / 497 469 0 28 15,076 258,538,747 bytes

root と UI はこの worktree で npm ci --ignore-scripts --no-audit --no-fund 後の展開物を使った。存在した root 550 records と UI 469 records はすべて lockfile と同一版だった。未展開の root 83 records と UI 28 records はすべて optional: true で、現在の OS/CPU 以外の native package などを含む。

手動で送信経路まで確認した候補は次の通り。

  • @opentelemetry/api とその導入元 prom-client: API とローカル registry だけで、exporter package と外向き送信 sink は確認されなかった。
  • @univerjs/telemetry と @univerjs/sheets-ui: identifier と optional capture 呼び出しはあるが、送信 provider の実装・登録は確認されなかった。
  • monaco-editor: telemetry という名前の内部型・service は含むが、MAESTRO が collector や転送先を登録する箇所は確認されなかった。現在は editor code と worker もローカル配信する。

pdfjs-dist、pdf-parse、Playwright、Mermaid、TypeScript、highlight.js、React、各種 CSS/parser package などにも語彙ヒットがあった。エラーサービス名を入力データとして扱う parser、CSS の scrollbar に含まれる文字列、テスト・開発コード、一般的な型名などが混在するため、これらは keyword candidate のまま未検証 である。語彙ヒットだけを telemetry 実装の証拠にはしていない。

Install script

lockfile の hasInstallScript: true を全件抽出した。これは install 時のコード実行面であり、通常 runtime の自動 telemetry とは別である。

lock package 区分 展開・確認状態 install 時の挙動
root [email protected] 直接 production 確認済み `prebuild-install
root [email protected] 直接 production 確認済み node install.js から optional crypto binding を node-gyp rebuild。script 自身に HTTP client はない。node-gyp はローカル header が無い環境では header 取得を行い得る。
root [email protected] 推移 production・optional 確認済み buildcheck 後に node-gyp rebuild。script 自身に HTTP client はない。
root playwright/node_modules/[email protected] 推移 production・optional Linux では未展開、未検証 lockfile flag のみ確認。macOS 向け optional package。
root [email protected] 推移 development-only 確認済み node install.js。対応 platform の optional binary が無ければ npm を起動し、それも失敗すると https://registry.npmjs.org/@esbuild/* の tarball を直接取得する。
root [email protected] 推移 development-only・optional Linux では未展開、未検証 lockfile flag のみ確認。macOS 向け optional package。
UI [email protected] 推移 production 確認済み node scripts/postinstall は親 manifest の semver 記法を検査して warning を出すだけで、通信しない。
UI [email protected] 推移 production・optional Linux では未展開、未検証 lockfile flag のみ確認。macOS 向け optional package。

通常の build-all.sh は plain npm ci を使うため、該当 platform では lifecycle script が動く。Docker build は builder と runtime の両方で npm ci --ignore-scripts を使い、その後に検査済みの native rebuild を行う。Dockerfile Dockerfile

再現方法

件数は次の Node.js スクリプトで再現できる。

node <<'NODE'
const fs = require('node:fs');
for (const file of ['package-lock.json', 'ui/package-lock.json']) {
  const lock = JSON.parse(fs.readFileSync(file, 'utf8'));
  const root = lock.packages[''];
  const direct = new Set([
    ...Object.keys(root.dependencies ?? {}),
    ...Object.keys(root.devDependencies ?? {}),
    ...Object.keys(root.optionalDependencies ?? {}),
  ]);
  const rows = Object.entries(lock.packages)
    .filter(([path, value]) => path.includes('node_modules/') && value.link !== true);
  const isDirect = ([path]) => {
    const name = path.replace(/^node_modules\//, '');
    return direct.has(name) && path === `node_modules/${name}`;
  };
  const count = (predicate) => rows.filter(predicate).length;
  console.log(file, {
    directProduction: Object.keys(root.dependencies ?? {}).length,
    directDevelopment: Object.keys(root.devDependencies ?? {}).length,
    records: rows.length,
    productionIncluded: count(([, v]) => v.dev !== true),
    developmentOnly: count(([, v]) => v.dev === true),
    transitive: count((row) => !isDirect(row)),
    transitiveProduction: count((row) => !isDirect(row) && row[1].dev !== true),
    transitiveDevelopment: count((row) => !isDirect(row) && row[1].dev === true),
  });
}
NODE

install script 候補は次で再現できる。

node -e 'for (const f of ["package-lock.json","ui/package-lock.json"]) { const l=require("./"+f); console.log(f,Object.entries(l.packages).filter(([p,v])=>p&&v.hasInstallScript).map(([p,v])=>({path:p,version:v.version,dev:!!v.dev,optional:!!v.optional}))) }'

語彙候補は、同じ拡張子・除外条件で次のように再抽出できる。

rg -l -i 'telemetr|analytics?|sentry|datadog|new\s?relic|mixpanel|amplitude|posthog|rollbar|bugsnag|phone.?home|usage.?report|update.?check|license.?check|opentelemetry' node_modules ui/node_modules --glob '*.js' --glob '*.cjs' --glob '*.mjs' --glob '*.json' --glob '!**/*.map'

この調査で使った機械集計結果は /tmp/dependency-egress-inventory.json、語彙走査結果は /tmp/dependency-egress-scan.json に残した。これらは一時成果物であり、再現の基準は上記 lockfile hash と手順である。

限界と次段

  • lockfile の全 records は集計したが、全依存の全関数を手作業で監査してはいない。
  • root 83 records と UI 28 records は、この OS/CPU では展開されない optional package だった。これらは lockfile の package metadata と名前だけを棚卸しし、実コードは走査していない。
  • keyword scan は難読化、動的 URL 組み立て、語彙を含まない独自収集を検出できない。逆に、多数の false positive を含む。
  • 通常 telemetry と別の、利用者設定による外部 API 通信、外部コンテンツ取得、悪意ある入力を使った egress はこの文書の結論に含めない。
  • 強い保証が必要な次段では、隔離ネットワーク上で主要 UI 操作と server workload を実行し、DNS・HTTP(S)・WebSocket を process/browser 単位で記録する。未展開 optional package は対象 OS ごとに --ignore-scripts で展開して静的確認し、必要な lifecycle script だけを別の隔離環境で観測する。