Skip to content

feat: pnpm バージョン固定と supply chain attack 対策 - #530

Merged
keito4 merged 3 commits into
mainfrom
feat/pnpm-pin-and-renovate-security
Mar 5, 2026
Merged

keito4 merged 3 commits into
mainfrom
feat/pnpm-pin-and-renovate-security

Conversation

@keito4

@keito4 keito4 commented Mar 5, 2026

Copy link
Copy Markdown
Owner

概要

pnpm のバージョン固定と supply chain attack 対策を実装する。

変更内容

セキュリティ対策

  • .devcontainer/Dockerfile: pnpm@10.30.3 にバージョンを固定(未固定では脆弱性リスク)
  • pnpm-workspace.yaml (新規): pnpm v10.16.0+ ネイティブ機能 minimumReleaseAge: 2880 を設定
    • 公開から 2 日未満のパッケージをインストール禁止(2880 分 = 2 日)
    • lockfile がある場合は再現性を維持するため自動スキップ
  • .npmrc (新規): pnpm セキュリティ設定(strict-peer-dependencies, verify-store-integrity 等)
  • .github/dependabot.yml: npm セクション維持(Renovate なしでも継続運用)

ドキュメント更新

各リポジトリタイプのセットアップガイドに pnpm セキュリティ設定セクションを追加:

  • docs/setup/web-app-nextjs.md
  • docs/setup/spa-react-vite.md
  • docs/setup/npm-library-cli.md

参考

テスト

  • ✅ pre-commit フック: Format, Lint, Test 通過
  • ✅ 全テスト 101 件パス

🤖 Generated with Claude Code

## 変更内容

### 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>
@coderabbitai

coderabbitai Bot commented Mar 5, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Pins 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

Cohort / File(s) Summary
Package Manager Version Pinning
.devcontainer/Dockerfile
Pins globally installed pnpm from latest to version 10.30.3, constraining package manager behavior during container setup.
pnpm Configuration
.npmrc, pnpm-workspace.yaml
Introduces new configuration files: .npmrc enforces strict peer dependency resolution, auto-installs missing peers, enables security audits, disables phantom dependency hoisting, and verifies store integrity; pnpm-workspace.yaml sets minimum 2-day release age constraint for package publishing (pnpm v10.16.0+).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

released, size/S

Poem

Hops through the package garden with glee, 🐰
pnpm@10.30.3 sets me free,
Strict peers and audits keep all so tight,
Release age gates guard what's right,
A burrow of config, configured with might!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main changes: pnpm version pinning and supply chain attack mitigation measures implemented across Dockerfile, .npmrc, and pnpm-workspace.yaml.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/pnpm-pin-and-renovate-security

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

PR レビュー: supply chain attack 対策

全体的に良いアプローチです。セキュリティ改善の方向性は正しく、特に minimumReleaseAge の導入は効果的な対策です。いくつか気になった点を共有します。


🔴 要修正

1. .npmrc: strict-peer-dependenciesauto-install-peers の矛盾

strict-peer-dependencies=true   # peer dep の競合でビルドを失敗させる
auto-install-peers=true         # 不足している peer dep を自動インストール

この2つは矛盾しています。auto-install-peers は pnpm v8+ ではデフォルト有効になっており、strict-peer-dependencies との組み合わせで予期しない動作(競合時にエラーになる前に自動解決してしまう)が起きる可能性があります。

提案: auto-install-peers を削除するか、明示的に false に設定してください。


🟡 要検討

2. Dockerfile の pnpm バージョンを Renovate が更新できない

RUN npm install -g pnpm@10.30.3

バージョンを固定したこと自体は良いですが、renovate.jsonpackageRulesmatchManagers: ["npm"] のみです。Dockerfile 内の pnpm バージョンは docker または regex マネージャーでないと Renovate が検出できません。

固定はしたが更新の自動化がない → 将来的に古いバージョンが放置されるリスクがあります。

提案:

{
  "description": "Dockerfile の pnpm バージョンを自動更新",
  "matchManagers": ["dockerfile"],
  "matchPackageNames": ["pnpm"],
  "minimumReleaseAge": "2 days"
}

もしくは regexManagers で Dockerfile の pnpm@x.y.z を検出するルールを追加してください。

3. .npmrc がリポジトリ全体に影響する

.npmrc をルートに置くと、pnpm だけでなく npm を使う CI ステップにも適用されます。pnpm 固有の設定(shamefully-hoistverify-store-integrity など)は npm では無視されますが、audit=true は npm の npm install にも適用されるため CI が遅くなる可能性があります。

PR 説明の「警告は出るがエラーではない」という点は確認済みとのことで問題ないとは思いますが、意図的な設計であることをコメントで明示しておくと良いです。

4. Renovate の patch/minor の automerge 設定が未定義

{
  "groupName": "npm patch/minor",
  ...
  // automerge の指定なし → Renovate のデフォルト(false)が適用される
}

major には automerge: false が明示されていますが、patch/minor には記載がありません。意図的に手動マージにするなら automerge: false を明示した方がレビュアーに伝わりやすいです。


🟢 良い点

  • minimumReleaseAge: "2 days" の適用は supply chain attack 対策として有効
  • Dependabot と Renovate の重複排除は適切
  • semantic-release major の無効化(enabled: false)はポリシーと一致
  • スケジュール(月曜 3am UTC)を Dependabot と合わせた点は一貫性があって良い
  • PR 説明に Renovate App のインストール手順を記載している点は親切

まとめ

auto-install-peers の矛盾解消と、Dockerfile の pnpm バージョン自動更新の仕組みを追加できれば、マージ可能な品質だと思います。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread .npmrc
@@ -0,0 +1,16 @@
# pnpm security settings
# Strict peer dependency resolution - fail on conflicts
strict-peer-dependencies=true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
renovate.json (1)

7-16: Consider explicitly setting automerge for the patch/minor rule.

The config:recommended preset 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83f081c and 1bb23d5.

📒 Files selected for processing (4)
  • .devcontainer/Dockerfile
  • .github/dependabot.yml
  • .npmrc
  • renovate.json

Comment thread renovate.json Outdated
Comment on lines +26 to +31
{
"description": "semantic-release: major は手動レビュー",
"matchPackageNames": ["semantic-release", "@semantic-release/*"],
"matchUpdateTypes": ["major"],
"enabled": false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
{
"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>
@claude

claude Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

コードレビュー

supply chain attack 対策という観点で方向性は良いと思います。以下にフィードバックをまとめます。

問題点

1. PR 説明と実際の差分の不一致

PR 説明には renovate.json 導入と dependabot.yml からの npm 移管が記述されていますが、実際の diff にはこれらのファイルが含まれていません。

  • PR 説明: renovate.json(新規)・dependabot.yml(npm セクション削除)
  • 実際の変更: .devcontainer/Dockerfile.npmrcpnpm-workspace.yaml の 3 ファイルのみ

Renovate 設定が含まれていない場合、PR 説明に記載された Renovate 側の minimumReleaseAge: "2 days" の効果が得られません。PR 説明の更新、または不足ファイルの追加が必要です。

2. .npmrcstrict-peer-dependenciesauto-install-peers の競合

strict-peer-dependencies=true
auto-install-peers=true

この 2 つは意図が競合しています。auto-install-peers は不足する peer dep を自動で補いますが、strict-peer-dependencies は競合を即 fail にします。自動インストールした peer dep が別の peer dep と競合した場合、挙動が不安定になる可能性があります。目的に応じていずれか一方を採用することを推奨します。

改善提案

3. pnpm-workspace.yamlpackages フィールドの検討

pnpm-workspace.yaml は本来 monorepo のワークスペースパッケージを定義するファイルです。このリポジトリが pnpm ワークスペースとして使われる場合、packages フィールドの明示を検討してください。

packages:
  - '.'
minimumReleaseAge: 2880
minimumReleaseAgeExclude: []

4. 冗長な pnpm デフォルト設定

以下は pnpm のデフォルト値のため明示不要です(意図を伝えるドキュメント目的なら許容範囲):

  • shamefully-hoist=false(pnpm デフォルト)
  • verify-store-integrity=true(pnpm デフォルト)

良い点

  • pnpm@10.30.3 のバージョン固定は supply chain attack 対策として適切
  • audit=true / audit-level=moderate によるインストール時の脆弱性チェックは有効
  • pnpm-workspace.yamlminimumReleaseAge: 2880(2日)は公開直後パッケージの除外として合理的
  • コメントが丁寧で意図が明確

まとめ

優先度 項目
PR 説明と diff の不一致(renovate.json 未追加)の確認・修正
strict-peer-dependencies + auto-install-peers の競合解消
pnpm-workspace.yamlpackages フィールド検討

Reviewed by Claude Sonnet 4.6

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d4031659-d491-48c5-877b-ef103aca0996

📥 Commits

Reviewing files that changed from the base of the PR and between 1bb23d5 and 45698ed.

📒 Files selected for processing (1)
  • pnpm-workspace.yaml

Comment thread pnpm-workspace.yaml
Comment on lines +5 to +7
# pnpm v10.16.0+ でサポート
# 単位: 分(2880 = 2 days)
minimumReleaseAge: 2880

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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"
fi

Repository: 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 "{}"
  ' \;
fi

Repository: 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.json

Repository: 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/Dockerfile

Repository: 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 .npmrc

Repository: 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:

  1. Add packageManager: "pnpm@10.30.3" (or later) to root package.json
  2. Change all CI workflows from npm ci to pnpm install --frozen-lockfile
  3. Update setup-node cache from 'npm' to 'pnpm'
  4. Pin DevContainer Codespaces pnpmVersion from "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>
@keito4 keito4 changed the title feat: pnpm バージョン固定と Renovate による supply chain attack 対策 feat: pnpm バージョン固定と supply chain attack 対策 Mar 5, 2026
@claude

claude Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

Claude Code レビュー

pnpm バージョン固定と supply chain attack 対策の実装を確認しました。全体的な方向性は適切ですが、いくつか指摘事項があります。


✅ 良い点

  • pnpm バージョン固定: pnpm@10.30.3 への固定は再現性と予測可能性の向上に有効
  • minimumReleaseAge: 2880: 公開直後パッケージのインストール阻止は supply chain attack(XZ Utils 等)への効果的な対策
  • ドキュメント整備: 3つのセットアップガイドに一貫したセキュリティ設定を追記しており、横展開しやすい

⚠️ 要対応

1. .npmrc に pnpm 専用オプションが混在している(P1)

他のレビュアーも指摘していますが、以下は pnpm 専用 オプションで npm では認識されません:

strict-peer-dependencies   # pnpm 専用
auto-install-peers         # pnpm 専用
shamefully-hoist           # pnpm 専用
verify-store-integrity     # pnpm 専用

npm v8+ はこれらに対して Unknown project config 警告を出力します。CI が npm ci を使っている場合、ログが汚染されるだけでなく、将来の npm major バージョンでエラーになるリスクがあります。

推奨対応: pnpm 専用設定は .npmrc ではなく、pnpm の設定ファイル(.pnpmfile.cjs または pnpm-workspace.yaml 内)に移動するか、ドキュメントのガイドのみに留める。


2. CI が npm ci を使用しているため minimumReleaseAge が無効(P1)

CodeRabbit も指摘していますが、このリポジトリの CI は npm ci を使っているため、pnpm-workspace.yamlminimumReleaseAge現状では機能しません

このリポジトリ自体が pnpm を使っていない場合、pnpm-workspace.yaml を配置してもセキュリティ効果はなく、誤った安心感を与えかねません。

推奨対応:

  • このリポジトリを pnpm に移行する、または
  • pnpm-workspace.yaml はテンプレートファイルとしてドキュメントに示すのみにして、ルートには配置しない

💡 改善提案(任意)

3. pnpm バージョンが複数箇所でバラバラになるリスク

Dockerfile では pnpm@10.30.3 に固定されていますが、Codespaces の devcontainer (pnpmVersion: "latest") とバージョンが一致していません。

// .devcontainer/codespaces/devcontainer.json
"ghcr.io/devcontainers/features/node:1": {
  "pnpmVersion": "latest"  // ← バージョン不一致のリスク
}

package.jsonpackageManager フィールドを活用して一元管理することを推奨します:

{
  "packageManager": "pnpm@10.30.3"
}

4. audit=true は pnpm ではデフォルト有効

.npmrcaudit=true は pnpm ではデフォルトで有効になっているため冗長ですが、明示的に記載することで意図が伝わるため許容範囲です。


まとめ

項目 評価
pnpm バージョン固定 ✅ 適切
minimumReleaseAge 設定 ⚠️ このリポジトリでは未機能
.npmrc 設定 ⚠️ npm/pnpm 混在で警告発生
ドキュメント整備 ✅ 適切

マージ前に P1 の2点の対応を推奨します。 特に .npmrc の pnpm 専用設定の分離は、npm を使う環境での副作用を避けるために重要です。

🤖 Reviewed with Claude Code

@keito4
keito4 merged commit 6d2e6a4 into main Mar 5, 2026
7 checks passed
@keito4
keito4 deleted the feat/pnpm-pin-and-renovate-security branch March 20, 2026 05:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant