v1.3.11

安全模型

最後更新


Localhost 綁定


HTTP server 只監聽 loopback 位址 127.0.0.1:17989 與 [::1]:17989 — 區網 client 無法連到 daemon。Web 儀表板內嵌於執行檔,由同一個 daemon 在 / 提供,因此一般情況下瀏覽器來源就是你自己的機器。CORS middleware 只接受 loopback 來源(localhost、127.0.0.1、::1),並以 Access-Control-Allow-Private-Network 回應 Chrome Private Network Access。原本另外接受的兩個託管來源 https://web.agenvoy.com 與 https://agenvoy-board.pardn.workers.dev 已於 v1.0.17 移除 —— 請使用 daemon 在 / 提供的儀表板。
碰到憑證、設定檔或行程狀態的端點,在綁定之外另有 localhostOnly() 守門。它檢查的是連線的 remote address(127.0.0.1 / ::1),不是 Origin header。
CORS 只擋頁面讀取回應,擋不住送出請求,因此 v1.3.11 起 daemon 另外拒絕跨站寫入:POST、PUT、PATCH、DELETE 的 Origin header 若不是 localhost、127.0.0.1 或 ::1,在進入 handler 前即回 403 [Denied] origin: ...(沒有 Origin 的請求,例如 curl,不受影響)。POST /v1/send 與 POST /v1/chat/completions 另要求 Content-Type: application/json,其餘一律回 415,堵住免 preflight 的 text/plain simple request。/v1/send 的 enable_sandbox:false(agen -p --skip-sandbox)只接受 loopback 連線,其他來源回 403。
自 v1.2.0 起 TUI 不再自行執行 agent,而是經 Unix socket ~/.config/agenvoy/daemon.sock 與 daemon 溝通;該 socket 以 0600 權限建立,只有擁有者能連線。

沒有提權模式


不存在 /sudo 指令,也沒有 session 層級的提權。/dangerous 指令已移除;請改用下述的逐次確認或 TUI 的直接操作。兩者皆由逐請求授權取代:越過安全邊界的呼叫只為那一次呼叫升起確認,授權範圍限於該 session 加上該特定路徑。
$HOME 一律可寫,無需設定(auto 下寫入 $HOME 內但在工作目錄外的路徑會詢問一次,見權限與確認)。以下情況落在其外:
情況行為
$HOME 內的敏感路徑boundary.Restricted 收集後升起一般確認(不需密碼),任何權限模式皆然。核准後該路徑授權給該 session
$HOME 之外的路徑boundary.Restricted 收集後升起確認,且要求作業系統密碼,任何權限模式皆然(v1.3.7 之前 auto 會直接拒絕、不跳提示)。核准後該路徑授權給該 session。v1.3.10 起 read_files 與 find_files 讀取可讀清單(內建系統與套件位置加上 readable_path)中的路徑不跳確認
需要寫到 $HOME 之外的指令Agent 在該次 run_command 或 background_command 附上 write_paths;這些路徑要等同一道密碼確認通過後才綁進 sandbox
被沙箱擋下、且 network 與 write_paths 都解決不了的指令自 v1.3.0 起該次呼叫可設定 sandbox: false,在沙箱外以使用者的完整權限執行。這種呼叫一律跳出需要密碼的確認 —— allow 規則、auto 與 allow-all Skill 都不能略過 —— 密碼驗證通過後才執行;確認標示為 outside sandbox: <cmd>
需要 root 權限的指令自 v1.3.2 起該次呼叫傳入 argv ['sudo', '<binary>', ...]。它會發起標示為 sudo: <cmd> 的確認,一律要求系統密碼,任何權限模式(含 allow-all)皆然,allow 規則也不能略過;核准後以 sudo ticket 在沙箱外執行 sudo -n <binary> ...

密碼檢查集中在 internal/sudo:sudo -n -v 探測 sudo ticket 是否仍在快取(上限 3 秒),sudo -S -v 驗證輸入的密碼(上限 30 秒)。sudo ticket 是唯一的計時器——沒有第二層 TTL。ticket 仍在快取時,提示不會要求輸入密碼;以 root 執行的 daemon 則直接略過檢查。
TUI 與本機 web 儀表板可以收集密碼;web 確認端點只接受來自 loopback 連線的密碼。自 v1.2.0 起 TUI 不再自行執行 sudo -v:密碼輸入在 TUI 的密碼欄位,隨回覆送到 daemon,由 daemon 以 sudo -S -v 驗證;確認只送到發起該次執行的 TUI 視窗。無法收集密碼的通道不會提權:自 v1.3.3 起 Telegram 與 Discord 的受限確認不提供 Yes,並引導至本機的 TUI 或 Web UI;遠端 HTTP 核准回 403,儀表板保留確認卡片並顯示錯誤(v1.3.3 之前這類通道可以核准,呼叫會以 skipped 回傳)。自 v1.3.3 起密碼錯誤時提示保留可再輸入,受限確認也不再提供 don't ask again 與 allow this turn。
自 v1.3.2 起 run_command 與 background_command 只接受上述 argv 形式的 sudo:不接受 sudo 旗標、不可巢狀 sudo / su,目標必須是不在 denied_command 上的裸 binary。sh -c 內的 sudo 仍會被拒絕,錯誤訊息會指向 argv 形式。sudo rm 會真的刪除,一般的 rm 則仍移入垃圾桶。pkg_manage 已移除(移除於 v1.3.2):系統套件改走 run_command,Linux 上用 ['sudo', <套件管理器>, ...],macOS 上用 brew 並設定 sandbox: false(brew 絕不搭配 sudo)。
run_command 的 sandbox 約束的是寫入而非讀取,因此指令只有在作業系統本身拒絕時才會在某路徑失敗。檔案工具(read_files、find_files、open_file、file_history)讀取 $HOME 以外的路徑前仍會跳出需要密碼的確認(v1.3.10 起 read_files 與 find_files 讀取可讀清單中的路徑不跳確認),讀取敏感路徑前則跳出一般確認,denied_path 則讀寫一律拒絕。已核准的路徑以 session 為單位存在 daemon 記憶體中,授權持續到 daemon 重啟為止。

永久拒絕


config.json 的 denied_path 是硬性拒絕清單:這些路徑讀寫皆不可,任何提示都無法核准,重試同一路徑會以相同方式失敗。項目必須是絕對路徑或以 ~/ 開頭;檔案系統根目錄會被拒收。denied_command 是其指令側的對應——清單上的 binary 一律拒絕,sh -c 內亦然。

敏感檔案防護


符合敏感集合的檔案——SSH keys、.pem、.key、.env 與憑證檔——讀寫皆受限:read_files、edit_file、remove_file、restore_file、open_file、find_files、file_history 在碰觸前都會升起確認——$HOME 內為一般確認,$HOME 外則需密碼驗證。自 v1.3.7 起此集合另涵蓋 .git 與 .claude 目錄、shell 啟動檔(.bashrc、.bash_profile、.profile、.zshrc、.zprofile、.zshenv、.zlogin)與 .mcp.json,因此用 read_files 讀 .git/config 或 ~/.zshrc 現在會詢問,argv 碰到其中任一項的唯讀白名單指令也不再命中。該集合為 sensitive_path(dirs / files / prefixes / extensions),v1.3.10 起內建於 go-pkg filesystem(先前為 configs/jsons/sensitive_path.json),並以 config.json 中的使用者項目擴充。此防護由 Go 強制,非基於 prompt。

密鑰遮蔽


v1.3.11 起,工具輸出中的密鑰在進入模型、log、action history 或 channel 之前即被遮蔽(internal/utils/secret,規則在 configs/jsons/secret_patterns.json)。偵測只靠格式比對——私鑰區塊、URL 內的憑證、Bearer / Basic / token 值、netrc 密碼,以及 AWS access key、GitHub、GitLab、Slack、Stripe、npm、Hugging Face token 與 LLM API key 等供應商格式——不交給 LLM 判斷是否該遮。
項目說明
格式前 3 字元 + ******** + 後 3 字元;短於 9 字元的值只留前 3 字元,3 字元以內整段變成 ********。PEM 私鑰區塊整段算一個值,不逐行遮蔽
工具結果與錯誤在 toolregister.Dispatch 遮蔽,agent 迴圈與對外 MCP server 共用
串流進度經 secret.Stream 逐 chunk 遮蔽(runToolExec 與 run_command 的 progress writer);跨 chunk 的私鑰區塊整段丟棄
背景報告存入前在 FinishBackground / UpdateBackground 遮蔽
寫回遮罩值edit_file 的 content / new_string 與 run_command 的 argv 含本 daemon 產生過的遮罩值時,以 [Invalid] 拒絕,避免遮罩值覆蓋真正的密鑰。只記遮罩後的字串,不記原文

需要金鑰的指令在同一個 run_command 內讀取並使用,例如 ['sh', '-c', 'set -a; . ./.env; set +a; curl ...'] 或 security find-generic-password,讓值不經過工具結果。
EN