Sandbox
每次 run_command、script 工具與 test_tool 的執行都由 go-pkg/sandbox 包裹:
| 平台 | 機制 |
|---|---|
| Linux | bubblewrap(bwrap) |
| macOS | sandbox-exec |
Sandbox 限制:不可特權執行、受限的檔案系統寫入範圍、可配置的網路存取、可配置的 CPU/記憶體上限。
三個呼叫者,單一進入點
Sandbox 恰有三個呼叫者,全部直接呼叫 sandbox.Wrap(ctx, binary, args, workDir, opt):
run_command—— 任意使用者發出的命令(internal/tools/runCommand.go)toolAdapter/script/execute—— script 工具擴充(script_*、ext_*)test_tool—— script 工具script.py的試跑
呼叫者與 sandbox.Wrap 之間沒有 wrapper 層。新增行為(例如新的資源上限)意味著貢獻至 go-pkg/sandbox,而非在 agenvoy 中加 shim。
Policy 注入
Policy 檔案編譯進 binary,並於啟動時與 ~/.config/agenvoy/config.json 中對應的鍵合併(使用者項目是追加,永不取代預設):
| 檔案 | config.json 鍵 | 用途 |
|---|---|---|
configs/jsons/denied_map.json |
denied_map |
Sandbox 拒絕暴露的路徑 |
configs/jsons/exclude_list.json |
— | 從列目錄 / 走訪 / 搜尋中排除的路徑 |
configs/jsons/white_list.json |
white_list |
run_command 可執行的 binary |
configs/jsons/read_only_command.json |
read_only_command |
跳過 confirm gate 的指令 |
configs/jsons/net_white_list.json |
net_white_list |
豁免 SSRF 防護的 host |
filesystem.LoadRuntime() 一次性注入合併後的 policy:
sandbox.New(DeniedMapBytes)
filesystem.New(Policy{DeniedMap: DeniedMapBytes, ExcludeList: configs.ExcludeList})
go-pkg/sandbox 與 go-pkg/filesystem 都會自動強制 IsDenied——呼叫端無需檢查。
檔案系統寫入 guard
go-pkg/filesystem 的寫入 API(WriteFile、WriteJSON、AppendText、CheckDir)全部在內部強制 IsDenied。任何繞過 go-pkg/filesystem、直接透過 os.WriteFile 寫入的 agenvoy code 都會逃脫 policy — 這是禁止的。
internal/filesystem package 僅保留路徑計算與 domain wrapper(例如 MCPPath、MCPSessionPath)。它不重複 read/write 邏輯。
子程序 argv-only schema
run_command 只接受 argv: string[](minItems 1)。它不接受帶自動 tokenize 的 command: string。此零解析做法移除了 agent 層的 shell-injection surface。
Shell 功能(pipe、redirect)需明確發出 ["sh", "-c", "cmd | pipe"]。該腳本以 mvdan.cc/sh 解析而非字串比對,且其中每個命令節點都會被檢查:
| 規則 | 效果 |
|---|---|
| 只接受裸指令名 | /usr/bin/curl 會被拒絕——binary 必須寫成 curl 且列於 allowlist |
| 不接受動態指令 | 由變數或命令替換組出的指令一律拒絕 |
sh -c 內永不允許 rm |
無論 allowlist 狀態一律拒絕 |
| Shell 內建指令 | 固定集合(cd、echo、test、export …)無需 allowlist 條目即可通過 |
巢狀 sh -c |
以相同規則遞迴驗證 |
Timeout
每個工具都有自己的 timeout,預設 1 分鐘,並可在註冊時逐一覆寫(fetch_page 與 search_web 90 秒、transcribe_media 5 分鐘、download_file 10 分鐘)。Subagent 呼叫含等待空位時間,由 MaxSubagentTimeoutMin(30 分鐘)設限。
這些皆為套件層級的值——過去用來控制它們的環境變數覆寫已移除。