背景
#509 (bash_write_guard の変異テスト)の着手前調査(2026-09-30)で見つけた見逃しです。2026-10-01 の着手前調査(develop@87f7a77 、#565 ・#567 の merge 後)で、原因と直し方を確かめました。判定の関数を呼んで結果を確かめただけで、ワークスペースの外への実際の書き込みは試していません。
問題(背景)
Bash の書き込み検査は、program を segment の先頭の語(代入を剥がした後)だけで決めます。そのため、前置きのコマンドや予約語の後ろの書き込みを認識しません。
入力(例)
今の判定
env tee /tmp/f、env FOO=1 cp a /tmp/f、sudo cp a /tmp/f、command cp a /tmp/f、nohup tee /tmp/f、timeout 5 cp a /tmp/f
許可
{ tee /tmp/f; }、! tee /tmp/f、if true; then tee /tmp/f; fi、while tee /tmp/f; do :; done
許可
env -C sub tee link/f(cwd を変える前置き。sub/link は外を指す symlink)
許可(/tmp 以外の外へも書ける)
影響(背景)
ワークスペースの外(主に /tmp。env -C では symlink の先のどこへでも)へ書き込めます。
同じ抽出を使う bash:verify の自動許可と inspect の段の書き込み検査でも、env tee out.txt・timeout 5 tee out.txt が「書き込みなし」と判定され、自動許可されます。
決めた設計(2026-10-01)
前置きを剥がしてから program を決める。 新しい葉の module に前置きの表を置き、segment の語に不動点まで繰り返し当てる(深さの上限 16、超えたら拒否)。結果は「program が決まった」「何も実行しない」「剥がし方が決められない」の 3 種類。
前置きの表 (値をとる option を区別する):env(-i・-・-0・-v・-u NAME・-P path・--・代入)、command(-p。-v・-V は実行しない)、builtin、exec(-c・-l・-a name)、nohup、sudo(flag と値をとる option。-l・-v・-k・-K・-V は実行しない。-e と sudoedit は operand が書き込み先)、doas、timeout・gtimeout(option と必須の時間)、nice、ionice、stdbuf、time(予約語と /usr/bin/time。-o file は書き込み先)、setsid、caffeinate、arch、!・{・}・if・then・elif・else・while・until・do・coproc・function NAME。
剥がし方が決められない形は拒否に倒す(正直な失敗)。ただし誤拒否を絞る :未知の option、値の有無が分からない option、env -S、cwd や shell を変える前置き(env -C・--chdir・sudo -s・sudo -i・sudo -D・sudo -R)、深さの上限を超えた形は、残りの語の basename を走査し、書き込みの program(tee・cp・mv・install・ln・mkdir・rm・touch・truncate・chmod・chown・cd)か前置きが 1 つでもあれば拒否する。無ければ今と同じく許可する。拒否は、今の cd - の拒否と同じ形で返す。
他の判定 :書き込みありの判定(bash:verify の自動許可・inspect の段)は、剥がした後の書き込み先で判定する。剥がし方が決められない形は、走査に一致した場合だけ書き込みありにする。保護された path の検出は、剥がした後の書き込み先で行う。どれも厳しくなる向きで、緩む経路はない。timeout 600 cargo test・env FOO=1 cargo test などの検証コマンドの自動許可は変わらない。
[security][tools] Bash の書き込み検査が同じコマンドの cd・pushd の後の cwd を追わず、symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #568 のための区別 :前置きを「exec の前置き」(env・nohup・timeout・nice・sudo・doas・ionice・stdbuf・/usr/bin/time・setsid・caffeinate・arch)と「shell の前置き」(command・builtin・!・予約語・{・予約語の time)に分けて返す。exec の前置きの後の cd は外部の program で、shell の cwd を変えない。[security][tools] Bash の書き込み検査が env・sudo・timeout などの前置きや { … } の中の書き込みを認識せず、ワークスペース外へ書き込める(#509 の調査で発見) #566 では、どちらの cd も今と同じく "working directory" の対象として記録する(厳しい側)。字句解析(tokenizer)は変えない((・) は今と同じ区切り)。
範囲外 :xargs、find -exec、sh -c、eval、su -c、watch、script -c は、文字列や stdin から command が決まる形なので [security][tools] Bash の字句検査では、プログラム経由の間接書き込みや検証コマンドの副作用を閉じ込められない #502 で扱う。表にない前置き(chrt・taskset・unshare・busybox など)は既知の限界として report に書く。
イベントの名前・schema は変えない。
対象ファイル
src/tools/bash_write_guard.rs — write_targets の program の決め方を新しい module の呼び出しに置き換え、拒否の配線と書き込み先の表のテストを足す
src/tools/bash_write_guard/command_prefix.rs — 新規。前置きの表、剥がし方、決められない形の走査、単体テスト
tests/issue566_bash_command_prefix_write_targets.rs — 新規。path_confinement_rejection を呼ぶ結合テスト
受け入れ条件
テストの設計(参考)
拒否する形(/tmp/f と、fixture の sub/link → 外):
問題の表の形すべて
env -i・env -u HOME・env -u HOME FOO=1・env --・/usr/bin/env の tee /tmp/f、env -S "tee /tmp/f"
command -p tee /tmp/f、exec tee /tmp/f、exec -a x tee /tmp/f
sudo -u root・sudo -E・sudo -- の tee /tmp/f、sudo -s tee /tmp/f、sudo -e /tmp/f、doas -u root tee /tmp/f
timeout -s KILL 5・timeout -k 1 5・timeout --preserve-status 5s の tee /tmp/f
nice -n 5・nice -5・ionice -c 3・stdbuf -oL・time -p の tee /tmp/f、/usr/bin/time -o /tmp/f true
for x in a; do tee /tmp/f; done、true && { tee /tmp/f; }、f() { tee /tmp/f; }、coproc tee /tmp/f
入れ子:env sudo timeout 5 tee /tmp/f など
決められない形:sudo -X u tee /tmp/f(未知の option)
許可のままの形:
env FOO=1 tee out.txt、printf x | env tee out.txt、timeout 5 cp a.txt b.txt、env tee /dev/null
env FOO=1 cargo test、env RUST_LOG=debug cargo test、timeout 600 cargo test、timeout --foreground 600 cargo test、timeout --unknown 600 cargo test(決められないが走査に一致しない)
nice cargo build、nohup cargo build、time cargo test、{ cargo test; }、command -v cargo、command -v tee /tmp/f、sudo -l tee /tmp/f
保護された path:env FOO=1 tee <保護された path> が拒否される。
テストの関数名には、すべて command_prefix を含めます(変異テストの名前のフィルタで、単体テストと結合テストの両方が走るようにするため)。
回帰で通す既存のテスト(参考)
tests/issue567_bash_glob_write_targets.rs
tests/issue428_bash_path_tokens.rs
tests/bash_workspace_confinement.rs
作業の場所(参考)
ビルドとテストは CARGO_TARGET_DIR=/Volumes/SSD_NX/tmp/issue-566-20261001-01/target-566 と CARGO_INCREMENTAL=0 で行います。
変異テストのコマンド(--lib は付けない。付けると結合テストが走らない):
cargo mutants --file src/tools/bash_write_guard/command_prefix.rs --timeout-multiplier 2 --minimum-test-timeout 60 --jobs 3 -o /Volumes/SSD_NX/tmp/issue-566-20261001-01/mutants -- command_prefix
ベースラインで単体テストと結合テストの両方が走ることを確かめます。TMPDIR も /Volumes/SSD_NX/tmp/issue-566-20261001-01/ の下に置きます。
着手前調査の report:ローカルの workspace/tmp/1001/issue-566/results/H-01/report.md。probe:/Volumes/SSD_NX/tmp/issue-566-20261001-01/probe。
背景
#509(bash_write_guard の変異テスト)の着手前調査(2026-09-30)で見つけた見逃しです。2026-10-01 の着手前調査(develop@87f7a77、#565・#567 の merge 後)で、原因と直し方を確かめました。判定の関数を呼んで結果を確かめただけで、ワークスペースの外への実際の書き込みは試していません。
問題(背景)
Bash の書き込み検査は、program を segment の先頭の語(代入を剥がした後)だけで決めます。そのため、前置きのコマンドや予約語の後ろの書き込みを認識しません。
env tee /tmp/f、env FOO=1 cp a /tmp/f、sudo cp a /tmp/f、command cp a /tmp/f、nohup tee /tmp/f、timeout 5 cp a /tmp/f{ tee /tmp/f; }、! tee /tmp/f、if true; then tee /tmp/f; fi、while tee /tmp/f; do :; doneenv -C sub tee link/f(cwd を変える前置き。sub/link は外を指す symlink)影響(背景)
env -Cでは symlink の先のどこへでも)へ書き込めます。bash:verifyの自動許可と inspect の段の書き込み検査でも、env tee out.txt・timeout 5 tee out.txtが「書き込みなし」と判定され、自動許可されます。決めた設計(2026-10-01)
-i・-・-0・-v・-u NAME・-P path・--・代入)、command(-p。-v・-Vは実行しない)、builtin、exec(-c・-l・-a name)、nohup、sudo(flag と値をとる option。-l・-v・-k・-K・-Vは実行しない。-eと sudoedit は operand が書き込み先)、doas、timeout・gtimeout(option と必須の時間)、nice、ionice、stdbuf、time(予約語と /usr/bin/time。-o fileは書き込み先)、setsid、caffeinate、arch、!・{・}・if・then・elif・else・while・until・do・coproc・function NAME。env -S、cwd や shell を変える前置き(env -C・--chdir・sudo -s・sudo -i・sudo -D・sudo -R)、深さの上限を超えた形は、残りの語の basename を走査し、書き込みの program(tee・cp・mv・install・ln・mkdir・rm・touch・truncate・chmod・chown・cd)か前置きが 1 つでもあれば拒否する。無ければ今と同じく許可する。拒否は、今のcd -の拒否と同じ形で返す。timeout 600 cargo test・env FOO=1 cargo testなどの検証コマンドの自動許可は変わらない。!・予約語・{・予約語の time)に分けて返す。exec の前置きの後の cd は外部の program で、shell の cwd を変えない。[security][tools] Bash の書き込み検査が env・sudo・timeout などの前置きや{ … }の中の書き込みを認識せず、ワークスペース外へ書き込める(#509 の調査で発見) #566 では、どちらの cd も今と同じく "working directory" の対象として記録する(厳しい側)。字句解析(tokenizer)は変えない((・)は今と同じ区切り)。対象ファイル
src/tools/bash_write_guard.rs— write_targets の program の決め方を新しい module の呼び出しに置き換え、拒否の配線と書き込み先の表のテストを足すsrc/tools/bash_write_guard/command_prefix.rs— 新規。前置きの表、剥がし方、決められない形の走査、単体テストtests/issue566_bash_command_prefix_write_targets.rs— 新規。path_confinement_rejection を呼ぶ結合テスト受け入れ条件
env tee out.txt・timeout 5 tee out.txtが true、timeout 600 cargo test・env FOO=1 cargo testが false になることをテストで固定したテストの設計(参考)
拒否する形(/tmp/f と、fixture の sub/link → 外):
許可のままの形:
保護された path:env FOO=1 tee <保護された path> が拒否される。
テストの関数名には、すべて command_prefix を含めます(変異テストの名前のフィルタで、単体テストと結合テストの両方が走るようにするため)。
回帰で通す既存のテスト(参考)
作業の場所(参考)
cargo mutants --file src/tools/bash_write_guard/command_prefix.rs --timeout-multiplier 2 --minimum-test-timeout 60 --jobs 3 -o /Volumes/SSD_NX/tmp/issue-566-20261001-01/mutants -- command_prefix
ベースラインで単体テストと結合テストの両方が走ることを確かめます。TMPDIR も /Volumes/SSD_NX/tmp/issue-566-20261001-01/ の下に置きます。
根拠(develop@87f7a77)