Repository navigation
#613 [security][tools] Bash の閉じ込めの検査で、引用していない CR と / を含まない相対の語の symlink を確かめる - #636
Merged
Kewton merged 3 commits intoOct 9, 2026
Merged
Conversation
Issue #613: the Bash read and write guards split words on a carriage return, which Bash keeps inside the word, and the static path check required a `/`. An outward symlink named `sub/cr\rlink`, `cr\rdir`, `lfcr\r`, `lf`, `space lf`, or `wide lf` therefore escaped the read confinement (and `cr\rdir`/`sub/cr\rlink` the write confinement). - path_tokens: add `CrMode` and read a carriage return as an ordinary word character; `read_words` takes the reading. - bash_write_guard / working_directory: `lex_shell` / `shell_tokens` take the reading; `write_targets`, `raw_write_targets`, and `inspect` union both readings, so a refusal or a recognized mutation is kept when either reading sees it. - read_guard: union both readings, collect the `/`-less static read words (and the right-hand side of a non-assignment `NAME=value`), and prove them against the workspace root and each working-directory candidate, running the literal proof only when the joined path is an existing symlink. bash.rs calls it. - fixtures/corpus: add the CR and `/`-less symlinks to both fixtures and the decision rows for the two problem tables, the allowed table, and the two pinned `true⏎…` values. 判断: design 2's "an undeterminable working directory refuses every `/`-less word" is not applied, because `tests/issue568_bash_cwd_write_targets.rs` (outside this issue's scope) requires a bare `cd` chain that names no path to stay allowed; the existing relative-candidate branch already refuses the words a `cd` could redirect. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Issue #613 follow-up: cargo mutants found four survivors where dropping a reading, an undecidable mark, or a proof route left the tests green. - working_directory: `merge` unit test (bases union, targets, relative_words, undecidable OR, and no duplicated base) and an `inspect` test for the WordChar-only CR `cd` candidate. Kills the emptied `merge`, the missing `!` in the base-dedup check, and the `|=` -> `&=` undecidable mark. - read_guard: `joined_is_symlink` unit test (an `ENAMETOOLONG` name needs the proof, a missing name does not) and a command-level over-long-name rejection. Kills the `!= NotFound` -> `== NotFound` narrowing. - fixtures/corpus: an inside `cr␍sub` directory holding a nested outward symlink, plus the `cd cr␍sub && cat esc` row; only the merged WordChar candidate reaches it. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
…issue-613-security-tools-bash-cr-symlink-602-603
5 tasks
Kewton
deleted the
feature/issue-613-security-tools-bash-cr-symlink-602-603
branch
October 9, 2026 00:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
Bash の閉じ込めの検査に、既存の見逃しが 2 種類ありました(#602・#603 の merge 前レビューで発見)。
echo x > sub/cr⏎link/f、cat lfcr⏎+CRLF の行末)。/を含まない相対の語:静的な path の判定が/を必須にしていたので、その語自体が外を指す symlink だと外を読めました(例:cat lf、dd if=lf、source lf)。この PR は、Issue 本文の設計(着手前調査 H-09 で 1 つに絞ったもの)どおりに直します。
変更(対象 8 ファイル、+598/−27)
src/tools/bash/path_tokens.rs:CrMode(Separator=今の読み方、WordChar=bash と同じ)。read_wordsが読み方を受け取る。src/tools/bash/read_guard.rs:引用の外に CR があるときだけ 2 つ目の読み方を走らせ、候補を合わせる(確かめられない語はどちらかで見つかれば拒否)。src/tools/bash_write_guard.rs:lex_shellに読み方を足し、書き込み先(write_targets・raw_write_targets)を 2 つの読み方で集めて合わせる。has_recognized_mutation・protected_path_mutation・confinement_rejectionはこの和から求めるので、どれも今より緩まない。src/tools/bash_write_guard/working_directory.rs:作業ディレクトリの候補を 2 つの読み方で求め、Inspection::merge(候補・行き先・/のない語の和、undecidable の OR)で合わせる。/を含まない静的な語は、symlink のときだけ確かめる)src/tools/bash/read_guard.rs:/のない静的な語と=の右側を集め、ワークスペースの直下と各作業ディレクトリの候補につないで lstat する。symlink(または NotFound・ENOTDIR 以外の lstat の失敗)のときだけ、今の literal の確かめ方で確かめる。src/tools/bash.rs:その呼び出し 1 か所と、CrModeを共有するためのpub(crate) mod path_tokens。decision-cases.jsonlに 52 行(51 → 103 件。問題の 2 つの表のすべての形、「許可のまま」の表、true⏎rm 保護 path=0 1 0 1 0 0 とtrue⏎tee 外の絶対パス=1 1 1 0 0 0 の固定)。tests/issue582_bash_read_candidates.rs・tests/issue567_bash_glob_write_targets.rsと単体テストに、CR を含む symlink と直下の外向き symlink の fixture を足した。既存のテストの行は 1 行も消していない。fixture はテストの一時ディレクトリ(target の下)の中だけ。path_guard.rs・shell_lexical.rs・sensitive_path.rs・hidden_path.rs・allow_policy.rs・planner/verify.rs・runner.rs・loop_run.rs・generality_guardrails.rs。event 名・schema も変えていない。検証
commandmate verifyの 15 ゲート:develope6e00a4dを取り込んだ HEAD で 15/15 pass(d2304485、scope 8 本、違反 0)。TMPDIR=/tmp cargo test --test issue582_bash_read_candidates --test issue567_bash_glob_write_targets(CI と同じ条件を作るため):取り込んだ HEAD で exit 0、19 passed・0 failed、28 passed・0 failed(issue567・issue582 の順)。0ccc81bdの src + この PR のテストと jsonl):新しい 5 本だけが FAIL し、既存のテストはすべて pass(echo x > sub/cr\rlink/f・cat lf・300 字の名前・判定の表のcr-redirect-writeなどが拒否されない)。cargo mutants --jobs 1、--in-diff、--libと結合テスト 2 本):Inspection::mergeの 3 件、joined_is_symlinkの lstat の失敗の経路)は、テストを足して直した。lex_shellの==→!=)は 2 つの読み方が入れ替わるだけで和が同じ(同値の変異)。この PR では直していない既存の穴(#613 の前からあり、この PR で緩んではいない)
cd・pushdの行き先(例:cd s2* && cat lf4):作業ディレクトリの候補にも undecidable の印にもならず、外を読み書きできる。H-10 で見つけた base からの穴で、別の Issue で扱う。tar -C(tar -C sub -cf - lf2など):オプションで作業ディレクトリを変える形。[security][tools] Bash の閉じ込めの検査が、.. と ~ だけの語を確かめず、dd の of= を書き込みとして認識しない(#613 の調査で発見) #628 で扱う。ls ..・ls ~が R=0、dd of=…が書き込みと認識されない。誤拒否
/のない語が拒否されるのは、その名前がワークスペースの中の外向き symlink のときだけ(記録の作業用ワークスペースには 0 個)。外向き symlink と同じ名前のコマンド(例:node)を使うときの拒否は、閉じ込めの約束として受け入れる。Refs #613
🤖 Generated with Claude Code