自作Chrome拡張の一括セキュリティレビューとGit履歴からの機密データ除去
クラウド会計ソフトAと連携する自作のChrome拡張機能を、まるごとセキュリティレビューにかけた。脆弱性、行儀の悪いAPIの叩き方、バグ。気になっていたものを全部洗い出して直すつもりで頼んだら、コードの問題よりも先に背筋が冷える発見があった。顧客名やスプレッドシートIDの実値が、Git履歴の中に残っていた。
依頼の出し方
依頼はシンプルに「全コードをレビューして、問題点をMarkdownに書き出してから、全部修正をかけて」とした。レビューと修正を分けたのは、何を直したのか後から追えるようにしたかったから。
着手前にひとつ条件を足した。未コミットの変更が残っていたら、先にステージング&コミットして、クリーンな状態からやってもらうこと。これが効いた。レビュー対象の差分と修正の差分が混ざらず、後のセルフレビューで「どの修正がどの指摘に対応するか」を差分単位で追えた。
ちなみにコミットされた未コミット変更は sessionStorage → chrome.storage.local への移行だった。このコミットに対して自動セキュリティレビューが走り、「タブ単位・セッション単位だった状態がグローバル・永続に変わり、消費側がその寿命を安全装置として当てにしていた」という指摘を別途返してきた。自分では「保存先を変えただけ」のつもりだった変更に、状態の寿命という観点が刺さった。
レビュー結果: Critical 4 / High 10 / Medium 9
全コードを6並列でレビューしてもらい、検出されたのは以下。
- Critical: 4件
- High: 10件
- Medium: 9件
指摘内容は memo/2026-06-11/code-review-findings.md に書き出した上で、22件を修正してもらった。修正対象は import.js の自動クリックフラグ、CSRF・エラーハンドリング不備、bridge.js、content.js のXSS、storage.js、background.js の4ハンドラなど多岐にわたる。
修正後は構文チェックとテストを実行し、392件全パス。コミット前にリスクの高い変更(autoclick の書き換えと try/finally ラップ)だけは差分でセルフレビューさせてからコミットした。
発覚: defaults.json の顧問先実名がGit履歴に残っていた
レビューの残り1件として「defaults.json に顧問先の実名が入っている」という判断事項が上がってきた。defaults.json 自体は .gitignore 済みだったが、過去のコミットには残ったままだった。.gitignore は「これから追跡しない」だけで、すでに歴史に刻まれたものは消してくれない。
対応として、defaults.json を全Git履歴から除去してもらい、GitHubへ force push した。ローカルのファイルは無傷なので、拡張機能はそのまま動く。
Turso化は見送り
このとき「機密データはTurso(SQLiteベース)に入れてレプリカから読み出す方がいいのか」も検討した。Chrome拡張からそれが機能するのかも分からないまま投げてみたが、結論は「Turso化は不要」。gitignore済みのローカルJSONで運用は成り立っており、履歴からの除去さえ済めば、わざわざDB層を足す理由がない。提案して、理由を聞いて、やらないと決める。この往復が数分で終わるのはありがたい。
第二波: 実データ入りダンプが14ファイル
defaults.json で終わりかと思ったら、続きがあった。memo配下に page.html というスナップショットがあり、正体を確認してもらったところ、会計ソフトAの画面をまるごと保存した実データ入りのHTMLダンプだった。
調査を広げると、当初把握していた3件に加えて同類のスナップショットが11件見つかり、計14ファイルを作業ツリーと全Git履歴から除去して force push。さらにスクリーンショット27件も履歴ごと消した。memo のファイル名(作業記録としての名前)は温存し、実データを含む中身だけを消すという仕分けにした。
開発中に「とりあえず保存」したページダンプやスクリーンショットが、機密データの倉庫になっていた。コードの脆弱性より、こっちの方がよほど現実的なリスクだったと思う。
仕上げ: Chrome DevToolsでスモークテスト
履歴書き換えという荒療治の後なので、拡張機能が壊れていないかのスモークテストまでやってもらった。Chrome DevTools MCP経由で、ログイン済みの自分のChromeに接続し、実データを書き込まない範囲でコンソールエラーと拡張パネルの描画を確認。全項目グリーン。
並行して、全refで機密ファイルの残存ゼロも確認してもらった。
最終状態
- 全コードレビュー: Critical 4 / High 10 / Medium 9 を検出、22件修正
- テスト392件全パス
- defaults.json(顧問先実名)を全Git履歴から除去、force push
- 実データ入りダンプ14ファイルを履歴ごと除去
- スクリーンショット27件を履歴ごと除去
- Chrome DevTools MCPでスモークテスト、全項目グリーン
- Turso化 → 検討の結果、不要と判断して見送り
学び
- .gitignore は過去を消さない。機密ファイルをignoreに足して安心していたが、追加前のコミットには全部残っていた。「いつからignoreしたか」を一度も確認していなかった
- 開発中の「とりあえず保存」が一番危ない。ページダンプ・スクリーンショットといった作業の副産物に実データが詰まっていた。コードレビューを頼んだら、コード外から機密が出てきた
- レビューの前にクリーンコミット。未コミット変更を先に片付けてから始めると、指摘と修正が差分単位で対応づく
- 「やらない判断」も外部化できる。Turso化のような構成変更は、自分で調べると半日かかる。投げて、理由付きで「不要」と返ってきたら、それで閉じられる
作業記録は memo/2026-06-11/code-review-findings.md に残してある。