中文導讀(編輯整理)
兩位審查者都在 hosted 頁面遭入侵的威脅模型下,指出原生確認流程的接收端綁定疑慮。這是靜態檢視發現,尚未重現攻擊;審查者也肯定來源檢查與本機 token 隔離。
這是另一次針對原生橋的專項檢視,題目為中文,不屬於上方 16 份英文題目的比較。
完整中文原始回覆
我看完了這個 commit 的原生配對橋(shell/darwin/Pairing.swift、PairingDialogs.swift、CloudPairing.swift、Browser.swift)以及它呼叫的 daemon 端程式。結論是有一個重要問題要處理,另有幾個中低風險。WebView 的 origin 檢查本身寫得不錯。
先說明限制:我沒有 clone repo,是透過網頁抓取工具逐段讀程式。下面的結論都有對照原始碼片段,但 control plane 伺服器端、agent 啟動參數、requireLocal 的實作和 daemon 監聽的位址,我都沒看到。
1. 【高】CloudPairLocal 可以把帳號主金鑰交給任何 offer
流程是:Cloud 頁面呼叫 CloudPairLocal,原生端跳出確認框,使用者按同意後 POST 到 /v1/cloud/pairing/offer,進入 internal/transport/cloud/pairing.go 的 Complete(),再由 deliver() 把資料封給 offer 裡的金鑰。
Complete()送出前只檢查三件事:offer 能 decode、還沒過期、offer.AccountID == p.AccountID。- offer 沒有簽章。
internal/domain/cloud/pairing.go只確認指紋和 offer 內的公鑰一致,註解自己也寫了 “The fingerprint is not authority”。 - 不會檢查 offer 是否來自本機發出、還在等待中的邀請,也不會用
OpenEncryptedOffer解開邀請密鑰。這兩個檢查只存在於另一條answer()路徑。 deliver()封裝的PairingHandover含有MasterSecret(帳號主金鑰或 content key)。- 確認框只顯示 daemon 回報的機器名稱,沒有任何可以讓兩台裝置互相比對的碼或指紋。相比之下,
IdentityPairing路徑有六位數比對碼。
影響: 如果 app.clawdline.com 被入侵、遭供應鏈污染,或營運方本身惡意,只要讓使用者在對話框按一次同意,就能拿到可以解密所有機器內容的金鑰。README 說「Cloud 只轉送密文」,這條路徑讓這個 E2E 承諾變成取決於那一次點擊。
建議: 貼上路徑也要求本機有對應的待處理邀請,或驗證邀請密鑰。確認框加上 offer 指紋或比對碼,並要求使用者在另一台裝置上核對。
2. 【中】CloudPairAgent 一鍵啟動 full 權限的 AI 助手
- 核准後,daemon(
cloud_agent.go呼叫orchestrator/detached_pairing.go)會啟動一個"permission_mode": "full"的助手。指令範本說明沒有人在看,它會執行clawdline cloud pair -offer '%[3]s'。 - Shell injection 目前擋得住。
DecodeCanonicalBase64URL對每個字元做[A-Za-z0-9_-]白名單檢查,還會重新編碼比對,所以單引號進不去。但這個保護完全靠上游,DispatchPairingAgent自己只檢查非空。建議改用環境變數或檔案傳遞 offer,或在 dispatch 時再驗證一次。 - Prompt injection 的空間有限,但仍然存在。
machine_name由頁面提供,經過清理,最多 64 字元並做 JSON 引號處理,但還是會被放進 full 權限 agent 讀的 prompt。 - 確認框顯示 offer 的前 12 個字元,使用者其實無法用它判斷任何事。建議改顯示 daemon 驗證過的資訊。
3. 【中低】本機配對對話框
- 預設按鈕給最高權限。
PairingDialogs.swift的chooseAlert第一個按鈕是「讀取並操作」,按 Return 就會直接授予。建議預設改成「唯讀」或「忽略」。 - 裝置名稱沒有清理。
notice.name在 Swift 端沒有長度限制,也沒有過濾控制字元或 bidi 字元,會直接進入對話框標題和shellLog。CLI 的clawdline pair(cmd/clawdline/auth.go)也會原樣印到終端機,可能被 ANSI escape 注入或誤導。 - 每次彈窗都會執行
NSApp.activate(ignoringOtherApps: true)。我沒看到有限速,可能被用來反覆搶焦點。
4. 【低】其他
CLAWDLINE_NEXT_CLOUD環境變數可以把受信任的 Cloud origin 改成任意 http(s) 位址,release build 也沒有擋。前提是攻擊者已經能設定這個 App 的環境變數。CloudPairLocal的status不需要確認就會回傳machine_id。Cloud 本來就知道這個值,影響可以忽略。PairingPermissionClient只把/從允許字元中移除,..不會被編碼。pairing_id來自本機 daemon,所以風險很低。
做得好的部分
- 兩個 handler 只註冊在 Cloud WebView 上。每次收到訊息都會檢查
isMainFrame、securityOrigin的 scheme、host、port,以及webView.url,subframe 和其他 origin 都會被拒絕。 - 確認框關閉後會重新檢查 origin,防止期間頁面被換掉。
- local token 用 HttpOnly、
SameSite=Strict的 cookie 注入,不出現在 URL 或頁面 script 中,log 裡也沒有 token 或 offer。 - 設定寫入只接受本機 token,已配對的瀏覽器會收到 403。
建議優先處理第 1 點。 可以這樣驗證:用自己帳號的 AccountID,在沒有發出邀請的情況下產生一個 offer,看 /v1/cloud/pairing/offer 會不會成功交出主金鑰。也要確認 control plane 的 CompletePairing 有沒有檢查 PairingID 屬於這台機器,不過如果 Cloud 本身是威脅來源,這個伺服器端檢查也沒有保護作用。