# 執行防護

與沙箱並行的寫入防護、argv-only 子程序 schema 與 timeout。

## 檔案系統寫入 guard

`go-pkg/filesystem` 的寫入 API（`WriteFile`、`WriteText`、`WriteJSON`、`AppendText`、`Copy`、`Move`、`Remove`）全部在內部強制 `IsDenied`；`CheckDir` 只負責建立目錄，不做此檢查。任何繞過 `go-pkg/filesystem`、直接透過 `os.WriteFile` 寫入的 agenvoy code 都會**逃脫 policy** — 這是禁止的。

`internal/filesystem` package 僅保留路徑計算、執行期上限與 domain wrapper。它不重複 read/write 邏輯。

在 `$HOME` 之外，`run_command` 與 `background_command` 必須事先宣告路徑：該次呼叫帶上 `write_paths`（僅限絕對路徑），使用者以系統密碼核准後，這些路徑才會為該 session 以讀寫方式綁進 sandbox。寫入未核准的路徑會得到明確訊息；對未綁定的 `$HOME` 外路徑出現權限錯誤時，會附上改帶 `write_paths` 重跑的提示，而非讓模型把裸錯誤誤判成擁有者問題。自 v1.3.2 起，沙箱內的指令因權限標記（`permission denied`、`operation not permitted`、`read-only file system`、`EACCES`、`not writable`）失敗、且輸出中沒有可經 `write_paths` 授權的 `$HOME` 外路徑時，結果附上的建議把離開沙箱視為最後手段，而非預設的重試方式（v1.3.11 起；v1.3.2 至 v1.3.10 是要求 agent 立即以 `sandbox: false` 重跑）：連線或認證失敗（如 `Permission denied (publickey)`、`could not resolve host`）→ 改用 `network: true`，或回報憑證問題；寫入 → 改用工作目錄下的路徑，或為失敗的路徑帶 `write_paths`；確實需要系統 socket、裝置或服務，或目標為 root 擁有 → `sandbox: false` 或 argv `['sudo', ...]`，由使用者以系統密碼核准；其餘情況回報被擋下的內容後停止。

## 子程序 argv-only schema

`run_command` 只接受 `argv: string[]`（minItems 1）與選填的 `write_paths`、自 v1.0.25 起的 `network`（布林值，預設關閉），以及自 v1.3.0 起的 `sandbox`（布林值，預設 true；`false` 在密碼驗證後於沙箱外執行，並忽略 `network` / `write_paths`）。`background_command` 在同一組參數上另加 `timeout_min`（1-60，預設 60）與 `keywords`（輸出監看要比對的錯誤關鍵字）；`run_command_background` 更名為 `background_command`，其 `interval` 與 `prompt` 參數已移除（移除於 v1.3.2）。它**不**接受帶自動 tokenize 的 `command: string`。此零解析做法移除了 agent 層的 shell-injection surface。

在最外層，自 v1.3.2 起接受 `['sudo', '<binary>', ...]`：不接受 sudo 旗標、不可巢狀 `sudo` / `su`，目標必須是不在 `denied_command` 上的裸 binary；它會發起一律要求系統密碼的 `sudo: <cmd>` 確認，核准後在沙箱外以 `sudo -n <binary> ...` 執行。v1.3.11 起，取消這類呼叫（前景或 `background_command`）時改送 `SIGTERM` 給 `sudo`，由它轉送給 root 指令，而不是對 process group 送 `SIGKILL`（後者殺不到 root 子行程）；超過 2 秒的 `WaitDelay` 仍未結束才會 kill `sudo`。`rm` 轉為移入垃圾桶（`sudo rm` 則真的刪除），其目標依路徑分級處理——工作目錄內於 `auto` 下直接執行，`$HOME` 內其他位置會詢問，`$HOME` 外需系統密碼——`cd` 也會被拒絕，因為工作目錄改由使用者在 TUI 設定；原本會切換工作目錄的 agent `cd` 已移除於 v1.3.0。

Shell 功能（pipe、redirect）需明確發出 `["sh", "-c", "cmd | pipe"]`（或 `bash -c`）。該腳本以 `mvdan.cc/sh` **解析**而非字串比對，且其中每個命令節點都會被檢查：

| 規則 | 效果 |
|---|---|
| 只接受裸指令名 | `/usr/bin/curl` 會被拒絕——binary 必須寫成 `curl` |
| 不接受動態指令 | 由變數或命令替換組出的指令一律拒絕 |
| 不接受 `rm` 與 `sudo` | 兩者在 `sh -c` 內一律拒絕；`sudo` 的錯誤會指向 argv 形式 `['sudo', '<binary>', ...]` |
| 拒絕清單上的 binary | 列於 `denied_command` 者一律拒絕，`sh -c` 內亦然 |
| Shell 內建指令 | 固定集合（`cd`、`echo`、`test`、`export` ...）無需 allowlist 條目即可通過 |
| 巢狀 `sh -c` | 以相同規則遞迴驗證；非靜態字串的巢狀腳本一律拒絕 |

## Timeout

每個工具都有自己的 timeout，預設 15 分鐘（`DEFAULT_TOOL_TIMEOUT`；v1.1.2 前為 1 分鐘），並可在註冊時逐一覆寫（`open_file` 10 秒、`html_template` 30 秒、`fetch_page` 與 `search_web` 90 秒、`generate_audio` 5 分鐘、`download_file` 10 分鐘、`generate_image` 15 分鐘、`run_command` 30 分鐘；`ask_user` 無上限）。背景執行則改由自身參數設限：`background_command` 與 `background_subagents` 的 `timeout_min` 最多 60 分鐘（預設 60）；`run_websocket` 與其 24 小時的 `timeout` 已移除（移除於 v1.3.2）。Subagent 呼叫含等待空位時間，由 `MAX_SUBAGENT_TIMEOUT_MIN`（30 分鐘）設限。

這些皆為套件層級的值——過去用來控制它們的環境變數覆寫已移除。
