Repository navigation
[security][tools] Bash の字句検査では、プログラム経由の間接書き込みや検証コマンドの副作用を閉じ込められない #502
Description
背景
2026-09-27 の品質監査(静的解析・コードメトリクス・ブラックボックステスト)で検出した指摘です。基準コミットは develop@38efcc1e15b1 です。
対象の発見事項:FIN-002(P0)。監査の記録はローカルの workspace/tmp/0927/quality-audit-01/final/(report.md、findings.jsonl、traceability.csv)にあります。
問題
Bash の書き込み検査(bash_write_guard.rs)はコマンド文字列からリダイレクトや既知コマンドの書き込み先を抽出しますが、
python3 -c やビルド・テストスクリプト(bash:verify で自動承認される cargo test など)の中で行われる書き込みは解析できません。
最終的な実行は sh -c です。
影響
信頼できないモデル出力や、リポジトリのテスト・ビルドスクリプトが、ワークスペースの外へ書き込んだり、
凍結された検証入力を書き換えたりできる可能性があります。検証モードでは、こうした間接コマンドが自動承認されることがあります。
根拠(基準コミット時点)
src/tools/bash_write_guard.rs:84src/tools/bash.rs:187src/tools/bash.rs:650src/tools/allow_policy.rs:140src/bin/gui_server/delegate.rs:38
監査での確認状況
- FIN-002(P0):未確認(攻撃未実行、AG-6 NOT_RUNNABLE)
注意:この指摘はコードを読んだうえでの推論です。監査では攻撃の実行による確認ができていません(委任先の安全機能により攻撃的テストを中止したため)。対処の前に、隔離した環境で成立するかを確認してください。
対処の方向
- 任意のシェル実行とビルド・テスト実行を「信頼できないプロセス実行」として扱い、OS レベルのファイルシステム・ネットワークの隔離(サンドボックス)を検討する。
- 字句検査を安全境界とみなさない前提で、
--yesとbash:verifyの説明に限界を明記する。 - 既存の検証・受入チェックは維持し、間接コマンドの corpus テストを追加する。
受け入れ条件
- 隔離環境で、間接書き込み(スクリプト経由・検証コマンド経由)が成立するかを確認し、結果を記録した
- 対処方針(サンドボックス導入または権限の縮小)を決定し、文書に限界を明記した
- 間接コマンドの corpus テストを追加した
- 既存の検証・受入・証跡・リリースゲートを緩めていない(
AGENTS.mdの honest-failure 方針) - 成長上限(
docs/dev/dev-guardrails.md)の baseline を引き上げていない
設計の決定(2026-10-11、H-18)
設計調査(H-18、2026-10-10、develop c98bdc7c)の推奨を、利用者が 2026-10-11 にすべて承認しました。根拠は分割の Issue の本文で develop d708619a の値に書き直しています。
方式
- Bash の閉じ込めを字句検査だけに頼らず、OS の軽量な隔離を足します。macOS は
sandbox-exec(Seatbelt)、Linux は landlock です。bubblewrap・コンテナは採りません。 - 隔離を差し込むのは、モデルの Bash(bash:verify の verify 区間を含む)と、検証・受け入れのコマンドの
sh -cの起動点の 2 か所だけです。新しい葉のモジュールに置き、成長上限の対象のファイルは変えません。 - 字句検査は残し、緩めません。拒否の理由をモデルに返す役と、隔離が使えない環境での守りとして多層の防御にします。緩めるかの判断は第 2 段の後に、別の Issue で誤拒否の実績を見て行います。
判断
| # | 論点 | 決定 |
|---|---|---|
| 1 | 隔離が使えない環境(入れ子の sandbox、landlock の無い kernel 等)での挙動 | C:--sandbox auto|required|off。既定の auto は、使えなければ字句検査だけで続け、警告とイベントで「未適用」を記録する。required は Bash と検証を正直に失敗させる。GUI の delegate は required |
| 2 | ワークスペースの外のキャッシュへの書き込み | B:既知のキャッシュ(cargo の registry・git、npm、pip、Playwright のブラウザ)だけ許可する。キャッシュへの書き込みは汚染の経路として残ることを文書に明記する |
| 3 | ネットワーク | 第 1 段は変えない。第 3 段で --offline のときだけ OS で遮断する(localhost は許可) |
| 4 | 読み取り隔離の範囲 | A:拒否リスト(home の秘密の場所だけ拒否)。Linux ではワークスペース内の資格情報ファイル(dotenv 等)は字句検査に残る |
| 5 | 隔離の範囲 | 第 1 段はモデルの Bash と検証コマンドだけ。第 3 段で runtime の依存の導入へ広げる |
段階と分割の Issue
| 段 | 仮名 | 中身 |
|---|---|---|
| 1a | #655:書き込み隔離(macOS) | 共通の葉モジュール、--sandbox のモード、run の一時ディレクトリ、新しいイベント bash_sandbox_status、Seatbelt の書き込み隔離、間接コマンドの corpus、文書 |
| 1b | #656:書き込み隔離(Linux) | landlock の書き込み隔離、ubuntu の CI での確認、GUI の delegate を required にする |
| 2 | #657:読み取り隔離 | home の秘密の場所の読み取り拒否(macOS はワークスペース内の資格情報ファイルも) |
| 3 | #658:ネットワークと依存の導入 | --offline の OS での遮断、runtime の依存の導入への隔離の拡大 |
1a → 1b → 2 → 3 の順に進めます(1b・2・3 は 1a の葉モジュールに依存します)。
細部の決定(2026-10-11、分割の本文案 H-21 の確認点)
- 1a の適用の試験は worker の環境(入れ子でない macOS)で行い、動かなければ PM が手元で記録する。
- 3 の Linux の
--offline:すべての TCP の connect を拒否し、run の dev server の port だけ許可する。同じ port の外部ホストへの接続が通ること、UDP は制限できないことを文書に書く。 - 1b の CI の kernel と landlock の ABI は、workflow を変えずに試験の出力で記録し、CI の上で「未適用」なら試験を失敗させる。
- 2 で home の npm の設定の読み取りを拒否する(private registry の認証を使う npm は Bash の中で失敗する。文書に書く)。
- 判断 2 のキャッシュに pnpm・yarn の store は含めない(必要になったら足す)。
この Issue を閉じる条件
- 1a([security][tools] Bash と検証コマンドの書き込みを OS の隔離で閉じ込める(第 1 段 a:macOS の Seatbelt と共通の土台) #655)と 1b([security][tools] Bash と検証コマンドの書き込みを Linux の landlock で閉じ込める(第 1 段 b) #656)が merge されたとき、この Issue を閉じます。
- 受け入れ条件との対応:
- 隔離環境での間接書き込みの確認と記録:H-18 の macOS の試作(無害な fixture)と、1a・1b の試験(macOS の手元、ubuntu の CI)
- 対処方針の決定と、文書への限界の明記:この節と、1a の文書の更新(何を OS が閉じ、何が字句検査だけか)
- 間接コマンドの corpus:1a で追加
- 既存のゲートを緩めない、成長上限の baseline を上げない:1a・1b の受け入れ条件
- 2・3 は新しい Issue に引き継ぎます。この Issue のコメントにある読み取り側の形(symlink 経由の秘密、ファイルを読む環境変数、heredoc の python 等)は 2 で、
--offlineの限界は 3 で扱います。
Activity
Kewton commentedon Sep 28, 2026
#501(PR #545)からの引き継ぎ:資格情報ファイルの遮断は、型付きツール(Read/Glob/Grep/Write/Edit)・MCP WorkspacePath・pack 入力・Bash の字句上の直接参照(.env や .ssh/ などを名指しするコマンド)で入りました。一方、次のような字句上秘密名が現れない Bash の読み出しは #501 の範囲外として残っています(偽のキーで確認、registry と leaf の双方で成立)。
あわせて、動的変数・間接 script・encoding・hardlink/複製・TOCTOU など、実行権限や隔離で解くべき経路も本 Issue の範囲です。
Kewton commentedon Sep 30, 2026
追記:#509 の着手前調査(2026-09-30、develop@beb0fdc)で確かめた間接書き込みの形
判定の関数
commandagent::tools::bash::path_confinement_rejectionを呼んで結果を確かめただけで、コマンドは実行していません。次の形は、書き込み先として認識されず許可されます。2 段目の絶対パスの検査が /usr /bin /opt /etc /tmp を読み取り用に許可しているため(src/tools/bash.rs:930-935)、書き込める先は主に /tmp です。sh -c 'cp a /tmp/f'、bash -c "tee /tmp/f"dd of=/tmp/f、sed -i … /tmp/f、rsync a /tmp/ffind . -exec cp {} /tmp/f \;--referencechmod --reference=a /tmp/f(skip(1)が本当の対象を読み飛ばす)sh|bash|zsh -cは中身を再帰して判定する(引用が解けなければ拒否)。一覧にない書き込みは「認識できない書き込み」として拒否側に倒す(誤拒否は増える)。字句検査を安全境界とみなさないという、この Issue の方針とあわせて判断してください。>|と数字でない>&を redirect として読まず、ワークスペース外へ書き込める(#509 の調査で発見) #565(>|・>&)、[security][tools] Bash の書き込み検査が env・sudo・timeout などの前置きや{ … }の中の書き込みを認識せず、ワークスペース外へ書き込める(#509 の調査で発見) #566(env・sudo などの前置き)、[security][tools] Bash の書き込み先の glob・brace を展開前の文字列で判定するため、中の symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #567(glob・brace と symlink)、[security][tools] Bash の書き込み検査が同じコマンドの cd・pushd の後の cwd を追わず、symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #568(cd の後の cwd)。Kewton commentedon Oct 3, 2026
追記:#568 の merge 前レビュー(2026-10-03、H-02・H-03)で確かめた、文字列や名前を実行時に組み立てる形
#568(PR #579)は、同じコマンドの中の
cd・pushd・popdの後の作業場所を追い、CDPATH・DIRSTACK が出たら「行き先が決められない」として扱います。ただし、次の形は字面に cd・CDPATH・DIRSTACK が現れない、または実行時に名前が決まるため、許可されます。判定の関数を呼んで確かめ、shell の挙動は scratchpad の中でpwdを使って確かめただけです(sub/link・linked-outsideは外を指す symlink)。n=CD; export "${n}PATH=sub"; cd link; tee f、同じ形でcat secretn=DIR; declare "${n}STACK[1]=sub/link"; popd; tee f、同じ形でcat secreteval cd sub && tee link/f、trap 'cd sub' DEBUG; tee link/fsource・.、alias g='cd sub'の後のg、X=cd; $X sub && tee link/f、cd sub && sh -c 'tee link/f'cd・pushd・popdを含むコマンドで、declare・typeset・export・local・readonly・printf -v・readに渡す名前の語に$が含まれる場合は、行き先を「決められない」とする([security][tools] Bash の書き込み検査が同じコマンドの cd・pushd の後の cwd を追わず、symlink を通ってワークスペース外へ書き込める(#509 の調査で発見) #568 の CDPATH・DIRSTACK と同じ扱い、正直な失敗)。eval・trap・sourceは、この Issue の方針(字句検査を安全境界とみなさない)とあわせて判断してください。/Volumes/SSD_NX/tmp/issue-568-20261003-01/probe-h03/とworkspace/tmp/1003/issue-568/results/H-03/report.md。Kewton commentedon Oct 4, 2026
#576 の確認で見つかった、この Issue の範囲の形(2026-10-04)
#576(コメントと heredoc の本文の中の
'で後ろのコマンドが隠れる問題)の直した箇所の確認で、次の形が見つかりました。shell の字句解析では決めきれないため、#576 の範囲から外して、この Issue で扱います。判定の関数に入力して確かめただけで、実際には書き込み・読み取りをしていません。python3 <<'EOF'⏎pass#'⏎open("sub/""link/f","w")#'⏎EOFsub/link/fになる。shell の読み方では path が 2 つに分かれるpython3 <<'EOF'⏎pass#'⏎open("sub/""link/secret").read()#'⏎EOFpython3 <<'EOF'⏎pass#'⏎open(".e""nv").read()#'⏎EOFsub/linkは、ワークスペースの外を指す symlink の fixture です。open("sub/" + "link/f")など)。字句解析で見つけるのではなく、インタープリタに渡すコードそのものの扱い(実行の前に確認を求める、など)として、この Issue で決めるのがよいと考えます。根拠:
workspace/tmp/1003/issue-576/results/H-05/report.md(B2)。Kewton commentedon Oct 7, 2026
2026-10-07:#582 の merge 前レビュー(H-03・H-04)で、字句検査では閉じ込められない形を追加で確かめました。どれも #582 の前の develop にもあり、#582 では直していません。判定の関数を tempdir の fixture に対して呼び、結果を見ただけです(外の実際の読み取りは試していません)。
X=sub/link/secret; echo $X | xargs catecho $HOME/x | xargs catecho "cat $X" | sh、sh -c '…'、eval '…'X=sub/link/*; echo $Xecho $HOME/*、echo $?/*=を含むため候補になりません(=付きの引数は [security][tools] Bash の 2 段目の path 検査が、空白を含む静的パスと = 付きの引数を候補にせず、外を読める(#582 のレビューで発見) #603 で扱います)。Kewton commentedon Oct 7, 2026
2026-10-08:#602・#603 の merge 前レビュー(H-06)で、字句検査で閉じ込められない形を追加で確かめました。どれも #602・#603 の前の develop にもあります。判定の関数を tempdir の fixture に対して呼び、結果を見ただけです。
BASH_ENV=sub/link/secret bash -c true、LD_PRELOAD=…、GIT_CONFIG_GLOBAL=…grep -fsub/link/secret x、--opt=a=sub/link/secret、--opt==…=を 2 つ含む形sh -c 'cat sub/link/secret'sh -c・bash -cの本文は、閉じ込めの判定で調べていない/を含まない相対の語は、別に起票しました(security)。Kewton commentedon Oct 9, 2026
2026-10-09:#635 の merge 前レビュー(H-12)で、字句検査で閉じ込められない形を追加で確かめました。どれも #635 の前の develop にもあります。判定の関数を tempdir の fixture に対して呼び、結果を見ただけです。
eval 'cd s2* && cat lf4'、eval cd s2* \&\& cat lf4evalの中身は読まれないcd s@(2) && cat lf4、cd s+(2) && cat lf4sh -cで extglob は既定で無効なので、優先度は低い)evalや実行される文字列の中身は、字句検査では追い切れません。この Issue の「OS レベルの隔離」か「権限の縮小」の判断に含めてください。Merge pull request #661 from Kewton/feature/issue-655-security-tools-…
Kewton commentedon Oct 13, 2026
設計の決定(2026-10-11)どおり、書き込み隔離の 1a・1b の merge で閉じます。
--offlineの外向きの通信を OS の隔離で遮断し、runtime の依存の導入を隔離の中で動かす(第 3 段) #658(--offlineのネットワークと依存の導入の隔離):この Issue から引き継ぎ/dev/pts)、[security][tools] OS の隔離の許可と記録の食い違い(外を指すキャッシュの symlink、降格の event、Linux の reason、展開の上限) #668(許可と記録の食い違い)、[security][tools] OS の隔離の残り:$HOME の外の toolchain、GUI の delegate の試験と文書、macOS の rename、private registry #669(文書・CI・環境の残り)受け入れ条件のうち「間接書き込みの確認」「方針の決定と限界の明記」「間接コマンドの corpus テスト」は #655・#656 で満たし、検証・受入・証跡のゲートと成長上限の baseline は変えていません。