🔎

意外と守ってくれる?Shai-Hulud2.0をきっかけにBunのセキュリティ機能を整理してみた

に公開

はじめに

つい先日話題になったShai-Hulud 2.0。
それに関連して投稿されたこちらの記事。
大変参考にさせていただいて、自分のリポジトリも念のため確認しました🙏

https://zenn.dev/hand_dot/articles/04542a91bc432e

この記事を読むまで「パッケージマネージャー自体にセキュリティ機構がある」ということを意識しておらず、実は便利な機能を十分に活用できていないのでは…?とハッとしました🤔

そこで、今回は最近使っているBunに焦点を当てて「Bunでできる多層防御」について調べてみました!
「公開直後のバージョンを避ける」「ライフサイクルスクリプトの実行を抑える」「インストール時にスキャンする」の3点を軸にまとめています。

調べればすぐにわかるような短い内容ではありますが、参考になれば幸いです!

Bunで整理する多層防御

早速結論ですが、今回の調査した内容をもとに整理すると...

  • 層1: minimum-release-age — 改ざんパッケージ拡散の抑制(サプライチェーン攻撃対策)
    • 公開直後の悪性・不安定なバージョンを同等機能で回避
    • 安定性チェックで連続リリース時の不安定な変更も回避
  • 層2: ライフサイクルスクリプト制御 - 最終防衛線
    • デフォルト仕様で抑止
    • 環境変数でさらに厳しく無効化も可能
    • 信頼できるもののみ有効化
  • 層3: SCAツール - 既知の脆弱性を検知する
    • Security Scanner API を使用
    • ローカル、CIともにインストールフェーズでスキャンを実行
    • 既知の脆弱性や(Socket使用の場合は)アクティブなサプライチェーン攻撃をチェック

Bunでは現在の機能 + SCA用の追加パッケージで割とシンプルに多層防御を設定できそうなことがわかりました!

以降で各層の詳細を見ていきます。

層1: minimum-release-age

元記事でも紹介されているように指定期間経過していないバージョンのインストールを拒否する機能です🛡️

Bunではどうなっている?

実はBunにもこの機能が2025年10月から追加されていました。

https://github.com/oven-sh/bun/issues/22679

https://bun.com/blog/bun-v1.3#minimum-release-age

設定はpnpmと同じような設定値になっています。

# bunfig.toml
[install]
# 3日で設定する場合
minimumReleaseAge = 259200 # seconds

# 除外設定
# パターン指定のサポートはなし
# PRは存在:https://github.com/oven-sh/bun/pull/23699
minimumReleaseAgeExcludes = ["@types/bun", "typescript"]

安定性チェック

pnpmとの動作の差分として安定バージョンの選択の機能が存在します。
Bunではage制限でインストールする候補をふるいにかけたうえで、その直後に短時間・連続リリースがある場合にはさらに古い安定板を選ぶという『安定性チェック』的な動作があります。

安定性チェック

pnpmとの違い

pnpmではminimumReleaseAgeは分単位の指定なので設定移行するようなときは注意が必要ですね。

pnpmにはBunのような「安定性チェック」の明示的な記載は公式にはありませんが、
(内部動作的にはあったりするんですかね?🤔 )
trustPolicy という、パッケージの信頼レベルが以前のリリースと比較して低下した場合にインストールをブロックする独自の防御機能があるようです。

trustPolicy: no-downgrade

バージョンに対する対処ひとつとっても、違いがあって面白いですね。

層2: ライフサイクルスクリプト

ライフサイクルスクリプトとはpreinstall / install / postinstall / prepare などが、インストールの途中で自動実行される仕組みのことです。
これはJavaScript以外の処理や環境依存の準備が必要なライブラリで使用される処理です。

このライフサイクルスクリプトを使用して悪さをするというのがKyohei Fukuda氏の記事でも説明されています。

Shai-Hulud 2.0ではpreinstallスクリプトを起点として感染します。

{
  "scripts": {
    "preinstall": "node setup_bun.js"
  }
}

つまり、何も対策していないと npm install しただけで、裏で勝手にスクリプトが動いて攻撃されてしまうということです。何も知らずにinstallしただけで動く、と思うと少し怖いですよね。

これに対して記事の中では.npmrcファイルにignore-scripts=trueを設定することでpreinstallスクリプトが実行されないと紹介されています。

Bunではどうなっている?

Bunでもライフサイクルを制御する機構は存在しています。
Bunでは依存関係のライフサイクルスクリプトを実行しないのがデフォルトとなっています。

https://bun.com/docs/pm/cli/install#lifecycle-scripts

ただし、メジャーなものに関しては実行可能になっているようです。

https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt

今回のShai-Hulud 2.0のようにメジャーなものでも感染が確認されているケースでは、すべてを無効するために別途環境変数の指定が必要なようなので注意が必要です👀

一部許可したい場合

厳しく制限しつつ、特定のライフサイクルスクリプトを許可したい場合には以下の設定を追加します。

Bunでの許可設定: trustedDependencies

{
  "name": "my-app",
  "version": "1.0.0",
  "trustedDependencies": ["my-trusted-package"] 
}

pnpmとの違い

この挙動に関してはpnpmも追従しており大きな差はなくなっています。
pnpmのv10以降では、ライフサイクルスクリプトを実行しないことがデフォルトになっています。
これに関しては動画の方でも説明されています!

https://youtu.be/WcFYrVReIe8?si=cikuUeHxfal7ploh

https://github.com/pnpm/pnpm/releases/tag/v10.0.0
https://socket.dev/blog/pnpm-10-0-0-blocks-lifecycle-scripts-by-default

ちなみにstrictDepBuildsを設定することで、ライフサイクルスクリプトに遭遇した場合にインストールが失敗するようにできるとのこと(v11以降でデフォルトにするか検討中とのこと)。

詳しくはこちら
https://pnpm.io/blog/2025/12/05/newsroom-npm-supply-chain-security#control-1-lifecycle-script-management

pnpmでの許可設定: onlyBuiltDependencies

{
  "name": "my-app",
  "version": "1.0.0",
  "pnpm": {
    "onlyBuiltDependencies": ["fsevents"]
  }
}

層3: SCAツール

SCA(Software Composition Analysis)ツールを利用することで、プロジェクト内の依存関係に含まれる既知の脆弱性を自動的に検出できます。
参考の記事・動画ではOSV-Scannerが紹介されています。

Bunではどうなっている?

Bunではインストール前にスキャンするAPIが存在しています。
これにより、依存関係を「入れてから検査する」のではなく、「入れる前に止める」設計が可能になります。

https://bun.com/docs/pm/security-scanner-api#security-scanner-api

#bunfig.toml
[install.security]
scanner = "@acme/bun-security-scanner"

OSV-ScannerのようにCLIやCIの中でスキャンできるだけでなく、インストール前にスキャンができるのが強力ですね!
現在、スキャナーとしては以下のようなものが公開されているようです🔎

SocketDev/bun-security-scanner

SocketDevが提供しているスキャナーです!
フリーモードでのスキャンも可能です👀

https://github.com/SocketDev/bun-security-scanner

2020年に創業した新興のセキュリティSaaSサービスを展開する企業です。
著名な企業にも導入されているようですね(恥ずかしながら今回の件でちゃんと認知しました)。

他のスキャンツールとの比較として気になるのが以下の文言。

Socket can actually detect an active supply chain attack
// Socketはアクティブなサプライチェーン攻撃を実際に検出できます

OSVのようなデータベースとの照合だけではないというのはさらなる安心材料になるかもしれません🤔
概要はよくある質問にもまとまっているのでご確認ください!
https://docs.socket.dev/docs/faq

bun-security-scanner/osv

こちらは公式なOrganization名のように見えますが、公式には記載ないので恐らく有志。
(ご存じの方がいれば教えてください🙇‍♂️)
なので、信頼感というところでは劣りますが、OSVデータベースとの照合チェックを行いたいという場合には良いかもしれません。

https://github.com/bun-security-scanner/osv

最終的な設定値

ここでは「まず事故を減らす」ことに絞って、最小の設定だけ載せます。厳密なポリシー運用はチーム事情で変わると思うので調整してみてください。

# bunfig.toml
[install]
minimumReleaseAge = 259200 # seconds

minimumReleaseAgeExcludes = ["@types/bun", "typescript"]

[install.security]
scanner = "@socketsecurity/bun-security-scanner"

まとめ

調べてみるとパッケージマネージャのそれぞれに防御機構があって面白かったですね。
また、Bunはpnpmの機能をもとに議論されたり、その逆があったりと高めあってセキュリティ対応されているところも知れることができました。

https://github.com/pnpm/pnpm/pull/8897

https://github.com/oven-sh/bun/issues/22679

個人的にどちらを軸にするかは少し悩みますが、設定が少なめでいろいろ対応できるBunを個人利用の中心にしていこうかなと考えています🤔
今後も動向をチェックしていきたいところです👀

機能比較まとめ

観点 Bun pnpm
公開直後の不安定な版を避ける minimum-release-age minimumReleaseAge
連続リリースを避ける追加チェック ◯ 安定性チェック ✕(公式仕様なし)
依存関係の信頼レベル低下を検出 trustPolicy
ライフサイクルスクリプトの実行制御
インストール時スキャン ◯(Security Scanner API) △(外部ツール連携)
マーベリックスのテックブログ

Discussion