Repository navigation
[security][tools] Bash でオプションにより作業ディレクトリを変える形(tar -C・git -C など)と、- で始まる pushd の行き先が検査されない(#613・#635 のレビューで発見) #637
Description
背景
#613 の merge 前レビュー(H-10)、#628 と #635 の着手前調査(H-11)、#635 の merge 前レビュー(H-12)で見つけた、Bash の閉じ込めの検査の既存の見逃しをまとめた Issue です(2026-10-09、利用者承認で起票)。着手前調査(H-14、2026-10-09)で、#635 と #628 の merge 後の develop(b506fbdf)でも同じ値であることを確かめ、設計を決めました。元の本文の 4(引用した字どおりの括弧の行き先の誤拒否)は、誤拒否を減らす変更で設計の幅が大きいため、別の Issue に分けます(「後続の候補」)。どの調査も、判定の関数を tempdir の fixture に対して呼び、結果を見ただけです。shell は、外の読み書きを伴わない pushd と echo を、作業用の一時ディレクトリで確かめただけです。
問題
R:2 段目の閉じ込めの判定、A:書き込みと認識、W:書き込み側の拒否、V:bash:verify の自動許可。fixture:ワークスペースの中の sub は普通のディレクトリで、その中の lf2 は外のファイルを指す symlink。-s2 も普通のディレクトリで、中に外向きの lf7 があります。
1. オプションで作業ディレクトリを変える形
プログラムのオプションで作業ディレクトリを変えると、作業ディレクトリの検査を通らず、その後の / のない相対パスが確かめられません。
| 入力(例) | develop の値 | 期待 |
|---|---|---|
tar -C sub -cf - lf2、tar -cf - -C sub lf2、tar -Csub -cf - lf2、tar --directory=sub -cf - lf2、tar --directory sub -cf - lf2、tar -xf a.tar -C sub lf2 |
R=0・V=1 | R=1 |
git -C sub log lf2、git -C sub -C deep log lf3、make -C sub lf2、make --directory=sub lf2 |
R=0・V=1 | R=1 |
env -C sub cat lf2、env --chdir=sub cat lf2、nice env -C sub cat lf2 |
R=0・V=1 | R=1 |
sudo -D sub cat lf2、sudo --chdir=sub cat lf2 |
R=0 | R=1 |
ninja -C sub lf2、pnpm -C sub exec cat lf2、yarn --cwd sub cat lf2、npm --prefix sub exec cat lf2、go -C sub run lf2、uv --directory sub run cat lf2、poetry -C sub run cat lf2、just -d sub cat lf2、cargo -C sub run lf2、patch -d sub lf2 < x.patch |
R=0 | R=1 |
tar -C s2* -cf - lf4、git -C s? log lf4、make -C {s2,x} lf4(値が glob・brace) |
R=0・V=1 | R=1 |
cd sub && tar -C deep -cf - lf3、tar -C sub -cf - deep/lf3 |
R=0 | R=1 |
比較:値が / を含む形(git -C sub/link status)、値そのものが外向き symlink の形(tar -C linked-outside …)、値が ..・~・$ の形は、今も R=1 です。
2. - で始まる pushd の行き先
pushd -- -s2 && cat lf7・pushd -- -s* && cat lf7・pushd -n -- -s2 && cat lf7 は R=0、pushd -- -s2 && echo x > lf7 は A=1・W=0 です。同じ形の cd -- -s2 && cat lf7 は R=1 です。shell は -- の後の語を、- で始まっても行き先として扱います(作業用の一時ディレクトリで sh -c 'pushd -- -s2' が -s2 に移ることと、pushd -- -1 が番号ではなく -1 という名前のディレクトリを探すことを確かめた)。
3. 拒否の理由の文
引用した glob の行き先(cd 's2*' && cat lf4)、引用した括弧の行き先(cd 's[2]' && cat lf5、cd "s[2]" && ls)の拒否で、理由の path が行き先ではなく後のコマンドの名前(cat・ls)になります。拒否はされますが、案内が分かりにくい形です。
原因
- 作業ディレクトリの検査は、
cd・pushd・popdの行き先だけを候補にします。プログラムのオプションの値は見ません。 env -C・sudo -Dは、前置きの解決で「決められない」になり、書き込み側では拒否されますが、作業ディレクトリの検査はその segment を読み飛ばします。読み取りの形は検査されません。pushdの位置引数は、--を外した後で+・-で始まるかを見て、スタックの番号として読み飛ばします。--があったかを見ていません。- 「決められない」の拒否の理由は、行き先の語ではなく、後に残った最初の相対の語を使います。
設計(決定)
- 1. 作業ディレクトリを変えるオプションの表を、新しい leaf module に置く。 各 segment の語を見て、表のプログラム(語の basename が一致するもの。前置きの
nice・timeout・command・sudo・envの後も含む)のオプションの値を取り出し、cdの行き先と同じ扱いで作業ディレクトリの候補に足し、行き先として閉じ込めを確かめます(extendとtargets)。候補は足すだけで、置き換えません([security][tools] Bash の書き込み検査が同じコマンドの cd・pushd の後の cwd を追わず、symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #568 の設計)。- 表(プログラム:短いオプション/長いオプション):
tar・gtar・bsdtar:-C/--directory。make・gmake:-C/--directory。git:-C(サブコマンドより前の全体のオプションだけ)。env:-C/--chdir。sudo:-D/--chdir。ninja・go・cargo:-C。pnpm:-C/--dir。poetry:-C/--directory。yarn・bun:--cwd。npm:--prefix。uv:--directory。just:-d/--working-directory。patch:-d/--directory。 - 値の形:
-C dir、-Cdir(短いオプションのまとまりの最後なら次の語、途中なら残り)、--directory=dir、--directory dir。--の後は見ません。 - 値が glob・brace の文字、
$・バッククォート、~、..の成分、空を含むときは、[security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635 の glob の行き先の印を立て、その後の相対の語を「決められない」で拒否します。 - 表に無いプログラム:今のまま(オプションの値を作業ディレクトリとしては見ない)。値が
/を含む形や、値そのものが外向き symlink の形は、今どおり読み取りの検査で拒否されます。find -execdirは仕組みが違うので、この Issue の範囲の外です(「後続の候補」)。 for・関数の中のcdを「決められない」にする規則(has_cd)は、オプションの値には当てません(オプションの作業ディレクトリは、そのコマンドの中だけで、繰り返しても積み重ならないため)。- 足した候補は、
cdと同じく後の segment にも残ります(足すだけの設計に合わせる)。
- 表(プログラム:短いオプション/長いオプション):
- 2.
pushdの--の後の語は、番号の判定に回さない。--を含むpushdは、最初の位置引数を、+・-で始まっても行き先として扱います。pushd -1・pushd +0(--なし)は今どおり読み飛ばします。 - 3. 理由の path を行き先の語にする。 glob の行き先の印を立てたときに、その語(行き先、またはオプションの値)を Inspection に記録し、
/のない語の分岐の「決められない」の理由で、後の相対の語の代わりにその語を使います。event の名前や schema は変えません。 - 試作(約 120 行。表の module 約 60 行、作業ディレクトリの検査の配線約 25 行、理由 1 行)で、上の表の形と 2 の形がすべて R=1 に、2 の書き込みの形が W=1 に、3 の理由の path が
s2*・s[2]になりました。
誤拒否の見積り
- 記録(
dev-reportsとworkspace/managementの jsonl から取った agent の Bash コマンド、重複なし 436 件):表のオプションを使う形とpushd --は 0 件です。試作で 436 件の値は 1 件も変わりませんでした。 - 参考(このリポジトリで動いた Claude Code の Bash コマンド、重複なし約 5.9 万件。正規表現で数えただけ):
git -C1,745 件(値の大半は$を含むか絶対パスで、今も拒否されるか、足した候補を確かめるだけ)、tar -C・--directory108 件、env -C28 件、sudo -D11 件、npm 系 54 件、cargo -C8 件、make -C1 件。どれも候補を足すだけなので、拒否になるのは、足した先に外向き symlink があるときか、値が glob・$などのときだけです。値が glob の形はgit -C3 件・tar -C2 件・env -C1 件で、どれも試験の文書の中か、$を含んで今も拒否される形でした。pushd --は 0 件です。 - 試作で新しく拒否された、ワークスペースの中に留まる形:
git -C sub status; cat lf2(root にlf2は無いがsub/lf2が外向き symlink。足した候補が後の segment に残るため)の 1 件だけです。git -C sub status、make -C sub test、tar -C sub -cf - f、tar -C sub -xf a.tar、env -C sub cat f、git commit -C HEAD、git -C sub -C deep status、npm --prefix sub test、git -C . statusは変わりませんでした。
受け入れ条件
- 上の表の形と 2 の形が R=1 になる(2 の書き込みの形は W=1)。3 の理由の path が、行き先の語(オプションの値を含む)になる。
- ワークスペースの中の読み書き(
git -C sub status、make -C sub、tar -C sub -cf - f、tar -C sub -xf a.tar、env -C sub cat f、git commit -C HEAD)を誤って拒否しない。誤拒否の見積りを記録する。 - 表に無いプログラムの扱い(今のまま)と、表の一覧を、module の文書に書く。
- 保護 path、秘密情報、bash:verify の自動許可、[security][tools] Bash の書き込み検査の字句解析がバッククオートのコマンド置換を扱わず、中の書き込みを見逃してワークスペース外へ書き込める(#576 の調査で発見) #581・[security][tools] Bash の字句解析が二重引用の中の $(…)・${…} の " を引用の閉じと読み違え、後ろをコメントとして消してワークスペース外へ書き込める(#581 のレビューで発見) #585・[security][tools] Bash の 2 段目の path 検査が、$・glob・動的な値で静的な相対パスや展開先を確かめず、ワークスペース外を読める(#571 を統合) #582・[security][tools] Bash の書き込み先で、引用した閉じ記号と実行される glob・brace が混ざると、外向き symlink の先への書き込みを拒否しない(#582 のレビューで発見) #602・[security][tools] Bash の 2 段目の path 検査が、空白を含む静的パスと = 付きの引数を候補にせず、外を読める(#582 のレビューで発見) #603・[security][tools] Bash の閉じ込めの検査が、引用していない CR を区切りとして扱い、/ を含まない相対の語を候補にしないため、外向き symlink を通して外を読み書きできる(#602・#603 のレビューで発見) #613・[security][tools] Bash の閉じ込めの検査が、.. と ~ だけの語を確かめず、dd の of= を書き込みとして認識しない(#613 の調査で発見) #628・[security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635 の印と判定が緩む向きに変わらない。今の decision の表([security][tools] Bash の 2 段目の path 検査が、$・glob・動的な値で静的な相対パスや展開先を確かめず、ワークスペース外を読める(#571 を統合) #582 の corpus の表と、H-03〜H-14 の probe)で、R・A・W・P・S が 1→0 になる行と、V が 0→1 になる行が 0 件である。
- 既存の検証・受入・証跡・リリースゲートを緩めていない(AGENTS.md の honest-failure 方針)。成長上限の baseline を引き上げていない。
対象ファイル
src/tools/bash_write_guard/option_directory.rssrc/tools/bash_write_guard/working_directory.rssrc/tools/bash_write_guard.rssrc/tools/bash.rstests/issue637_bash_option_working_directory.rs
参照
- 新しい leaf module は表と値の取り出しだけを持ち、作業ディレクトリの検査からは配線だけで呼びます。上位の module には
modの宣言を 1 行足すだけです。 - 試験は新しいファイルに置きます([security][tools] Bash の閉じ込めの検査が、brace の展開から出てくる ~ を確かめず、home の一覧を読める(#628 のレビューで発見) #641 と同時に進めるため。[security][tools] Bash の 2 段目の path 検査が、$・glob・動的な値で静的な相対パスや展開先を確かめず、ワークスペース外を読める(#571 を統合) #582 の decision の表は変えない)。
- 着手前調査の記録:ローカルの workspace/tmp/1007/wave1/results/H-14/report.md(試作の差分は SSD の wave1-20261007/h14/proto.diff)
- 発見の記録:ローカルの workspace/tmp/1007/wave1/results/ の H-10・H-11・H-12 の report.md
根拠(develop b506fbdf)
- 作業ディレクトリの候補は
cd・pushdの行き先だけ:src/tools/bash_write_guard/working_directory.rs:180-241 - 前置きが解決できない segment(
env -C・sudo -D)は読み飛ばす:src/tools/bash_write_guard/working_directory.rs:196-201、前置きの判定はsrc/tools/bash_write_guard/command_prefix.rs:324・:459 pushdの番号の判定は--を見ない:src/tools/bash_write_guard/working_directory.rs:224- 理由の path は後の最初の相対の語:
src/tools/bash.rs:835-841
後続の候補(この Issue の範囲の外)
- 引用やエスケープで字どおりにした括弧の行き先(Next.js の動的ルートのディレクトリへ引用して cd する形)を、[security][tools] Bash の書き込み先で、引用した閉じ記号と実行される glob・brace が混ざると、外向き symlink の先への書き込みを拒否しない(#582 のレビューで発見) #602 の引用された glob の印で区別して、字どおりの候補として足す(元の本文の 4。別の Issue に分ける)。
find … -execdir(見つけたファイルのディレクトリで実行する形)と、表に無いプログラムの作業ディレクトリのオプション。
関連(参考)
- [security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635
- [security][tools] Bash の閉じ込めの検査が、.. と ~ だけの語を確かめず、dd の of= を書き込みとして認識しない(#613 の調査で発見) #628
- [security][tools] Bash の閉じ込めの検査が、brace の展開から出てくる ~ を確かめず、home の一覧を読める(#628 のレビューで発見) #641
- [security][tools] Bash の書き込み検査が同じコマンドの cd・pushd の後の cwd を追わず、symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #568
- [security][tools] Bash の書き込み先で、引用した閉じ記号と実行される glob・brace が混ざると、外向き symlink の先への書き込みを拒否しない(#582 のレビューで発見) #602
- [security][tools] Bash の字句検査では、プログラム経由の間接書き込みや検証コマンドの副作用を閉じ込められない #502
Activity
Kewton commentedon Oct 9, 2026
2026-10-09:元の本文の 4(引用した字どおりの括弧の行き先の誤拒否)は、着手前調査(H-14)で設計が大きいと分かったため、#643 に分けました(利用者承認)。この Issue では 1〜3 を扱います。
Merge pull request #646 from Kewton/feature/issue-637-security-tools-…
Kewton commentedon Oct 10, 2026
PR #646 で develop に merge しました(merge commit c98bdc7、CI・acceptance・Next.js domain oracles success)。オプションで作業ディレクトリを変える形(tar -C・git -C・make -C・env -C・sudo -D など)を表にして検査し、pushd -- の行き先と拒否の理由の語も直しました。merge 前レビュー(H-16)は blocker 0。綴りの変種は #645、引用した括弧の誤拒否は #643 で扱います。
Merge pull request #683 from Kewton/feature/issue-643-tools-bash-cd-n…