GitHub の private vulnerability reporting を使ってください。
このリポジトリの Security タブ → Report a vulnerability から、非公開でやりとりできます。
- **Issue へは書かないでください。**Issue は公開されます。
- メールアドレスはここに載せていません。上の経路なら、 報告する側も受ける側もアドレスを明かさずに済みます。
返信の目安は 7 日です。個人が趣味で維持しているリポジトリなので、 それ以上かかることがあります。急ぐ場合はその旨を書いてください。
| ✅ | **配布物を読む側が害を受けうるもの。**同梱の verifyReleaseSourceInputs.mjs に、細工した archive で任意のファイルを書かせる・読ませる・止められる経路があるなど |
| ✅ | ビューア(npm run dev / build 成果物)で、開いただけで害があるもの |
| ✅ | リポジトリや release に、意図せず秘密情報が入っているのを見つけたとき |
| ✅ | 依存パッケージの既知脆弱性で、このプロジェクトの使い方で実際に踏むもの |
依存の脆弱性は、踏む経路を添えてください。npm audit の出力だけだと、
この使い方では到達しないものが大半で、切り分けから始めることになります。
同じことを求めるので、こちらの切り分けも出します。
| 対象 | nanoid 3.3.16(<3.3.18) |
| 深刻度 | high — GHSA-2v37-7h3g-55p8 |
| 経路 | vite → postcss → nanoid(推移依存。直接依存ではありません) |
| 区分 | dev のみ(npm ls nanoid --omit=dev が空。対照として three は解決します) |
| 出荷物 | 入っていません(dist/assets/*.js に nanoid の文字列が 0 件。対照として three は 1 件) |
| 修正版 | 3.3.18 が在ります(fixAvailable: true) |
**踏む経路が無いと判断しました。**この advisory は「独自の生成器へ長さ 0 を渡すと
無限ループしうる」もので、ビルド時に postcss が使う経路には当たりません。
受け手が受け取る成果物にも入っていません。
まだ上げていません。踏む経路が無いと測れたので、
依存の更新は監査への対応とは切り離して判断します
(package-lock.json は記録された入力で、触ると全 artifact を作り直すことになるため、
更新するなら「確かめた物と配る物を別にしない」工程を通します)。
**この判定は 2026-08-15 時点の実測です。**そちらで測り直せます。
npm audit --json
npm ls nanoid --omit=dev # 空なら dev のみ
grep -c nanoid dist/assets/*.js # 0 なら出荷物に無い
対象
-
mainの最新 -
直近の release 1 本——GitHub Releases で
Latestと表示されている 1 本です。ここに版数を書かないのは、tag の中身が恒久的に 1 版古くなるからです。 release を作る commit の時点では、その版はまだ公開されていません。 版数を書くと、その tag の
SECURITY.mdは必ず 1 つ前の版を指したまま固まります (実測: v0.6.18 / v0.6.19 / v0.6.20 / v0.6.21 の 4 tag すべてがそうなっていました。 外部監査 P2・v0.6.22 で直しました)。mainを見れば正しい値が書いてありますが、 受け手が読むのは tag の中身です。
対象外
-
それより古い release。過去の release asset は上書きしません(immutable に残します)。 古い版に問題があった場合は、新しい版で直して、古い版に問題があることを notes へ書きます。
この「上書きしません」は、そちらで確かめられます。
npm run check:published-assets基準は
test/fixtures/published-assets-baseline.v1.json(tag と asset 名と digest の一覧)。 いま公開されているものと突き合わせ、1 件でも digest が変わっていれば落ちます。 同じ実行の中で基準を 1 件だけ壊した写しも当てて、一致がちょうど 1 件減ることまで見ます ——検査が壊れていれば「全部無傷」も「全部消えた」も同じように通ってしまうためです。取得に失敗したときは件数を出さずに終了コード 2 で止まります。 「取れなかった」と「消えた」を同じ 0 件として報告しないためです。
-
docs/の文章の誤り。これは security ではなく、通常の Issue でお願いします。 -
開発時のみ使うもの(
test/の道具・scripts/のうち release に同梱していないもの)。 ただし同梱しているもの(verifyReleaseSourceInputs.mjs)は対象です。
ここは security の話より先に、何を売っていないかの話です。
half_plug_topology_profile.v3.*.json に入っている区間・event・接点状態は、
寸法モデルを走査した計算結果であって、実物を測った値ではありません。
modelLimitations.verifiedPhysical false
この値が false である限り、実物との突き合わせは 1 件も済んでいません。
2026-08-06 時点で false です。判定は
docs/VERIFIED_PHYSICAL_GATE.md の条文に従って
docs/measurements/measurement-records.v1.json の記録から機械で決まります。
記録は 0 件です。
- 根拠の区分は
FACT(一次情報)/DERIVED(演算)/ASSUMPTION(仮定)で、ASSUMPTIONが 54 件あります(artifacts/verification_summary.json)。 - ジャック内部の接点ばね寸法はすべて仮定です(UNKNOWNS.md §3)。
- 導通・音響の実測はしていません。
この artifact を、安全性に関わる判断の根拠にしないでください。 機器の設計・適合性の判断・事故の原因究明などに使う場合、 **実物で測り直してください。**手順は docs/VERIFICATION_PLAN.md にあります。
同梱の verifyReleaseSourceInputs.mjs は、
**「配布物に記録された入力の sha256 が、tag の source と一致するか」**を計算し直すだけです。
- こちらの自己申告が正しいことは証明しません。 同じ人が作った manifest と、同じ人が push した source を比べています。
- **モデルが正しいことも証明しません。**入力が記録どおりであることしか見ていません。
- v0.6.0(
toolVersion5)で、信頼できない archive に対して安全に止まるようにしましたが、 すべての細工に耐えることは示していません。190 個・20 種類で試験した範囲までです (docs/release/verify-tool-v5-notes.md)。 現に、塞いだつもりの穴が繰り返し残っていました。 v5 → v6 で 3 つ(同名 entry の後勝ち・ディレクトリ入力の symlink ループ・圧縮入力の上限なし)、 v6 → v7 で 2 つ(同じ場所を指す別の綴り・読み飛ばす entry との衝突)、 v7 → v8 で 3 つ(PAX の上書き・読み飛ばす entry の正規化漏れ・パス末尾の空白)、 v8 → v9 で 3 つ(名前の上書きが 2 つ効く形・展開されるのに数えない entry・不正 UTF-8 の置換)、 v9 → v10 で 4 つ(ディレクトリか確かめずに先頭階層を剥がす・受理するのに展開できない archive・PAX の NUL 切り捨て・閉じていない denylist)、 v10 → v11 で 3 つ(自分自身を指す hardlink・ディレクトリを指す hardlink・ 値が読めない PAX と中身を持てない型の本体)、 v11 → v12 で 3 つ(祖先が通常ファイルや symlink でも、その下の entry を受理していた・ linkname の上書きに状態機械が無かった・PAX の値の範囲とuname/gnameの文字符号)、 v12 → v13 で 2 つ(生の USTAR 数値欄をsizeしか見ていなかった——mode/uid/gid/mtimeにabcを書いて checksum を取り直した archive をOKと言っていた・ local PAX の pending 状態がpath/linkpathにしかなく、mtimeだけのxを 末尾に置いた archive が素通りしていた)、 v13 → v14 で 3 つ(ヘッダ形式を確かめずに 345..499 を prefix として読んでいた—— old GNU ではそこは atime/ctime/sparse の領域で、bsdtar は prefix を使わず python は使うので 同じ archive から別の木ができた・typeflag が除外表だったのでZや空白のような知らない型が 素通りし、中身を数えないままOKと言っていた・長さ 0 の PAXpath=で member が 丸ごと一覧から消えていた)、 v14 → v15 で 2 つ(長さ 0 の PAX 鍵が、鍵の分類を丸ごと迂回していた——size=やSUN.holesdata=のように値を空にするだけで allowlist を飛ばせた。 これは v14 でこちらが開けた穴です・終端 zero block のあとを一度も見ておらず、 zero block を 1 個置いた後ろに member を隠せた)、 v15 → v16 で 3 つ(範囲定義を manifest に縛っていなかった——--scopeを 中身も確かめずに受けていたので、範囲を狭めるだけで「漏れ 0 件」を作れた・ 名前が空の member を黙って捨てていた・directory を渡したとき FIFO などの 特殊なノードが一覧から消えていた)。 どれも外部監査の指摘で、こちらで反例を再現してから直しています。 v16 では、指摘に無い 2 件をこちらで見つけて塞ぎました (本体の詰め物が欠けた archive と、終端の印を見ないまま尽きる archive。 どちらも 2 実装が拒むのに受理していた)。 v12 の 1 件は、その再現の途中でこちらが見つけたものです (hardlink の指す先の末尾スラッシュを剥がして受理していた。監査の指摘にはありません)。 v8 からはふつうの tar 展開を oracle にした差分試験を置き、 「検算が見た中身」と「展開してできる中身」が食い違ったら落ちるようにしました (verify-tool-v8-notes.md)。 その差分試験自体に、版ごとに穴がありました。 v8 は片方向で「展開されるのに検算が数えない」欠陥を素通りし(v9 で逆向きを追加)、 v9 は oracle が 1 実装だけだったので、oracle と同じ癖の欠陥を見つけられませんでした (v10 で python tarfile を必須 oracle に追加。 verify-tool-v10-notes.md)。 OKの意味を狭めました(v16・外部監査 §1)。 v15 までstatusは不一致が無いことしか見ておらず、 記録漏れの探索をしていなくてもOKになりました。 受け手には「探して見つからなかった」と読めます。やらなかったことは合格ではありません。 できていない工程があればVERIFICATION_INCOMPLETE(exit 1)と言います。 v0.4.0 より前の tag(v0.3.0 自身も対象)はこれに当たります——その tag には範囲定義が無いためで、 sha256 の突き合わせ自体は今までどおり実施しています。- 正当な archive を拒む欠陥が、7 版続けて見つかりました。
v9 は独立した 2 つの member がそれぞれ長い名前を使うだけで
ARCHIVE_INVALIDになり、 v10 は GNU の長い linkname(K)と PAXlinkpathを拒み、 v11 は GNU tar がふつうに書く負の時刻(mtime=-1)と、hardlink の連鎖と、 指す先の別の綴り(./root/Aなど)を拒んでいて、 v12 は directory の PAX path が/で終わる形・PAX の値の先頭ゼロ・ 歴史的な signed checksumを拒んでいました (いずれも実装が展開できる形です。v10 / v11 / v12 / v13 でそれぞれ修正)。 そして v13 は 同一 PAX ヘッダ内の重複鍵とリンクの名前の末尾スラッシュを 拒んでいました(v14 で修正)。 先頭ゼロは、前回の監査が勧めた正規表現をそのまま採ったことが原因です ——勧告を機械的に採ると、その勧告自体が過剰拒否になりうる。 そのため v14 では、監査の勧告のうち 3 点を採らず、 採らない理由を実測で示しました(verify-tool-v14-notes.md §6)。 勧告どおりに書くと、GNU tar 自身の既定の出力形式を拒むことになるためです。 塞ぎすぎは「実物が通る」確認では見つかりません——この repo の実物は 最長パス 95 文字で、これらの機構を使わないためです。 そして v14 は 正当な old GNU sparse・mtime=1.・冪等な directory の重複を 拒んでいました(v15 で修正)。 そして v15 は metadata だけの PAX が GNUL/Kと共存する正当な形を 拒んでいました(v16 で修正)。 v16 では corpus の「通す」材料を 72 個へ増やしました(v0.6.6 時点は 9 個)。 過剰拒否のうち 1 件は監査の指摘ではなく、こちらの根拠検査が見つけました (頭 1 個しか無い archive を「壊れている」と言っていた)。 逆に、こちらが「過剰拒否だ」と判断して通すように直した 2 件は誤りでした—— 長さ 0 のmtime=/uid=は、GNU tar が archive ごと拒みます。 開発機に GNU tar が無く、手元の 2 実装がそろって通したのを「割れていない」と読んだためで、 CI の ubuntu 側が落として分かりました。 2 実装で足りるかどうかは、2 実装では決められません。 - 止める理由を 2 つに分けました(v12)。
ARCHIVE_INVALIDは「矛盾・破損・曖昧、または展開できない」、ARCHIVE_UNSUPPORTEDは「ふつうの tar なら展開できるが、この道具の範囲の外」です。 v11 までは後者もARCHIVE_INVALIDと言っており、 展開できる archive を「壊れている」と呼んでいました。 どちらも exit code は 2 で、OKにはなりません。 - 手元で確かめられないことは、確かめられないと書きます。
止める理由の半分は、片方の実装だけでは見えません。
その run の 2 実装がそろって通すのに止めているものが 35 件あり、内訳は
GNU tar 側でだけ根拠が取れる 15 件 / bsdtar 側でだけ取れる 11 件 /
どちらでも取れていない 9 件です(2026-08-14 実測)。
bsdtar 側の 1 件は、CI の ubuntu 側が落として分かりました。
ドライブレター付きのパス(
C:evil.txt)は、bsdtar が剥がして python が残すので macOS では同じ archive から別の木ができます。ubuntu では GNU tar と python が そろってC:evil.txtを作るため、根拠が消えます。 開発機だけで「割れている」と読むと、根拠を過大に報告します ——v15 のときは逆向き(過小)で同じことをしています。 「どちらでも取れていない」が v18 で 5 → 9 件へ増えました。 増えた 4 件はパスの綴り(root/./x・root//x・前後の空白・制御文字)で、 前から止めていたのに corpus に材料が無く、一度も oracle に掛かっていませんでした。 外部監査(2026-08-12)が「catalog に在るのに踏まれない止め方」を指摘し、 材料を足した瞬間に手元の 2 実装がそろって通すことが分かったものです。 止めているのは実装の割れではなく、方針です ——同じ場所を別の綴りで指せると、記録との突き合わせが意味を失うためです。 「どちらでも取れていない」が v15 で 2 → 5 件へ増えました。 増えた 3 件は新しく直した箇所ではなく、材料を足したときに 「前から根拠なく止めていたもの」をこの試験自身が炙り出したものです (見え方を変える鍵の方針・通常ファイルの重複)。 一度これを 8 件と書きましたが、多すぎました——hdrcharsetと終端 zero block の 3 件は、GNU tar が警告を出していた(A lone zero block at 3など)ことが CI の ubuntu 側で分かりました。開発機の 2 実装だけで「根拠が無い」と書くと、 根拠を過小に報告します。 「実装が割れているから止めた」と書けないものは、方針で止めていると書きます。 「どちらでも取れていない」と書いた行は、次に直す候補の一覧でもあります—— v14 で減った 1 件は、まさにその行が過剰拒否だったものです。 一覧はtest/tarExtractionOracle.test.tsのEVIDENCE_ELSEWHEREにあり、 どこで根拠が取れるかを毎 run 両方向で照合します (verify-tool-v12-notes.md §7)。 - CI を GNU tar(ubuntu)と bsdtar(macOS)の matrix にしました(v12)。 v11 まで CI は ubuntu 1 本、開発は macOS だったので、 2 実装が同じ変更に対して同時に効いたことが一度もありませんでした。 最初の run で ubuntu 側が 8 件落ち、 止める理由の半分が片方の platform でしか測れていなかったことが分かりました (archive の扱いではなく、試験の書き方の欠陥でした)。 差分試験は v13 でパスの一覧ではなく型つきの木(型・指す先・中身のバイト)を比べ、 必須 oracle が動いていなければ「合格」ではなく失敗にします。
- CI は read-only です(
.github/workflows/ci.yml・permissions: contents: read)。 publish する経路を持ちません。action は full commit SHA で固定しています。 - **release asset は上書きしません。**v0.1.0 以降のすべての asset について、 release のたびに byte 一致を確認しています。
- 依存は
package-lock.jsonで固定しています。npm ciで入れてください。
直したものは、何が起きうる状態だったかを notes に書いて公開します。 報告してくださった方の名前を載せるかどうかは、そのときに伺います。 黙って直して黙って出すことはしません。