feat: pnpm バージョン固定と supply chain attack 対策 - #530
Conversation
## 変更内容 ### pnpm バージョン固定 (Dockerfile) - `npm install -g pnpm` → `npm install -g pnpm@10.30.3` に固定 - バージョン未固定のままだと最新版が自動インストールされ、 悪意のある新規リリースを意図せず取り込むリスクがある ### .npmrc 追加(pnpm セキュリティ設定) - `strict-peer-dependencies=true`: peer dep コンフリクトで即失敗 - `shamefully-hoist=false`: phantom dependencies の排除 - `audit=true / audit-level=moderate`: インストール時に脆弱性チェック - `verify-store-integrity=true`: コンテンツハッシュで改ざん検知 ### Renovate 導入(直近 2 日のリリースをスキップ) - `renovate.json` を追加し、npm 依存を Renovate で管理 - `minimumReleaseAge: "2 days"` により公開後 2 日未満のパッケージを 依存更新対象から除外(supply chain attack の主要な攻撃窓を閉じる) - Dependabot から npm セクションを削除(重複防止) - Docker / GitHub Actions は引き続き Dependabot が担当 > Note: Renovate を有効にするには GitHub App のインストールが必要。 > https://github.com/apps/renovate Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughPins pnpm to a specific version and introduces configuration files to enforce stricter package management policies, including peer dependency resolution, security audits, phantom dependency prevention, and minimum release age constraints for package publishing. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR レビュー: supply chain attack 対策全体的に良いアプローチです。セキュリティ改善の方向性は正しく、特に 🔴 要修正1.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1bb23d5603
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,16 @@ | |||
| # pnpm security settings | |||
| # Strict peer dependency resolution - fail on conflicts | |||
| strict-peer-dependencies=true | |||
There was a problem hiding this comment.
Move pnpm-only keys out of shared
.npmrc
The new project-level .npmrc adds pnpm-specific options (for example strict-peer-dependencies, auto-install-peers, shamefully-hoist, and verify-store-integrity) that npm does not support, so every npm invocation now emits Unknown project config ... warnings; this affects this repo immediately because CI/workflows run npm commands, and npm already warns these unknown keys may stop working in a future major release. Keeping pnpm-only settings in the shared config introduces noisy logs now and a forward-compatibility risk for the npm-based pipeline.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
renovate.json (1)
7-16: Consider explicitly settingautomergefor the patch/minor rule.The
config:recommendedpreset may enable automerge for certain update types. If the intent is to require review for all npm updates (even patch/minor), explicitly set"automerge": false. If automerge is desired for patch/minor, this is fine as-is but worth documenting.💡 Optional: Explicit automerge setting
{ "description": "npm: 直近2日のリリースはスキップ(supply chain attack 対策)", "matchManagers": ["npm"], "minimumReleaseAge": "2 days", "labels": ["dependencies", "npm"], "commitMessagePrefix": "deps:", "groupName": "npm patch/minor", "groupSlug": "npm-deps", - "matchUpdateTypes": ["patch", "minor"] + "matchUpdateTypes": ["patch", "minor"], + "automerge": false },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@renovate.json` around lines 7 - 16, The renovate rule block for npm updates (matchManagers: ["npm"], groupName: "npm-deps") doesn't explicitly declare an automerge policy; update that object to include an explicit "automerge" field set to true or false depending on desired behavior (e.g., "automerge": false to require reviews for patch/minor updates), so that the policy is not implicitly inherited from config:recommended and the intent is clear and documented.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@renovate.json`:
- Around line 26-31: The rule block that matches "semantic-release" packages
currently has "enabled: false" which stops Renovate from creating any PRs;
change that to keep the rule enabled and instead set "automerge": false (or
remove "enabled") so Renovate will create PRs for major updates but will not
automerge them; update the JSON object that contains "matchPackageNames":
["semantic-release", "@semantic-release/*"] and "matchUpdateTypes": ["major"] to
use "automerge": false (and remove or set "enabled": true) to reflect the
intended manual-review behavior.
---
Nitpick comments:
In `@renovate.json`:
- Around line 7-16: The renovate rule block for npm updates (matchManagers:
["npm"], groupName: "npm-deps") doesn't explicitly declare an automerge policy;
update that object to include an explicit "automerge" field set to true or false
depending on desired behavior (e.g., "automerge": false to require reviews for
patch/minor updates), so that the policy is not implicitly inherited from
config:recommended and the intent is clear and documented.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 74d0a3ce-8013-42ed-990a-e6968aff6b3f
📒 Files selected for processing (4)
.devcontainer/Dockerfile.github/dependabot.yml.npmrcrenovate.json
| { | ||
| "description": "semantic-release: major は手動レビュー", | ||
| "matchPackageNames": ["semantic-release", "@semantic-release/*"], | ||
| "matchUpdateTypes": ["major"], | ||
| "enabled": false | ||
| } |
There was a problem hiding this comment.
enabled: false disables updates entirely, not just automerge.
The description says "semantic-release: major は手動レビュー" (manual review required), but enabled: false prevents Renovate from creating PRs for semantic-release major updates at all. If the intent is to require manual review (but still receive PRs), use automerge: false instead.
🐛 Proposed fix to allow PRs with manual review
{
"description": "semantic-release: major は手動レビュー",
"matchPackageNames": ["semantic-release", "@semantic-release/*"],
"matchUpdateTypes": ["major"],
- "enabled": false
+ "automerge": false
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| "description": "semantic-release: major は手動レビュー", | |
| "matchPackageNames": ["semantic-release", "@semantic-release/*"], | |
| "matchUpdateTypes": ["major"], | |
| "enabled": false | |
| } | |
| { | |
| "description": "semantic-release: major は手動レビュー", | |
| "matchPackageNames": ["semantic-release", "@semantic-release/*"], | |
| "matchUpdateTypes": ["major"], | |
| "automerge": false | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@renovate.json` around lines 26 - 31, The rule block that matches
"semantic-release" packages currently has "enabled: false" which stops Renovate
from creating any PRs; change that to keep the rule enabled and instead set
"automerge": false (or remove "enabled") so Renovate will create PRs for major
updates but will not automerge them; update the JSON object that contains
"matchPackageNames": ["semantic-release", "@semantic-release/*"] and
"matchUpdateTypes": ["major"] to use "automerge": false (and remove or set
"enabled": true) to reflect the intended manual-review behavior.
pnpm v10.16.0+ のネイティブ機能で supply chain attack 対策を実装するため、 Renovate 設定を削除し pnpm-workspace.yaml に移行。 ## 変更内容 - `pnpm-workspace.yaml` を追加 - `minimumReleaseAge: 2880`(2日間 = 2880分) - パッケージ追加・更新時に公開から2日未満のバージョンはインストールを拒否 - GitHub App 不要・外部サービス依存なし - `renovate.json` を削除(pnpm native で代替可能なため不要) - `dependabot.yml` の npm セクションを復元 ## minimumReleaseAge の動作 - `pnpm add` や `pnpm update` で依存グラフを解決する際に適用 - pnpm-lock.yaml が存在する場合はスキップ(環境再現性を優先) - pnpm v10.16.0+ が必須(現在 v10.30.3 を使用) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
コードレビューsupply chain attack 対策という観点で方向性は良いと思います。以下にフィードバックをまとめます。 問題点1. PR 説明と実際の差分の不一致PR 説明には
Renovate 設定が含まれていない場合、PR 説明に記載された Renovate 側の 2.
|
| 優先度 | 項目 |
|---|---|
| 高 | PR 説明と diff の不一致(renovate.json 未追加)の確認・修正 |
| 中 | strict-peer-dependencies + auto-install-peers の競合解消 |
| 低 | pnpm-workspace.yaml の packages フィールド検討 |
Reviewed by Claude Sonnet 4.6
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pnpm-workspace.yaml`:
- Around line 5-7: The CI is using npm so pnpm workspace controls like
minimumReleaseAge in pnpm-workspace.yaml are ignored; update the repo to enforce
pnpm by adding packageManager: "pnpm@10.30.3" (or later) to root package.json,
change all CI workflow steps that run npm ci to run pnpm install
--frozen-lockfile, update any setup-node cache entries that use 'npm' to use
'pnpm', and pin DevContainer/Codespaces pnpmVersion from "latest" to the same
specific version; finally run pnpm install locally to generate and commit
pnpm-lock.yaml so the pnpm gate (minimumReleaseAge) and pnpm security settings
in .npmrc take effect.
| # pnpm v10.16.0+ でサポート | ||
| # 単位: 分(2880 = 2 days) | ||
| minimumReleaseAge: 2880 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# First, understand the repository structure
echo "=== Repository root files ==="
ls -la | head -20
echo
echo "=== Root package.json check ==="
if [ -f package.json ]; then
cat package.json | head -30
else
echo "No root package.json found"
fi
echo
echo "=== pnpm-workspace.yaml content ==="
if [ -f pnpm-workspace.yaml ]; then
cat pnpm-workspace.yaml
else
echo "No pnpm-workspace.yaml found"
fiRepository: keito4/config
Length of output: 2727
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== packageManager field in root package.json ==="
if [ -f package.json ]; then
jq -r '.packageManager // "NOT SET"' package.json
else
echo "Root package.json not found"
fi
echo
echo "=== CI/workflow files referencing pnpm ==="
fd -HI '\.(ya?ml|json)$' -path '*/.github/*' -o -path '*/\.gitlab-ci\.yml' | while read f; do
if rg -l 'pnpm|corepack|packageManager' "$f" 2>/dev/null; then
echo "Found in: $f"
fi
done
echo
echo "=== .npmrc or .pnpmrc files ==="
fd -HI '^\.(npmrc|pnpmrc)$'
echo
echo "=== All package.json files with packageManager field ==="
fd -HI '^package\.json$' -x sh -c '
pkg_mgr=$(jq -r ".packageManager // \"<missing>\"" "{}" 2>/dev/null || echo "error")
if [ "$pkg_mgr" != "<missing>" ] && [ "$pkg_mgr" != "error" ]; then
echo "{}": $pkg_mgr
fi
'Repository: keito4/config
Length of output: 344
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== CI workflow files ==="
find .github/workflows -type f -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -10
echo
echo "=== Content of CI workflows (checking for pnpm setup) ==="
find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -exec sh -c '
echo "--- File: {} ---"
grep -n -i "pnpm\|corepack\|packageManager\|setup-node" "{}" || echo "No pnpm/corepack references found"
' \;
echo
echo "=== .npmrc and .pnpmrc files ==="
find . -maxdepth 3 -type f \( -name '.npmrc' -o -name '.pnpmrc' \) 2>/dev/null || echo "None found"
echo
echo "=== Checking devcontainer configuration ==="
if [ -d .devcontainer ]; then
find .devcontainer -type f \( -name '*.json' -o -name 'Dockerfile' -o -name 'devcontainer.json' \) -exec sh -c '
echo "--- File: {} ---"
head -50 "{}"
' \;
fiRepository: keito4/config
Length of output: 14236
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== All package.json files in the repo ==="
find . -name package.json -type f | head -20
echo
echo "=== Check which ones have packageManager field ==="
find . -name package.json -type f -exec sh -c '
result=$(jq -r ".packageManager // \"<missing>\"" "{}" 2>/dev/null || echo "parse_error")
if [ "$result" != "<missing>" ] && [ "$result" != "parse_error" ]; then
echo "{}: $result"
fi
' \;Repository: keito4/config
Length of output: 166
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Content of .npmrc ==="
cat -n .npmrc
echo
echo "=== Check for setup-node configuration in CI workflows ==="
grep -A5 "setup-node" .github/workflows/ci.yml | head -30
echo
echo "=== DevContainer node feature configuration ==="
echo "Local DevContainer:"
jq '.features | keys[] as $key | select($key | contains("node")) | {($key): .[$key]}' .devcontainer/devcontainer.json
echo
echo "Codespaces DevContainer:"
jq '.features | keys[] as $key | select($key | contains("node")) | {($key): .[$key]}' .devcontainer/codespaces/devcontainer.jsonRepository: keito4/config
Length of output: 1351
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check for .node-version file ==="
if [ -f .node-version ]; then
cat -n .node-version
else
echo "No .node-version file found"
fi
echo
echo "=== Check pnpm-lock.yaml to understand pnpm version used ==="
if [ -f pnpm-lock.yaml ]; then
head -5 pnpm-lock.yaml | grep -i "lockfile\|pnpm"
echo "..."
else
echo "No pnpm-lock.yaml found"
fi
echo
echo "=== Check for pnpm version in CI install steps ==="
grep -B5 -A5 "pnpm install\|npm install" .github/workflows/ci.yml | head -40
echo
echo "=== Check Dockerfile for pnpm installation ==="
grep -n -i "pnpm" .devcontainer/DockerfileRepository: keito4/config
Length of output: 246
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for pnpm installation in entire repo ==="
grep -r "pnpm" .devcontainer/Dockerfile .github/workflows/ --include="*.yml" --include="*.yaml" --include="*.json" | grep -v "pnpmVersion\|pnpm-workspace" | head -20
echo
echo "=== Check if npm scripts reference pnpm ==="
jq '.scripts' package.json
echo
echo "=== Verify current node setup in ci.yml - full install section ==="
sed -n '/Install dependencies/,/^ - /p' .github/workflows/ci.yml | head -20
echo
echo "=== Check root .npmrc comments for pnpm reference ==="
head -20 .npmrcRepository: keito4/config
Length of output: 2018
Switch CI from npm ci to pnpm install and pin pnpm version globally.
The minimumReleaseAge control in pnpm-workspace.yaml (line 7) is currently non-functional because CI workflows use npm ci instead of pnpm install. The .npmrc file has pnpm-specific security settings, but npm ignores both the workspace configuration and supply chain controls.
To enforce the 2-day release age gate:
- Add
packageManager: "pnpm@10.30.3"(or later) to root package.json - Change all CI workflows from
npm citopnpm install --frozen-lockfile - Update setup-node cache from
'npm'to'pnpm' - Pin DevContainer Codespaces
pnpmVersionfrom"latest"to a specific version
Additionally, generate a pnpm-lock.yaml file by running pnpm install locally and committing it.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pnpm-workspace.yaml` around lines 5 - 7, The CI is using npm so pnpm
workspace controls like minimumReleaseAge in pnpm-workspace.yaml are ignored;
update the repo to enforce pnpm by adding packageManager: "pnpm@10.30.3" (or
later) to root package.json, change all CI workflow steps that run npm ci to run
pnpm install --frozen-lockfile, update any setup-node cache entries that use
'npm' to use 'pnpm', and pin DevContainer/Codespaces pnpmVersion from "latest"
to the same specific version; finally run pnpm install locally to generate and
commit pnpm-lock.yaml so the pnpm gate (minimumReleaseAge) and pnpm security
settings in .npmrc take effect.
supply chain attack 対策として pnpm v10.16.0+ の minimumReleaseAge 機能と セキュリティ設定を各リポジトリタイプのセットアップガイドに追加する。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude Code レビューpnpm バージョン固定と supply chain attack 対策の実装を確認しました。全体的な方向性は適切ですが、いくつか指摘事項があります。 ✅ 良い点
|
| 項目 | 評価 |
|---|---|
| pnpm バージョン固定 | ✅ 適切 |
minimumReleaseAge 設定 |
|
.npmrc 設定 |
|
| ドキュメント整備 | ✅ 適切 |
マージ前に P1 の2点の対応を推奨します。 特に .npmrc の pnpm 専用設定の分離は、npm を使う環境での副作用を避けるために重要です。
🤖 Reviewed with Claude Code
概要
pnpm のバージョン固定と supply chain attack 対策を実装する。
変更内容
セキュリティ対策
.devcontainer/Dockerfile:pnpm@10.30.3にバージョンを固定(未固定では脆弱性リスク)pnpm-workspace.yaml(新規): pnpm v10.16.0+ ネイティブ機能minimumReleaseAge: 2880を設定.npmrc(新規): pnpm セキュリティ設定(strict-peer-dependencies, verify-store-integrity 等).github/dependabot.yml: npm セクション維持(Renovate なしでも継続運用)ドキュメント更新
各リポジトリタイプのセットアップガイドに pnpm セキュリティ設定セクションを追加:
docs/setup/web-app-nextjs.mddocs/setup/spa-react-vite.mddocs/setup/npm-library-cli.md参考
pnpm-workspace.yamlに設定可能テスト
🤖 Generated with Claude Code