「開き扉」現象:Open VSXの新スキャナーに存在したフェイルオープンの不具合が、マルウェアをそのまま通過させていた

TL;DR: Open VSXが導入した新しい公開前スキャンパイプラインには、フェイルオープン(処理失敗時に安全側ではなく通過側に倒れる)の不具合がありました。公開エンドポイントに大量のリクエストを送りつけるだけでスキャナーがスキップされ、悪意ある拡張機能が「PASSED(合格)」の判定付きでそのまま公開されてしまう状態でした。この問題は報告からわずか3日で修正されました。

  •  •  •

Open VSXは、Cursor、Windsurf、そしてより広範なVS Codeフォークのエコシステムを支える拡張機能マーケットプレイスです。同サービスは最近、公開前スキャンパイプラインを導入しました。これは重要な取り組みであり、正しい方向性だと言えます。マルウェア検知、シークレットスキャン、バイナリ解析、名前スクワッティング(類似名によるなりすまし)対策など、まさにこのエコシステムが切実に必要としていた基盤です。

問題は次の点にありました。このパイプラインは、「スキャナーが一つも設定されていない」場合と「すべてのスキャナーの実行に失敗した」場合の両方を、単一のブール値の戻り値で表現していたのです。呼び出し元はこの2つの状態を区別できませんでした。そのため、高負荷時にスキャナーが実行に失敗すると、Open VSXはこれを「スキャン対象なし」と解釈し、拡張機能をそのまま素通りさせてしまっていました。

無料の公開者アカウントを持つ悪意ある行為者であれば、誰でもこの脆弱性を悪用できました。特別な権限も、内部関係者としてのアクセスも不要です。エンドポイントにリクエストを大量に送りつけ、あとは待つだけで済んでしまうのです。

私たちはこの問題を2026年2月8日(日曜日)にOpen VSXチームへ報告しました。チームは問題を認識し、2026年2月11日に修正版をリリースしました。迅速かつプロフェッショナルな対応であり、脆弱性開示のお手本と言える進め方でした。Open VSXチームの対応には敬意を表します。

私たちはこの問題を「Open Sesame(開け、ゴマ)」と名付けました。以下で詳しく見ていきましょう。

スキャンパイプラインの仕組み

悪意ある拡張機能という脅威の増大を受け、Open VSXチームは公開前スキャンパイプラインの実装に着手していました(Issue #1331 PR #1529)。目指していたのは、あらゆる拡張機能を公開前に検査する拡張可能な検証システムです。マルウェア検知、名前スクワッティング対策、シークレットスキャンなどが含まれ、検査に失敗した拡張機能は管理者レビュー待ちとして隔離される仕組みでした。

このパイプラインは、次のような流れで動作します。

Image

  1. 公開者がAPI経由で拡張機能をアップロード
  2. 拡張機能は保存されるが、スキャンが完了するまでは非アクティブ状態でダウンロード不可
  3. スキャンは2段階で実行。まず高速な同期チェックがインラインで実行され、いずれかのチェックに失敗した場合は即座に拒否される。次に、登録済みのすべてのスキャナーが並列のバックグラウンドジョブとして投入される
  4. 完了監視サービスがこれらのジョブを監視し、すべて合格すると拡張機能をダウンロード可能な状態にする。いずれかのスキャナーが脅威を検知した場合は、管理者レビュー待ちとして隔離される
  5. 別途稼働する復旧監視プロセスが、停止・失敗したスキャンを定期的にチェックし、再度キューに投入する

設定されたすべてのスキャナーに合格しない限り、拡張機能はユーザーの手元に届かない――これが本来の保証内容です。では、この仕組みがどのように破綻するのかを見ていきましょう。

この不具合は、ExtensionScanService.javaが結果をどう報告し、その呼び出し元がその結果をどう解釈するかという部分に存在していました。

まず、スキャン投入処理のメソッドは次の通りです。

Image

そして、PublishExtensionVersionHandler.java内の呼び出し元は次のようになっています。

Image

問題がお分かりでしょうか。このブール値は、まったく異なる2つの状況を一緒くたにしてしまっています。

(A) スキャナーが一つも設定されていない場合。 これは管理者の意図的な選択であり、アクティブ化しても安全です。

(B) すべてのスキャナージョブの投入に失敗した場合。 これは高負荷時などに起きる一時的なエラーであり、アクティブ化するのは安全ではありません

submitScannerJobsがfalseを返すと、呼び出し元は常に(A)のケースだと判断してしまいます。その結果、拡張機能は「合格」の判定を受け、アクティブ化され、ダウンロード可能になります。しかし高負荷下では、同じfalseという値が実際には(B)のケースを意味していることがあります。スキャナー自体は存在しているのに、データベースのコネクションプールが枯渇しているせいで、単にキューに投入できなかっただけなのです。

さらに厄介なことに、同じパターンがExtensionScanJobRecoveryService.javaにも存在していました。これは、失敗したスキャンを検知して再試行するために設計された復旧処理そのものです。つまり、バックアップとして機能するはずの安全網に、それが守るべき本体のシステムとまったく同じ穴が開いていたのです。

悪用の実態

特別な前提条件は必要なく、攻撃者に必要なのはユーザーアカウントだけです。攻撃者は任意のペイロードを仕込んだ標準的な.vsixファイルとして悪意ある拡張機能を複数用意し、公開エンドポイントに大量に送りつけます。

この手法が成立する理由は次の通りです。拡張機能を公開するたびに、スキャンパイプラインが起動し、ScanJobレコードの保存とJobRunrへのジョブ投入が必要になります。この2つの処理は、同一の共有プールからデータベースコネクションを奪い合います。十分な数の同時リクエストが発生すると、jobScheduler.enqueue()が例外をスローし始めます。catchブロックはエラーをログに記録するだけで処理を先へ進めてしまいます。すべてのスキャナーの投入が失敗し、enqueuedCountはゼロのままとなり、メソッドはfalseを返します。

こうして扉が開いてしまいます。拡張機能はopen-vsx.org上で公開され、スキャンステータスは「PASSED」と表示されるのです。

私たちは制御された環境下でのテストにより、この挙動を確認しました。フェイルオープンの経路を確実に再現できています。実運用環境では、1回の試行あたりの成功する時間的余地(レースウィンドウ)は狭いものの、公開エンドポイントにレート制限が存在しないため、攻撃者はコストゼロで何度でも再試行できます。つまり、これは「起こるかどうか」ではなく「いつ起こるか」という問題でした。

この不具合をすり抜けた拡張機能は、マーケットプレイス上の他の拡張機能と見分けがつきません。すべてのセキュリティチェックをスキップしたことを示す表示はUI上のどこにもなく、正規に合格した拡張機能とまったく同じように見えてしまいます。

修正内容

今回もOpen VSXチームは見事な対応を見せ、この脆弱性を即座に受け止め、わずか3日間で修正を完了させました。

今回の的確な修正は、問題の核心にあった曖昧さそのものに対処するものです。もはや曖昧なブール値は存在せず、失敗は失敗として明確に扱われるようになりました。

Image

Image

利用者が取るべき対応

直接的なリスクはすでに解消されています。Open VSXは2026年2月11日に修正版をリリース済みです。脆弱性が存在していた期間中に新規インストールまたは更新を行った拡張機能がある場合は、念のため確認しておくことをおすすめします。

この個別の不具合を離れて考えても、こうしたパターンが何を意味するのかを見つめ直す価値はあります。公開前スキャンは重要な防御層ではありますが、あくまで一つの層に過ぎません。パイプライン自体の設計は健全なものでしたが、「何もすることがない」状態と「何かがうまくいかなかった」状態を区別できない単一のブール値のせいで、この基盤全体が、圧力がかかると開いてしまう扉と化してしまいました。これは典型的なアンチパターンの一つです。フェイルオープン的なエラー処理が、本来は正当な「処理不要」ケース用に設計されたコードパスの陰に隠れてしまうのです。

同様のパイプラインを構築する際は、失敗状態を明示的に扱うようにしてください。「作業不要」と「作業失敗」を同じ戻り値で表現することは、決して避けるべきです。

これはまさに、Koiが検知するために作られたタイプのギャップでもあります。当社のリスクエンジンは、ネットワークリクエスト、ファイルシステムへのアクセス、コードパターンといった拡張機能の挙動を深く分析し、マーケットプレイス側のスキャナーが実行されたかどうかに関わらず、悪意のある意図を捉えます。詳しくはデモのご予約からご確認ください。

開示タイムライン

  • 2026年2月8日(日曜日): Open VSXチームへ脆弱性を直接報告
  • 2026年2月11日(火曜日): Open VSXチームが報告を確認
  • 2026年2月11日(火曜日): コミット 64720ccにて修正版をリリース

該当するCWE

  • CWE-636(セキュアなフェイルの欠如): 今回の問題の核心。スキャナーの失敗が、拡張機能のブロックではなく承認という結果につながっていた点
  • CWE-755(例外的状態の不適切な処理): enqueue()の例外は捕捉・ログ記録されていたが、その結果が処理結果に反映されていなかった点
  • CWE-362(競合状態): 同時並行の公開リクエストのタイミングを利用してスキャン中にコネクションプールを枯渇させる、タイミング依存の攻撃である点
  • CWE-400(制御されないリソース消費): 公開エンドポイントにレート制限が存在せず、コネクションプールの無制限な枯渇を許してしまっていた点

翻訳元: https://www.koi.ai/blog/open-sesame-how-a-fail-open-bug-in-open-vsxs-new-scanner-let-malware-walk-right-in

本記事は koi.ai の記事を翻訳・要約したものです。