Repository navigation
[security][tools] Bash の閉じ込めの検査が、.. と ~ だけの語を確かめず、dd の of= を書き込みとして認識しない(#613 の調査で発見) #628
Description
背景
#613 の着手前調査(H-09、2026-10-08)で、#613 の範囲の外として見つけた、Bash の閉じ込めの検査の既存の見逃しです。#613 の merge 前レビュー(H-10)で tar -C の形を追記しました。着手前調査(H-11、2026-10-09)で、#613 の merge 後の develop(2a078deb)の値を確かめ、範囲と設計を決めました。どの調査も、判定の関数を tempdir の fixture に対して呼び、結果を見ただけです。shell は起動しておらず、ワークスペースの外の実際の読み書きは試していません。
範囲(H-11 で決定):この Issue は ..・~ だけの語と、dd of= を扱います。tar -C sub … のように、オプションで作業ディレクトリを変える形は、別の Issue に分けます(下の「分けたもの」)。ただし tar -C ..・git -C .. のように値が ..・~ の形は、この Issue の 1 で止まります。
問題
R:2 段目の閉じ込めの判定、A:書き込みと認識、W:書き込み側の拒否、P:保護 path の書き込みの検出、V:bash:verify の自動許可。
1. .. と ~ だけの語
ls ..・ls ~・cat ~ が R=0 です。ワークスペースの親ディレクトリや home の一覧を読めます。.. と ~ で始まる語は、/ を含まない語の検査(#613 の設計 2)の対象から外していて、/ を含まないので字どおりのパスの候補にもならないためです。/ を含む形(ls ../、cat ../x、ls ~/、cat ~/x、ls sub/..)は、今も R=1 です。
| 入力(例) | develop の値 | 期待 |
|---|---|---|
ls ..、ls -la ..、du -sh ..、find .. -name x、grep -r foo ..、cat .. |
R=0・V=1 | R=1 |
ls '..'(引用しても親ディレクトリ) |
R=0・V=1 | R=1 |
ls ~、cat ~、ls ~root、ls ~+、ls ~- |
R=0・V=1 | R=1 |
make PREFIX=~、cat --file=~、cat --file=..(= の右側) |
R=0・V=1 | R=1 |
tar -C .. -cf - x、git -C .. status |
R=0・V=1 | R=1 |
cd ..、cd ~、cd ~root && ls |
R=1・W=1 | 変わらない |
2. dd of=… が書き込みとして認識されない
dd if=in of=out が A=0・V=1 です。dd の of= は書き込み先なのに書き込みとして認識されず、bash:verify で自動許可されます。保護 path への書き込みも検出できません(H-11 で確認)。
| 入力(例) | develop の値 | 期待 |
|---|---|---|
dd if=in of=out(どちらも中) |
A=0・V=1 | A=1・V=0(R・W は 0 のまま) |
dd で of= に保護ファイル(conv=notrunc 付き、引用付きも) |
A=0・P=0・V=1 | A=1・P=1・V=0 |
保護ファイルのあるディレクトリへ cd して dd … of= にそのファイル |
A=0・P=0 | A=1・P=1 |
dd of= に外向き symlink、../x、~/f、外の絶対パス |
R=1・A=0・W=0・V=1 | R=1・A=1・W=1・V=0 |
dd if=in of=/dev/null |
A=0・V=1 | A=1・W=0・V=0 |
dd if=in(of= なし)、dd if=in > out |
今の値 | 変わらない |
外向き symlink を通る形が R=1 なのは、= の右側を読み取りの候補として確かめる既存の検査(#603)によるもので、書き込み側では認識していません。
影響
- 1:ワークスペースの外の一覧(ファイル名)を読めます。
- 2:書き込みのコマンドが自動許可の対象になり、保護 path の検出から漏れます。
設計(決定)
- 1.
..と~だけの語を、字どおりのパスの候補にする。 先頭の代入(NAME=value)より後の語が..ちょうど、または~で始まり/を含まないとき、その語を字どおりの読み取りの候補にします。既存の字どおりのパスの検査が..と~を拒否します。=の右側(make PREFIX=~、cat --file=..)も同じです。/を含む形は今の扱いのままです。echoの区切り(echo ~、echo ..)は、今の「echoで$HOMEを表示する」例外と揃えて、候補にしません。- 先頭の代入(
X=~ cargo test)は、今どおり変数として扱い、候補にしません。 cd ..の扱いとの関係:行き先の..・~は、今も書き込み側で拒否されています(W=1)。変えません。cd sub && ls ..は、bash では中に留まりますが、作業ディレクトリの候補には root も残るので、拒否されます。cd sub && cat ../f(/を含む)が今も R=1 なのと同じ扱いです(..の成分は、作業ディレクトリによらず拒否する方針)。
- 2.
ddのof=を書き込み先として認識する。ddの引数のうちof=で始まり値が空でないものを、操作名ddの書き込み先にします。書き込みの閉じ込め、作業ディレクトリの候補、保護 path の検出、自動許可の判定に、ほかの書き込み先と同じく渡ります。of=/dev/nullは、出力のリダイレクトやteeと同じく許可します。 - 3. corpus の表を更新する。 [security][tools] Bash の 2 段目の path 検査が、$・glob・動的な値で静的な相対パスや展開先を確かめず、ワークスペース外を読める(#571 を統合) #582 の decision の表の
allow-dd-nonpathの行(ddで中から中へ書く形)は A=0・V=1 を期待しています。これを A=1・V=0 に改め、上の表の形の行を足します。厳しくなる向きの変更で、表の他の行の期待は変えません。 - event の名前や schema は変えません。拒否は既存の理由の文を使います。
誤拒否の見積り
- 記録(
dev-reportsとworkspace/managementの jsonl から取った agent の Bash コマンド、重複なし 436 件):..・~だけの語、dd、tar、-Cを含むものは 0 件です。試作で 436 件の値は 1 件も変わりませんでした。 - 参考(このリポジトリで動いた Claude Code の Bash コマンド、重複なし約 5.9 万件。agent の記録とは別の母集団):
..だけの語は 93 件(多くはコメントや文字列の中で、実際のコマンドの引数はls .. | grep …・cd ..など)、~だけの語はls -A ~などです。どちらもワークスペースの外を読む形で、拒否するのが閉じ込めの約束どおりです。ddは 5 件で、of=を中に書く形はありません。 - 新しく拒否される形と、書き直し方:
cd sub && ls ..:root からlsで書けば通ります。ls "~"(~という名前のファイル):字句の読み方が引用を区別しないため、拒否されます。先頭に./を付ければ通ります。make PREFIX=~のような、home を値に渡す形:閉じ込めの外を指すので、拒否のままとします。
- 自動許可から外れる形:
dd … of=…(中に書く形も)。書き込みのコマンドなので、自動許可の対象でないのが正しい扱いです。
受け入れ条件
- 上の 2 つの表の形が、期待どおりの値になる。
- ワークスペースの中の読み書き(
ls、ls src、ls -la、ls .、catで中のファイル、echo ~、echo ..、git diff main..feature、git log HEAD~1、X=~ cargo test)の値が変わらない。dd if=in of=out(どちらも中)は R=0・W=0 のまま。 - 誤拒否の見積り(記録 0 件)と、上の「新しく拒否される形」を記録する。
- 秘密情報、[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 の印と判定が緩む向きに変わらない。今の decision の表([security][tools] Bash の 2 段目の path 検査が、$・glob・動的な値で静的な相対パスや展開先を確かめず、ワークスペース外を読める(#571 を統合) #582 の corpus の表、H-03・H-05・H-06・H-09・H-10 の probe)で、R・A・W・P・S が 1→0 になる行と、V が 0→1 になる行が 0 件である。
- 既存の検証・受入・証跡・リリースゲートを緩めていない(AGENTS.md の honest-failure 方針)。成長上限の baseline を引き上げていない。
対象ファイル
src/tools/bash/read_guard.rssrc/tools/bash_write_guard.rstests/corpus/apps/issue582-bash-read-candidates/fixtures/decision-cases.jsonltests/issue582_bash_read_candidates.rstests/issue628_bash_parent_home_dd.rs
参照
- 着手前調査の記録:ローカルの workspace/tmp/1007/wave1/results/H-11/report.md(試作の差分は SSD の wave1-20261007/h11/proto.diff)
- 発見の記録:ローカルの workspace/tmp/1007/wave1/results/H-09/report.md の「範囲外で見つけたもの」、H-10/report.md の「後続の候補 F2」
src/tools/bash/read_guard.rsの単体試験は、同じ corpus の表を読みます。表のallow-dd-nonpathの行を改めないと、その試験とtests/issue582_bash_read_candidates.rsの表の試験が落ちます(H-11 の試作で確認)。
根拠(develop 2a078deb)
..・~を/のない語の検査から外す:src/tools/bash/read_guard.rs:174-176(is_relative_target)/のない語は字どおりのパスの候補にならない:src/tools/bash/read_guard.rs:243-271(push_static)、src/tools/bash/path_tokens.rs:376-394(is_literal_pathは/を要する)echoの例外:src/tools/bash/read_guard.rs:102-111- 書き込み先の一覧に
ddがない:src/tools/bash_write_guard.rs:447-541(collect_program_targets) /dev/nullの例外:src/tools/bash_write_guard.rs:137-141- 自動許可は「書き込みと認識しない」ことを条件にする:
src/tools/allow_policy.rs:129-131 - corpus の表の行:
tests/corpus/apps/issue582-bash-read-candidates/fixtures/decision-cases.jsonl:42
関連(参考)
- 分けたもの(新しい Issue の候補):
- オプションで作業ディレクトリを変える形:
tar -C sub -cf - lf2・tar -cf - -C sub lf2・tar -Csub …・tar --directory=sub …・git -C sub …・make -C sub …・env -C sub cat lf2・sudo -D sub cat lf2が R=0 です(subの中のlf2は外向き symlink)。値が/を含む形(git -C sub/link status、make -C sub/link)は今も R=1 です。 - 作業ディレクトリの検査([security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635 と同じファイル)に、プログラムごとのオプションの表を足す設計になるので、[security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635 の後に進めます。
- オプションで作業ディレクトリを変える形:
- [security][tools] Bash の閉じ込めの検査が、引用していない CR を区切りとして扱い、/ を含まない相対の語を候補にしないため、外向き symlink を通して外を読み書きできる(#602・#603 のレビューで発見) #613
- [security][tools] Bash の 2 段目の path 検査が、空白を含む静的パスと = 付きの引数を候補にせず、外を読める(#582 のレビューで発見) #603
- [security][tools] Bash の cd・pushd の行き先が 1 つに一致する glob だと作業ディレクトリが検査されず、外を読み書きできる(#613 のレビューで発見) #635
- [security][tools] Bash の字句検査では、プログラム経由の間接書き込みや検証コマンドの副作用を閉じ込められない #502
Activity
Kewton commentedon Oct 9, 2026
2026-10-09 追記:#613 の merge 前レビュー(H-10)で、同じ種類の見逃しをもう 1 つ確かめました。
tar -C sub -cf - lf2・tar -cf - -C sub lf2が R=0・V=1 です(subの中のlf2は外のファイルを指す symlink)。オプションで作業ディレクトリを変える形で、git -Cと同じ種類です。#613 の前の develop からあります。この Issue で、..・~・dd of=と一緒に扱います(対象ファイルは着手前調査で確かめます)。Merge pull request #642 from Kewton/feature/issue-628-security-tools-…
Kewton commentedon Oct 9, 2026
PR #642 で develop に merge しました(merge commit b506fbd、CI・acceptance・Next.js domain oracles success)。.. と ~ だけの語を字どおりのパスの候補にし、dd の of= を書き込み先として認識するようにしました。merge 前レビュー(H-13)は blocker 0。-C の形は #637、brace の展開から出る ~ は #641 で扱います。