flawopen.com/Teardowns/cve-2026-102422-shell-quote-comment-line-terminator-injection
CVE-2026-102422: shell-quote のコメントトークン注入
shell-quote 1.11.0 より前では、コメントトークンの後に改行終端文字を含む文字列が続くと、引用済み出力を POSIX シェルに渡した際に意味が変わる可能性がありました。攻撃者がトークンに影響でき、アプリが quote() を呼び、その出力をシェルで実行することが条件です。1.11.0 以降へ更新してください。
荷札のコメントは、その行の残りを隠せます。次の部分に改行が含まれると、コメントがそこで終わり、その後の内容が再び見えるようになります。この不具合では、後続トークンがシェル向けの引用出力でその状態を作れてしまいました。
主要な概念と専門用語
shell-quote- シェル形式のトークンを解析・引用する JavaScript パッケージです。出力はシェル構文向けであり、汎用の安全な文字列形式ではありません。
コメントトークン- シェルのコメント構文を表すトークンです。行終端文字が現れるまで後続テキストに影響します。
行終端文字- CR、LF、U+2028、U+2029 などの行境界で、シェルはコメントの終わりとして扱うことがあります。
CWE-78- OS コマンド内の特殊要素を適切に無害化できていない問題です。
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-78
根本原因の分析 (Root Cause)
影響を受ける quote() の経路は、コメントトークン自体に含まれる行終端文字を拒否していましたが、コメントを出力した状態を記録していませんでした。そのため後続の文字列トークンに行終端文字を含められました。攻撃者が影響できるトークンが quote() に渡され、返されたテキストが POSIX シェルで実行されると、行境界がコメントを終わらせ、後続テキストがシェル構文として解析されます。上流修正はコメント状態を記録し、それ以降のすべての文字列トークンで行終端文字を拒否します。これは、コメントトークン自体の行終端文字を拒否した CVE-2026-9277 の修正とは別の問題です。メンテナーは High / CVSS 3.1 8.1 と評価し、Ubuntu は別途 CVSS 4.0 9.2 Critical と評価しています。CVSS の版が異なるため、数値を同列に扱えません。
ステップ・バイ・ステップの攻撃フロー
トークンが引用関数に届く
アプリがコメントと後続文字列を含むトークン列を作ります。攻撃者がその列に影響できる必要があります。
関数がコメント構文を出力する
1.11.0 より前の quote() はコメント本文を検査しますが、その後に文字列が続くことを記録しませんでした。
後続トークンに行境界が含まれる
CR、LF、U+2028、U+2029 によってシェルコメントが終わる可能性があります。
アプリが出力をシェルで実行する
文書化された解釈の対象は、出力を POSIX シェルに渡す呼び出し元です。
更新版が危険な列を拒否する
1.11.0 はコメント状態を記録し、後続文字列に行終端文字があれば例外を発生させます。
ソースコード比較:脆弱 vs 堅牢化
脆弱な抜粋はコメントトークン内の行終端文字を検査しますが、後続文字列の状態を記録しません。上流の変更ではコメントを出力した状態を記録し、行終端文字を含む後続文字列を拒否します。この抜粋は解析と引用の境界を示すもので、エクスプロイトやシェルコマンドではありません。
ソース抜粋: quote.js — focused upstream diff hunks
// commit 6002b2e 適用前の quote.js — コメントトークン部分の抜粋
if ('comment' in s && typeof s.comment === 'string') {
if (LINE_TERMINATORS.test(s.comment)) {
throw new TypeError('`comment` must not contain line terminators');
}
return '#' + s.comment;
}
// commit 6002b2e 適用後の quote.js — 追加されたガードとコメント状態の正確な抜粋
var sawComment = false;
return xs.map(function (s) {
if (sawComment && typeof s === 'string' && LINE_TERMINATORS.test(s)) {
throw new TypeError('a token after a `comment` must not contain line terminators');
}
if ('comment' in s && typeof s.comment === 'string') {
if (LINE_TERMINATORS.test(s.comment)) {
throw new TypeError('`comment` must not contain line terminators');
}
sawComment = true;
return '#' + s.comment;
}
});
エンジニアリング&システム堅牢化チェックリスト
- ✓直接・間接依存の
shell-quoteを 1.11.0 以降へ更新し、ロックファイルと配布物で解決された版を確認します。 - ✓
quote()があらゆる文脈で安全だと見なさず、シェル呼び出しを避ける代替にも使わないでください。 - ✓可能なら
shell: falseと引数配列で固定の実行ファイルを起動し、対象プログラムに応じて各引数を検証します。 - ✓シェルが避けられない場合はコマンド構造を固定し、実行前にコメントトークン後の行終端文字を拒否します。
- ✓後続文字列の CR、LF、U+2028、U+2029 を対象に回帰テストを追加し、
parse()由来のトークンも確認します。 - ✓攻撃者が影響できるトークンを
quote()に渡してから出力を実行する箇所を確認します。パッケージを使っているだけでは悪用可能性は確定しません。 - ✓npm の版から Ubuntu パッケージの修正状況を推測せず、該当するディストリビューションの勧告を確認します。