AWS CodeBuildを使ってビルドしているサイトのNodeパッケージマネージャーをnpmからpnpmに切り替えてみた
しばたです。
今年9月ごろにnpmを対象としサプライチェーン攻撃を行ったShai-Huludワームが再び猛威を振るっています。
サプライチェーン攻撃に対してNodeパッケージの利用者側で打てる対策の一つとして「リリースから一定期間経たパッケージだけ更新対象にする」というのがあります。
(大抵のサプライチェーン攻撃は早期に発見され対策される傾向にあるため)
Nodeの場合pnpmのminimumReleaseAgeパラメーターやyarnのnpmMinimumReleaseAgeパラメーターの設定が該当します。
npmにはまだ同等機能は無く機能の実装に対する議論が進行中という状況です。
私は過去に登壇のために個人のポートフォリオサイトを作成しており、このサイトの開発にnpmを使っていました。
今回セキュリティ対策の一環としてNodeパッケージマネージャーをnpmからpnpmに切り替えたのでその手順と結果を共有しようと思います。
対象サイトの構成
対象となるサイトは個人のポートフォリオサイト(https://www.shibata.tech/)になります。
こちらはコンテンツの作成にAstroを使いパッケージマネージャーにnpmを採用しています。
サイトはCloudFront + S3の構成を基本とし、ソースコードのビルドとS3へのデプロイにCodeBuildを使っています。

サイトのアーキテクチャ図
このためローカル環境の設定変更のほかにCodeBuild側の設定変更も必要となります。
また、ローカル環境はNushell on WSL2 (Ubuntu 24.04)というちょっと珍しい構成なのですが、プラットフォーム・シェル依存の部分は少なめですので適宜他の環境に読み替え可能だと思います。
切り替え手順
ここから各環境で実施した手順を紹介していきます。
1. ローカル環境の設定変更
最初にpnpmのインストールが必要です。
pnpmのインストール方法は複数ありますが、npmからの切り替えを主目的としているのでスタンドアロンスクリプトを使ったインストールで良いでしょう。
利用しているシェルに応じて適宜インストールスクリプトを実行してpnpmをインストールします。
# PowerShell環境の場合
Invoke-WebRequest https://get.pnpm.io/install.ps1 -UseBasicParsing | Invoke-Expression
# その他シェルの場合
curl -fsSL https://get.pnpm.io/install.sh | sh -
どちらのスクリプトもGitHubからpnpmのリリースバイナリをダウンロードしてpnpm setupコマンドを実行してインストール作業を行う形になっています。
パッケージマネージャーの切り替え
続けて既存の設定(npmの設定)からpnpm用のロックファイルpnpm-lock.yamlを作成して設定を移行します。
これはpackage.jsonのあるディレクトリ[1]に移動しpnpm importコマンドを実行すればOKです。
# 当該ディレクトリに移動して pnpm import コマンドを実行
cd <当該ディレクトリ>
pnpm import
私の環境での実行結果はこんな感じでした。
# pnpm importコマンドの実行結果
> pnpm import
WARN 1 deprecated subdependencies found: sitemap@8.0.0
Progress: resolved 561, reused 0, downloaded 0, added 0, done
依存関係ツリーのどこかに使われなくなった?ものがあった様ですが今回はこの警告を無視しても問題ありませんでした。
コマンドが完了するとpnpm-lock.yamlが作成されるので今後はこちらを使用します。
既存のpackage-lock.jsonはもう不要なので削除し、またnode_modulesの内容も刷新する必要があるためディレクトリごと削除してやります。
# 不要になった package-lock.json を削除
rm package-lock.json
# ついでに node_modules もいったん削除
rm --recursive node_modules/
なおpnpmでもpackage.jsonはそのまま使用します。
この状態でpnpm installコマンドを実行して各種パッケージを入れなおしてやります。
# 改めてパッケージをインストールし直す
pnpm install
これでパッケージマネージャーの切り替えは完了です。
あとはnpmコマンドを使用している箇所を適宜pnpmコマンドに変更してやれば環境全体の切り替えが完了します。
npmとpnpmコマンドの比較についてはpnpmのドキュメントで確認できます。
私の環境の場合以下の切り替えが必要でした。
npm i <特定のパッケージ>→pnpm add <特定のパッケージ>npm install→pnpm installnpm ci→pnpm install --frozen-lockfilenpm run dev→pnpm run devnpm run build→pnpm run build
minimumReleaseAge の設定
次に今回のメインとなるminimumReleaseAgeパラメーターを設定します。
pnpm config setコマンドを使うと各種パラメーターの値を更新できます。
設定のスコープは--locationパラメーターで設定可能でグローバル(global)かプロジェクト毎(project)のどちらかを選びます。
minimumReleaseAgeパラメーターに指定する値は"分"単位となり、例えば7日間としたい場合は10080を指定してやります。
# minimumReleaseAge の設定例 (10080分 = 7日)
pnpm config set --location=project minimumReleaseAge 10080
# 更新結果を確認
pnpm config list --location=project
指定すべき値に関しては「環境に依る」としか言えないのですが、インターネット上の情報を見る限りでは1日後(1440)から一週間後(10080)くらいの期間を選んでいる方が多い様です。私の環境も少し長めの猶予とし一週間後(10080)としました。
プロジェクト毎の設定を更新した場合pnpm-workspace.yamlファイルにその内容が記載されるので適宜ソース管理の対象に入れてやります。
(Optional) その他設定変更
必要に応じて次の対応もすると良いでしょう。
- (npmを使う必要が完全に無くなった場合) npm自体のアンインストール
minimumReleaseAgeパラメーターをグローバルに設定
2. AWS CodeBuildの設定変更
ローカル環境の切り替えが終わった後はAWS CodeBuild側の設定も切り替えてやる必要があります。
私の環境で使っていたbuildspec.ymlを一部抜粋すると次の通りです。
version: 0.2
env:
git-credential-helper: yes
variables:
# ・・・省略・・・
phases:
install:
runtime-versions:
nodejs: 22
commands:
# Install : 指定バージョンのNode.jsをインストール + npm ciを実行
- n 22.22.1
- npm ci
#
# ・・・中略・・・
#
build:
on-failure: ABORT
commands:
# Build : ふつうに npm run build
- |
echo "Start build..."
npm run build
echo "Build completed!"
# ・・・後略・・・
ざっくり
nコマンドで開発環境と同じバージョンのNode.jsをインストールnpm ciコマンドでパッケージをインストールnpm run buildコマンドでソースコードをビルド
という形でnpmを利用しています。
本日時点でCodeBuildから標準提供されるコンテナイメージにおいてnpmとyarnはデフォルトインストール済みであるもののpnpmはインストールされていません。
たとえば私の環境で使っているAmazon Linux 2023 AArch64 standard:3.0イメージの定義を確認すると次の初期設定をしています。
# Node.js関連の初期設定を一部抜粋 : yarnは初期インストールされるがpnpmは対象外
ENV NODE_22_VERSION="22.20.0" \
NODE_20_VERSION="20.19.5" \
NODE_18_VERSION="18.20.8"
RUN n --no-preserve $NODE_18_VERSION && npm install --save-dev -g -f grunt \
&& npm install --save-dev -g -f grunt-cli \
&& npm install --save-dev -g -f webpack \
&& npm install --save-dev -g -f yarn \
&& n $NODE_20_VERSION && npm install --save-dev -g -f grunt \
&& npm install --save-dev -g -f grunt-cli \
&& npm install --save-dev -g -f webpack \
&& npm install --save-dev -g -f yarn \
&& dnf install -y -v libuv-1.44* \
&& n $NODE_22_VERSION && npm install --save-dev -g -f grunt \
&& npm install --save-dev -g -f grunt-cli \
&& npm install --save-dev -g -f webpack \
&& npm install --save-dev -g -f yarn \
&& cd / && rm -rf $N_SRC_DIR \
&& rm -rf /tmp/* && rm -rf ~/.npm/_logs/
このためpnpmを使う場合はbuildspec.ymlに別途インストール処理を追加してやる必要があります。
追加の仕方はなんでも良いのですが、上記処理に倣ってnpmからpnpmをインストールする形にするのが手っ取り早いでしょう。
# npmからpnpmをインストールするコマンド例
npm install --save-dev -g -f pnpm
ここまでを踏まえて最終的にbuildspec.ymlを次の様にしました。
version: 0.2
env:
git-credential-helper: yes
variables:
# ・・・省略・・・
phases:
install:
runtime-versions:
nodejs: 22
commands:
# Install : 指定バージョンのNode.jsとpnpmをインストール
- n 22.21.1
- npm install --save-dev -g -f pnpm
# npm ci → pnpm install --frozen-lockfile に切り替え
- pnpm install --frozen-lockfile
#
# ・・・中略・・・
#
build:
on-failure: ABORT
commands:
# Build : npm run build → pnpm run build に切り替え
- |
echo "Start build..."
pnpm run build
echo "Build completed!"
# ・・・後略・・・
主な変更点は次の通りです。
- installフェーズに
npm install --save-dev -g -f pnpmを追加 npm ciをpnpm install --frozen-lockfileに変更npm run buildをpnpm run buildに変更
3. 結果確認
これでCodePipelineのパイプラインを実行してエラーなくデプロイまでできれば対応完了です。

切り替え後のパイプラインがエラーなく完了

CodeBuildのログも期待した結果になっている
ログからも期待した動作に切り替わっていることが見て取れます。
#
# INSTALLフェーズ
#
[Container] 2025/11/26 04:35:43.820415 Entering phase INSTALL
[Container] 2025/11/26 04:35:43.856082 Running command n 22.21.1
installing : node-v22.21.1
mkdir : /usr/local/n/versions/node/22.21.1
fetch : https://nodejs.org/dist/v22.21.1/node-v22.21.1-linux-arm64.tar.xz
copying : node/22.21.1
installed : v22.21.1 (with npm 10.9.4)
[Container] 2025/11/26 04:35:47.084571 Running command npm install --save-dev -g -f pnpm
npm warn using --force Recommended protections disabled.
added 1 package in 1s
1 package is looking for funding
run `npm fund` for details
[Container] 2025/11/26 04:35:48.212895 Running command pnpm install --frozen-lockfile
Lockfile is up to date, resolution step is skipped
Progress: resolved 1, reused 0, downloaded 0, added 0
Packages: +465
#
# ・・・・中略・・・
#
Done in 4.3s using pnpm v10.23.0
#
# BUILDフェーズ
#
[Container] 2025/11/26 04:35:52.715081 Entering phase BUILD
[Container] 2025/11/26 04:35:52.716345 Running command echo "Start build..."
pnpm run build
echo "Build completed!"
Start build...
#
# ・・・・中略・・・
#
04:36:02 [build] 17 page(s) built in 2.45s
04:36:02 [build] Complete!
Build completed!
最後に
以上となります。
シンプルなポートフォリオサイトが対象だったので切り替え作業も割と簡単に済みました。
本記事の内容がnpmからpnpmへの切り替え手順の一例として皆さんの参考になれば幸いです。
余談
私はShai Huludといえばバンドの方を真っ先に思い浮かべる人間なんですが、ウェブでの検索結果が今回の件に汚染されてしまったのが悲しいです。
大抵はソースコードのルートになると思います ↩︎






