# Sandbox

每次 `run_command` / `background_command`、script 工具與 `test_tool` 的執行都由 `go-pkg/sandbox` 包裹：

| 平台 | 機制 |
|---|---|
| Linux | bubblewrap（`bwrap`） |
| macOS | `sandbox-exec` |

Sandbox 限制：不可特權執行、受限的檔案系統寫入範圍、逐次開放的網路存取、可配置的 CPU/記憶體上限。

自 v1.0.25 起 `run_command` 預設在網路關閉（`NetworkDeny`）下執行，除非該次呼叫設定 `network: true` —— 此時僅該次呼叫改為 `NetworkAllow`；連網呼叫永不視為唯讀，因此 `safe` 下需確認，`auto` 下不跳提示直接執行；v1.3.1 起的背景版本規則相同（`run_command_background` 更名為 `background_command`，移除於 v1.3.2）。自 v1.3.0 起 `sandbox: false` 會傳入 `AllowAll`，該次呼叫完全不經沙箱，但須先通過使用者的系統密碼驗證。自 v1.3.2 起 argv `['sudo', '<binary>', ...]` 也在沙箱外執行：其需要密碼的 `sudo: <cmd>` 確認核准後，以 sudo ticket 執行 `sudo -n <binary> ...`。Script 工具（`script_*`、`ext_*`）與 `test_tool` 以不帶 option 的方式呼叫 `sandbox.Wrap`，因此仍保有網路存取。v1.0.25 開發期間短暫存在的連網 `run_script` 工具從未進入正式版本；請改用帶 `network: true` 的 `run_command`。

## 三個呼叫者，單一進入點

Sandbox 恰有三條呼叫路徑，全部直接呼叫 `sandbox.Wrap(ctx, binary, args, workDir, opt)`：

1. `run_command` 與 `background_command` —— 任意使用者發出的命令，共用同一條 `runCommand` 路徑（`internal/tools/runCommand.go`、`runCommandBackground.go`）
2. `toolAdapter/script` 的 execute —— script 工具擴充（`script_*`、`ext_*`）
3. `test_tool` —— script 工具 `script.py` 的試跑（上限 60 秒）

自 v1.3.0 起，指令工具與 `test_tool` 以 `sandbox.Run`（go-pkg v0.13.17）啟動包裹後的行程，指令結束後會終止整個 process group；script adapter 則自行啟動並等待行程。呼叫者與 `sandbox.Wrap` 之間沒有 wrapper 層。新增行為（例如新的資源上限）意味著貢獻至 `go-pkg/sandbox`，而非在 agenvoy 中加 shim。

## Policy 注入

`filesystem.LoadRuntime()` 在啟動時讀取 `~/.config/agenvoy/config.json`，並把合併後的 policy 交給 `go-pkg/sandbox` 與 `go-pkg/filesystem`。部分 policy 有編譯進執行檔的預設值並與使用者項目合併，其餘僅存在於使用者設定。

| Policy | 內嵌預設 | config.json 鍵 | 用途 |
|---|---|---|---|
| 敏感路徑 | go-pkg `filesystem/jsons/sensitive_path.json`（v1.3.10 前為 `configs/jsons/sensitive_path.json`） | `sensitive_path` | 存放憑證、金鑰、shell 啟動檔或工具設定的路徑（v1.3.7 起另含 `.git`、`.claude`、`.bashrc`／`.zshrc` 等 shell rc 檔與 `.mcp.json`），讀寫皆適用——需經 per-session 授權才能存取：`$HOME` 內為一般確認，`$HOME` 外需系統密碼。分四類：`dirs`、`files`、`prefixes`、`extensions` |
| 排除清單 | `configs/jsons/exclude_list.json` | — | 從列目錄 / 走訪 / 搜尋中排除的路徑 |
| 唯讀指令 | `configs/jsons/read_only_command.json`（v1.3.3 起 532 筆；`cd` 於 v1.3.0 移出後為 409 筆） | `read_only_command` | 跳過 confirm gate 的指令。自 v1.0.25 起，`run_command`（v1.3.1 起含背景版本，現為 `background_command`）的執行檔本身、執行檔加第一個參數、或執行檔加前兩個參數任一在清單上即算命中（`git` 的全域旗標如 `-C <dir>` 會先略過）；任一參數為敏感路徑，或設定 `network: true` 時取消命中 |
| 拒絕指令 | — | `denied_command` | `run_command` 一律拒絕的 binary，`sh -c` 內亦然 |
| 拒絕路徑 | — | `denied_path` | 讀寫皆永久禁止的路徑；任何提示都無法核准。項目必須是絕對路徑或以 `~/` 開頭，拒收根目錄，symlink 的實際目標也一併拒絕 |
| 可讀路徑 | go-pkg `filesystem/jsons/readable_path.json` | `readable_path` | v1.3.10 起。`read_files`／`find_files` 免確認讀取的 `$HOME` 外位置（系統與套件目錄，如 `/usr`、`/etc`、`/opt`、`/Library`）；`run_command` argv 中存在、在 `$HOME` 外且不在清單內的絕對路徑會取消唯讀白名單放行 |
| SSRF 豁免 | — | `net_white_list` | 豁免 `http_request` SSRF 防護的 host |

指令政策為**拒絕清單**。不存在 binary 允許清單：`white_list` 與 `path_white_list` 已移除，`sensitive_map` 已更名為 `sensitive_path`；設定檔若仍留有這些鍵會被靜默忽略（啟動警告已於 v1.1.2 移除）。`$HOME` 以外的路徑存取是透過確認提示逐 session 授權，不是靠設定檔清單。

`denied_path` 會作為 denied map 交給 `go-pkg/sandbox`；agenvoy 端則由 `boundary.Resolve` 與 `boundary.WriteBinds` 在任何檔案工具或 `write_paths` 綁定前先行檢查。
