Git --end-of-options: バージョン管理における引数インジェクションの防止

Git --end-of-options: バージョン管理における引数インジェクションの防止

Git における --end-of-options の役割

Git はバージョン 2.24.0(2019 年 11 月)で --end-of-options フラグを導入し、オプション解析を停止する専用マーカーを提供しました。これは、Git が標準の POSIX -- マーカーをリビジョンと pathspec を分離するために再利用した結果、ダッシュで始まるリビジョンがコマンドラインオプションと誤解されるセキュリティギャップが生じたために必要となりました。

標準的な Unix ツールでは -- がオプションの終了を示します。しかし Git では、git log main -- README.md のようなコマンドで --main がリビジョンであり、README.md がファイルパスであることを示すために使用されます。そのため、スクリプトが git log "$rev" を実行し、変数 $rev がダッシュで始まる場合、Git はそのリビジョンを参照ではなくフラグとして解析しようとします。

信頼できないリビジョンやパスを安全に扱うには、正しい構文は次の通りです:

git log --end-of-options "$rev" -- "$path"

この構文では、--end-of-options がリビジョンを保護し、-- が pathspec を保護します。

バージョン別サポート

サブコマンドごとに --end-of-options のサポートが段階的に展開され、互換性が断片的になっています:

  • Git 2.24.0: フラグの最初の導入。
  • Git 2.30.0: カスタム引数パーサを持つ git rev-parse のサポートが追加。
  • Git 2.43.1(2024 年 2 月): 以前は内部で -- を処理していたためフラグを拒否していた git checkoutgit reset のサポートが追加。

引数インジェクションと CWE-88

引数インジェクション(CWE-88)とは、プログラムが信頼できない文字列を引数リスト(argv 配列)に渡し、受け取るバイナリがそれらの文字列をダッシュで始まるオプションとして解析してしまうことです。シェルを必要としない点でコマンドインジェクションとは異なり、exec 呼び出しで引数配列を使用していても脆弱性が存在します。

危険な Git オプション

攻撃者がコマンドに注入できる場合、いくつかの Git オプションは攻撃の原始的手段として利用されます:

  • --upload-pack=<cmd>: git clone がサーバ側のバイナリを指定するために使用。
  • -c core.sshCommand=<cmd>: 任意の Git 呼び出しに対して SSH 接続方法を上書きする。

歴史的前例

この脆弱性クラスは、複数のバージョン管理システム(VCS)やツールに影響を与えてきました:

  • CVE-2019-13139(Docker Build): git-context URL フラグメント(#ref:dir)が git fetch に渡され、--upload-pack の注入が可能になった。
  • 2017 年 8 月の開示: CVE-2017-1000117(Git)、CVE-2017-1000116(Mercurial)、CVE-2017-9800(Subversion)、CVE-2017-12836(CVS)はすべて、ホスト名を ssh の引数として渡すことに関係しています。-oProxyCommand= で始まるホスト名は任意のコマンド実行を許しました。

パッケージマネージャへの影響

ほとんどのパッケージマネージャ(例:Bundler、npm、pip、Cargo、Go)は、マニフェスト(package.jsongo.mod など)から取得した URL やリファレンスを Git サブプロセスに渡すことが日常的です。19 のパッケージマネージャを調査したところ、デフォルトで 17 が git バイナリをフォークしていることが分かりました。

緩和策

パッケージマネージャは引数インジェクション防止のためにさまざまな戦略を採用しています:

  1. -- の使用: 多くのツール(例:Bundler)は URL の前に -- を付加します。POSIX の慣例に従っていますが、Git のリビジョンに対しては不十分です。
  2. 入力バリデーション: ダッシュで始まるブランチ名や URL を拒否するツールもあります。
  3. --end-of-options の使用: 2026 年 7 月時点で、cmd/go(Go)のみが全体で --end-of-options を使用しています(CVE-2025-68119 の修正として実装)。
  4. ライブラリの使用: Cargo(libgit2 経由)や Poetry(dulwich 経由)などのツールは、Git ライブラリを使用して argv の境界を完全に回避しますが、これにはライブラリ実装の上流セキュリティパッチを手動で追跡する必要があります。

互換性のトレードオフ

多くのツールが --end-of-options を避ける主な理由は、サポートされる最小 Git バージョンです。このフラグを利用するには、ほとんどのコマンドで Git 2.24.0、rev-parse で 2.30.0、checkoutreset で 2.43.1 が最低要件となります。これにより、Ubuntu 18.04(Git 2.17.0)など、古いディストリビューションが提供する Git バージョンを使用しているユーザーは対象外となります。

セキュリティ推奨事項のまとめ

Git をスクリプトやアプリケーションでラップする開発者向けの推奨事項:

  • ダッシュで始まる 信頼できない入力を直接引数として渡さない。
  • 環境が Git $\ge$ 2.30.0(checkout/reset では 2.43.1)を保証する場合は --end-of-options を使用
  • マーカーを組み合わせるgit <cmd> --end-of-options <rev> -- <path> を使用して、リビジョンと pathspec の両方をオプションパーサから完全に分離する。

SUMMARY: --end-of-options フラグは、ダッシュで始まる信頼できない入力がコマンドラインオプションとして解析され、引数インジェクションの脆弱性につながるという重大なセキュリティギャップを解消します。

TITLE: Git --end-of-options: バージョン管理における引数インジェクションの防止

Sources