• #security
  • #vulnerability
  • #pnpm-overrides
  • #path-traversal
  • #nuxt
  • #dependency-update
開発mdx-playgroundメモ

Nuxtプロジェクトのセキュリティレビューと依存関係の脆弱性を全件修正した

pnpm audit を叩いたら、CriticalとHighが並んで画面が赤く染まった。放置していた依存関係の脆弱性を一掃する日が来た。


やったこと

3視点の並列レビュー

claude-code-syncで3つの視点から同時にレビューを走らせた。

  • セキュリティ: 秘密情報の漏洩、インジェクション、依存関係の脆弱性
  • パフォーマンス: ビルド時間、バンドルサイズ、ランタイム効率
  • SRE: 可用性、監視、障害復旧

セキュリティレビューでCriticalとHighが複数見つかり、そこから修正作業に入った。


セキュリティ修正の詳細

.env漏洩 → 誤検知

レビューが .env ファイルの存在を指摘してきたが、.gitignore に含まれており、リポジトリには入っていない。誤検知として対処不要と判断。

content-images.ts にパストラバーサル防御を追加

画像配信ミドルウェアに ../ を使ったディレクトリ外アクセスの防御がなかった。normalize + resolve でパスを正規化した上で、contentDir の配下であることを検証するガードを追加した。

const contentPath = normalize(resolve(contentDir, dirPath, filename))
if (!contentPath.startsWith(contentDir)) return

この2行で content/ ディレクトリ外への読み出しを遮断する。

依存関係の脆弱性修正

直接依存とトランジティブ依存で対処方法が分かれた。

直接依存(pnpm update で解決):

パッケージ脆弱性対処
devalueプロトタイプ汚染最新版に直接更新
h3ReDoS最新版に直接更新

トランジティブ依存(pnpm overrides で対処):

直接依存していないパッケージは pnpm update では更新できない。package.jsonpnpm.overrides フィールドで強制的にバージョンを指定した。

{
  "pnpm": {
    "overrides": {
      "fast-xml-parser": ">=4.5.1",
      "rollup": ">=4.30.1",
      "@isaacs/brace-expansion": ">=3.0.1",
      "tar": ">=6.2.1"
    }
  }
}

overridesはロックファイル全体に作用するので、どのパッケージが間接的に依存していても一律で安全なバージョンに引き上げられる。

修正結果

深刻度修正前修正後
Criticalあり0件
Highあり0件
Moderate-3件(残存)
Low-4件(残存)

残った7件はdevDependency経由か、パッチが未リリースのもの。本番には影響しないため許容とした。


テスト結果

修正後、全テストを実行して既存機能への影響がないことを確認した。

  • 34ファイル
  • 14,817テスト 全パス

overridesで依存バージョンを強制的に変えているので、テストが通ることの確認は省略できない。実際に全件通ったので、互換性の問題はなかった。


学んだこと

  • pnpm overrides はトランジティブ依存の脆弱性修正に使える。 npm の overrides や yarn の resolutions に相当する。ロックファイル再生成で即座に反映される
  • パストラバーサルの防御は resolvestartsWith の2ステップ。 normalize だけでは不十分で、resolve で絶対パスに変換してから基準ディレクトリとの前方一致を取る
  • セキュリティレビューの誤検知は記録に残す。 「.envは.gitignoreに含まれている」と書き残すだけで、次回の同じ指摘をスキップできる

振り返り

3視点の並列レビューは、1人で順番に見ていくより抜け漏れが減る。特にセキュリティは「見落としたら終わり」の領域なので、別の目が入る価値がある。

CriticalとHighが全て消えて pnpm audit の出力が静かになったとき、ようやく肩の力が抜けた。残りのModerateとLowはパッチが出たら片付ける。