Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ required-features = ["gui"]
name = "gui_server"
path = "tests/gui_server.rs"
required-features = ["gui"]

[target.'cfg(target_os = "linux")'.dependencies]
landlock = "0.4"
14 changes: 10 additions & 4 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,25 @@ before the shell starts.
an inode outside the workspace can still be written through. Seatbelt decides
by path, so it allows writing the workspace path even though the inode lives
outside; the sandbox only denies *creating* a new hardlink to an outside
inode.
inode. On Linux that creation needs Landlock ABI2 (kernel 5.19); on an older
kernel the link is not closed and is named in the `bash_sandbox_status`
reason.
- Backend availability is probed once at run start. On macOS the backend is
Seatbelt through `sandbox-exec`; under a nested sandbox it reports `rc=71` and
is unavailable. Other platforms have no backend in this stage.
is unavailable. On Linux the backend is Landlock; it requires ABI1 (kernel
5.13) and is unavailable below that, when Landlock is disabled, or when a
seccomp filter denies the ruleset syscalls. Other platforms have no backend in
this stage.
- `auto` continues with lexical checks only when the backend is unavailable,
warns once at startup, and records `bash_sandbox_status` with `applied=false`
and a reason. `required` instead fails Bash and verifier commands with
`bash_sandbox_unavailable`. `off` keeps the previous behavior. None of them
reports "sandboxed" when the sandbox was not applied.
- The dependency caches (cargo registry/git/package-cache, npm, pip, Playwright
browsers) stay writable so builds and tests work; they remain a contamination
path outside the workspace. pnpm and yarn stores are not allowed in this
stage.
path outside the workspace. On macOS they live under `~/Library/Caches` and
`~/.cargo`; on Linux under `~/.cargo`, `~/.npm`, and `~/.cache`. pnpm and yarn
stores are not allowed in this stage.

macOS `mktemp` ignores `TMPDIR` and targets the per-user temp directory, which
the sandbox denies. A `mktemp` inside the run temp directory (`mktemp -p
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Recommended route / 推奨順路:
Trial, Gate 1, and separate status, history, and result pages
- [Extensions](en/extensions.md) — four layers, boundaries, registration, and
the supply lifecycle
- [CLI reference](en/cli-reference.md) — all 66 public flags, defaults, and
- [CLI reference](en/cli-reference.md) — all 67 public flags, defaults, and
conflicts
- [Plan YAML editing](en/plan-yaml.md) — commented templates, offline
validation, next commands, and recovery diffs
Expand Down Expand Up @@ -53,7 +53,7 @@ walkthrough. Read the [security model](../../SECURITY.md) before enabling
- [GUI 入門](../user/getting-started-gui.md) — 前提確認、サンプル Trial、Gate 1、
分離された実行状況・履歴・結果ページ
- [拡張](ja/extensions.md) — 4 レイヤー、境界、登録導線、供給ライフサイクル
- [CLI リファレンス](ja/cli-reference.md) — 公開されている全 66 フラグ、既定値、排他関係
- [CLI リファレンス](ja/cli-reference.md) — 公開されている全 67 フラグ、既定値、排他関係
- [Plan YAML の編集](ja/plan-yaml.md) — コメント付き template、offline 検証、次コマンド、
recovery 差分
- [スラッシュコマンド](ja/slash-commands.md) — 一般レジストリで受け付ける全 24 コマンド名と
Expand Down
12 changes: 7 additions & 5 deletions docs/guide/en/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ before parsing finishes or in the resolved configuration after parsing; see
[Conflicts and combinations](#conflicts-and-combinations).

Clap also generates `-h`/`--help` and `-V`/`--version`. They are not part of the
66 application flags below. The hidden `--completion-contract-json <PATH>` is an
67 application flags below. The hidden `--completion-contract-json <PATH>` is an
internal integration surface and is intentionally not a public user flag.

## Flag reference
Expand Down Expand Up @@ -79,6 +79,7 @@ internal integration surface and is intentionally not a public user flag.
| `--style` | `<STYLE>` | `default` | Pass the plan presentation/generation style. | [Inline flags](slash-commands.md#inline-flags) |
| `--resume` | `<RESUME>` | none | Load the named saved minimal-loop session for a direct `--prompt` run. | [Session options](#conflicts-and-combinations) |
| `--offline` | none | off | Block runtime dependency setup and Bash commands containing npm/pnpm/yarn/cargo install, curl, or wget. Provider/API requests and other network-capable commands are unaffected. | [Providers](providers.md) |
| `--sandbox` | `<auto\|required\|off>` | `auto` | Select the OS write sandbox for Bash and verifier commands. `auto` applies it when the backend is available and otherwise continues with lexical checks only; `required` fails those commands when the backend is unavailable; `off` keeps the previous behavior. | [OS write sandbox](#os-write-sandbox) |
| `--quiet` | none | off (`narration = "normal"`) | Suppress presentation narration. | [Top-level keys](configuration.md#top-level-keys) |
| `--summary-json` | none | off | Append one machine-readable terminal run summary as the final stdout line. Omitting it preserves existing stdout bytes. | [Headless execution](../../user/headless.md) |
| `--trace` | none | off | Opt in to scrubbed provider request and response traces under the active run directory. | [Troubleshooting](troubleshooting.md) |
Expand All @@ -99,7 +100,7 @@ internal integration surface and is intentionally not a public user flag.

## OS write sandbox

The hidden `--sandbox auto|required|off` flag (default `auto`) controls an
The `--sandbox auto|required|off` flag (default `auto`) controls an
OS-level write sandbox for the model Bash tool and for verification/acceptance
commands. The lexical checks still run first; the sandbox adds confinement for
writes the lexical guard cannot see.
Expand All @@ -117,9 +118,10 @@ writes the lexical guard cannot see.
- **Modes:** `auto` uses the backend when available and otherwise warns once and
records `bash_sandbox_status` with `applied=false` and a reason, continuing
with lexical checks only; `required` fails Bash and verifier commands with
`bash_sandbox_unavailable`; `off` keeps the previous behavior. The backend is
unavailable under a nested sandbox (`rc=71`) and on a platform without one
(macOS only in this stage).
`bash_sandbox_unavailable`; `off` keeps the previous behavior. On macOS the
backend is Seatbelt and is unavailable under a nested sandbox (`rc=71`); on
Linux it is Landlock and is unavailable below ABI1 (kernel 5.13), when
disabled, or when a seccomp filter denies it.
- Dependency caches (cargo, npm, pip, Playwright browsers) stay writable so
builds and tests work; they remain a contamination path outside the workspace.

Expand Down
7 changes: 4 additions & 3 deletions docs/guide/ja/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
[排他関係と組み合わせ](#排他関係と組み合わせ)を参照してください。

Clap が生成する `-h`/`--help` と `-V`/`--version` は、以下のアプリケーション固有の
66 フラグには含めません。非表示の `--completion-contract-json <PATH>` は内部連携用であり、
67 フラグには含めません。非表示の `--completion-contract-json <PATH>` は内部連携用であり、
公開ユーザーフラグではありません。

## フラグ一覧
Expand Down Expand Up @@ -78,6 +78,7 @@ Clap が生成する `-h`/`--help` と `-V`/`--version` は、以下のアプリ
| `--style` | `<STYLE>` | `default` | plan の表示/生成スタイルを渡します。 | [インラインフラグ](slash-commands.md#インラインフラグ) |
| `--resume` | `<RESUME>` | なし | 直接 `--prompt` 実行で、指定した保存済み minimal-loop セッションを読み込みます。 | [セッションオプション](#排他関係と組み合わせ) |
| `--offline` | なし | オフ | runtime の依存関係セットアップと、npm/pnpm/yarn/cargo install、curl、wget を含む Bash コマンドを禁止します。provider/API 要求とその他のネットワーク可能なコマンドには影響しません。 | [プロバイダ](providers.md) |
| `--sandbox` | `<auto\|required\|off>` | `auto` | モデルの Bash ツールと検証・受け入れコマンドに対する OS レベルの書き込みサンドボックスを選びます。`auto` は backend が使えれば適用し、使えなければ字句検査だけで続けます。`required` は backend が使えないときに対象コマンドを失敗させます。`off` は今までの挙動を保ちます。 | [OS 書き込みサンドボックス](#os-書き込みサンドボックス) |
| `--quiet` | なし | オフ(`narration = "normal"`) | プレゼンテーションのナレーションを抑制します。 | [トップレベルキー](configuration.md#トップレベルキー) |
| `--summary-json` | なし | オフ | stdout 最終行へ機械可読な終端runサマリを1件追加します。省略時は既存stdout bytesを維持します。 | [Headless execution](../../user/headless.md) |
| `--trace` | なし | オフ | active run の `trace/` directory に scrub 済み provider exchange を opt-in 保存します。 | [トラブルシューティング](troubleshooting.md) |
Expand All @@ -98,11 +99,11 @@ Clap が生成する `-h`/`--help` と `-V`/`--version` は、以下のアプリ

## OS 書き込みサンドボックス

隠しフラグ `--sandbox auto|required|off`(既定 `auto`)は、モデルの Bash ツールと検証・受け入れコマンドに対する OS レベルの書き込みサンドボックスを制御します。字句検査は先に働き、サンドボックスは字句検査から見えない書き込みを閉じ込めます。
フラグ `--sandbox auto|required|off`(既定 `auto`)は、モデルの Bash ツールと検証・受け入れコマンドに対する OS レベルの書き込みサンドボックスを制御します。字句検査は先に働き、サンドボックスは字句検査から見えない書き込みを閉じ込めます。

- **OS が閉じるもの(backend が使えるとき):** コマンドやスクリプトの中の書き込み — `sh -c 'cp ...'`、`eval`、`xargs`、`find -exec`、インタープリタの `open(..., 'w')`、`dd of=`、`sed -i` — と、workspace の外の inode への hardlink です。書き込めるのは、workspace・run の一時ディレクトリ・既知の依存キャッシュ・`/dev/null` などのデバイスだけです。
- **字句検査だけのもの:** 読み取り、ネットワーク、backend が使えないときの全コマンドです。`--yes`・`--allow bash:verify`・`--offline` は、それ自体では間接的な書き込みを閉じません。適用されたサンドボックスだけが閉じます。`--allow bash:verify` はコマンドを分類で通すだけなので、そのコマンドが呼ぶスクリプトの中身を制限するのはサンドボックスだけです。
- **モード:** `auto` は backend が使えれば適用し、使えなければ起動時に一度警告し、`applied=false` と理由つきの `bash_sandbox_status` を記録して字句検査だけで続けます。`required` は Bash と検証コマンドを `bash_sandbox_unavailable` で失敗させます。`off` は今までの挙動を保ちます。入れ子の sandbox(`rc=71`)と backend の無いプラットフォーム(この段階は macOS のみ)では未適用です。
- **モード:** `auto` は backend が使えれば適用し、使えなければ起動時に一度警告し、`applied=false` と理由つきの `bash_sandbox_status` を記録して字句検査だけで続けます。`required` は Bash と検証コマンドを `bash_sandbox_unavailable` で失敗させます。`off` は今までの挙動を保ちます。macOS は Seatbelt で、入れ子の sandbox(`rc=71`)では未適用です。Linux は Landlock で、ABI1(kernel 5.13)未満・無効化・seccomp による拒否では未適用です。
- 依存キャッシュ(cargo・npm・pip・Playwright のブラウザ)はビルドとテストのために書き込み可能なままにします。workspace の外の汚染経路として残ります。

macOS の `mktemp` は `TMPDIR` を見ません。`mktemp -p "$TMPDIR"` か `$TMPDIR` 配下のテンプレートを使ってください。適用の試験の記録は `tests/corpus/apps/issue502-indirect-write/fixtures/macos-probe-results.md` にあります。
Expand Down
44 changes: 35 additions & 9 deletions src/bin/gui_server/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ const DELEGATION_WORKSPACE_CHANGED: &str = "Gate 1 workspace changed before CLI
const CONTINUATION_WORKSPACE_CHANGED: &str = "Gate 1 workspace changed before CLI continuation";
const RECOVERY_WORKSPACE_CHANGED: &str = "Gate 1 workspace changed before Recovery Run";
pub(super) const DELEGATE_PERMISSION_POLICY: &str = "read,write,bash:verify";
/// Fixed flags every delegated CLI invocation starts with. `--sandbox required`
/// (issue #656, decision 4) makes a delegated run fail honestly with
/// `bash_sandbox_unavailable` when the OS write sandbox cannot be applied, for
/// example inside a nested sandbox, instead of silently continuing with lexical
/// checks only.
const DELEGATE_FIXED_ARGS: &[&str] = &[
"--allow",
DELEGATE_PERMISSION_POLICY,
"--sandbox",
"required",
"--quiet",
"--footer",
"off",
"--stream",
"off",
];
const PACK_DIRECTORY_ENV: &str = "COMMANDAGENT_PACK_DIRECTORY";
const PACK_ID_ENV: &str = "COMMANDAGENT_PACK_ID";
const PACK_VERSION_ENV: &str = "COMMANDAGENT_PACK_VERSION";
Expand Down Expand Up @@ -295,15 +311,7 @@ fn delegated_cli_command(
command
.current_dir(&execution_workspace)
.env("COMMANDAGENT_EVAL_EVENTS", paths.events_path())
.args([
"--allow",
DELEGATE_PERMISSION_POLICY,
"--quiet",
"--footer",
"off",
"--stream",
"off",
])
.args(DELEGATE_FIXED_ARGS)
.arg("--cwd")
.arg(&execution_workspace)
.arg("--state-dir")
Expand Down Expand Up @@ -385,3 +393,21 @@ fn apply_confirmed_pack(
.env(PACK_HASH_ENV, hash);
Ok(true)
}

#[cfg(test)]
mod tests {
use super::*;

/// Issue #656 decision 4: the delegated CLI must be started with
/// `--sandbox required` so a GUI run fails honestly when the OS write
/// sandbox cannot be applied instead of silently dropping it.
#[test]
fn delegated_cli_requires_the_os_write_sandbox() {
assert!(
DELEGATE_FIXED_ARGS
.windows(2)
.any(|pair| pair == ["--sandbox", "required"]),
"{DELEGATE_FIXED_ARGS:?}"
);
}
}
7 changes: 6 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,6 @@ pub struct Cli {
pub offline: bool,
#[arg(
long,
hide = true,
value_enum,
value_name = "auto|required|off",
default_value = "auto",
Expand Down Expand Up @@ -1114,6 +1113,12 @@ mod tests {
assert!(help.contains("--quiet"));
}

#[test]
fn help_includes_sandbox() {
let help = Cli::command().render_long_help().to_string();
assert!(help.contains("--sandbox"));
}

#[test]
fn help_hides_completion_contract_json() {
let help = Cli::command().render_long_help().to_string();
Expand Down
Loading
Loading