⚠️ 歷史紀錄:本報告描述的問題已於 2026-11-07 全部修正並合回 Agent 主線(修正卡 CM-3559~3564、3573~3576、3584,驗收紀錄見母卡 CM-3507)。文中的檔名與行號是掃描當下(commit
bab2aa3)的版本,修正後已變動,請勿拿來定位現行程式。
這一棒沒有要立刻處理的問題。 工具找到 0 個問題,「重點看什麼」7 項我逐條人工查過,也沒有成立的漏洞。但這不等於這塊完全乾淨:工具一條候選都沒提出,檢查員面板(三個獨立檢查員投票的那一關)沒東西可投。另外有 3 件「設計上就信任主系統」的事,寫在文末「留意事項」,給決策者知道邊界在哪,不算漏洞。
cloud_http.py:TLS 驗證(確認對方真的是主系統的那道檢查)、逾時、DPoP 標頭(每個請求現簽的「持有私鑰證明」,綁這次的網址,防止被重送);cloud_channel.py:派工回報、上傳、串流下載都走這條;config_pull.py:Agent 定期向主系統拉設定,逐類交給「套用者」(applier,負責把某一類設定寫進本機);agent_status.py:Agent 回報給主系統的狀態欄位;tool_schemas.py、顯示名 agent_name.py、下載加密開關 file_protect_sync.py、金鑰頁 keys_view.py、主控台票券入口 console_ticket.py;sync/__init__.py。範圍共 10 檔、約 1006 行。為了查證呼叫端,我有翻範圍外的 sync/app/enroll.py、identity/__init__.py、console/api/__init__.py、scan/app/secret_form.py、platform/ops.py、jedi_agent_protocol/events.py。這些檔只為查證而讀,不當作發現。
| # | 這是什麼問題 | 出事會怎樣 | 要先有什麼才打得到 | 在哪裡 | 怎麼修 | 嚴重度 |
|---|---|---|---|---|---|---|
| — | 工具沒有提出任何候選問題 | — | — | — | — | — |
沒有 HIGH/CRITICAL,所以不需要逐條開檔核對;「重點看什麼」的人工查證見下一節。
cloud_http.py:能不能關掉驗證、逾時多少、會不會跟著跳轉、DPoP 綁什麼
request(:49-55)和 stream(:71-77)建 httpx.Client 時,verify 寫死為 cloud_tls_verify(cfg)。呼叫端多帶的 **kwargs 只會傳給 client.request(...),而 httpx 的 request 不收 verify,所以沒有任何參數能覆寫驗證。request 預設 30 秒;stream 規定呼叫端一定要給(cloud_channel.get_stream 給的是 connect 30/read 120 秒)。httpx.Client 預設 follow_redirects=False。全 repo grep 過,範圍內四個呼叫點(config_pull.py:140、enroll.py:116、cloud_channel.py:67、:105)都沒有開它。3xx 不會被跟著導到 http 或別的主機。auth_headers(method, url, ...) 的 url 是完整網址 cfg.cloud_endpoint + path(:52、:73),不只是 path。query 另外交給 httpx 接上,不進簽章範圍,這是刻意設計(cloud_channel.py:63 有註明)。request 的 **kwargs 理論上可以帶 follow_redirects=True,目前沒有任何呼叫端這麼做。config_pull.py:主系統能遠端改哪些設定
enroll.py:252-262,identity 4 類在 identity/__init__.py:56-59。allowed_targets/allowed_target_names:決定 Agent 能連誰(B6a 在看);console_allow_cidr:只能收窄(A7 已核);offline_operators、offline_login_days:離線期間誰能登入、能登幾天(A7);ldap_config:LDAP 連線設定;trust_anchors:Agent 信任哪幾把主系統公鑰(cloud_trust.apply_trust_anchors,範圍外);file_protect:見第 6 項。error_tracking、vulndb、profiles、tool_schemas、agent_name 不直接開門。cloud_endpoint(只從 env CLOUD_ENDPOINT 讀,config/config.py:167)和 CIDR 安裝底線。:109、:121-134)。一類失敗只記 log、不記那一類的版本,下次重給;其餘類照樣套用。所以不是整批回滾,而是「一類一類各自生效」。檔頭有寫明,這是 SR-04.6 要求的「各 observer 互不影響」。versions 只是 Agent 告訴主系統「我手上有哪版」,Agent 不檢查新收到的版本有沒有比較新。但要回放,得先能冒充主系統的 TLS 回應,而通道有 TLS 驗證加 DPoP。主系統本身若被攻下,本來就能直接下新設定,不需要回放。agent_status.py:心跳送出哪些狀態欄位,有沒有洩漏內網拓撲
AGENT_STATUS_KEYS 限制(jedi_agent_protocol/events.py:39-43),契約外的欄位在 snapshot()(:36-42)會被丟掉。vulndb_date、credential_store、configured_tools、storage、browser_port、outbox_size、local_admin_set、local_admin_password_set_at、ldap_source、sync_state、last_restart_reason、console_allow_cidr_installed、name、name_updated_at。name 只在使用者從主控台改過名、還沒回報時才帶(agent_name.py:88-93),不會自動送出主機名。storage、console_allow_cidr_installed 的內容由範圍外的模組產生,沒有逐支追到底。依欄位名稱推,分別是儲存統計與安裝時設定的 CIDR 白名單,後者本來就是主系統要知道的設定值。storage 欄的實際內容留給 files 模組那一棒確認)。tool_schemas.py:工具表單定義有沒有簽章、default/options 會不會被當成可信值
apply(:69-77)只檢查形狀(是 dict、tools 是 list),就原子寫進檔案。scan/app/secret_form.py(範圍外):
options 只用來限制下拉選單能選的值(:65-66);default 在使用者沒填、而且不是祕密欄時,當成欄位值存起來(:89-90)。也就是說,主系統給的 default 會一路流到 connector 的參數。keys_view.py:38:不驗簽解通行證,會不會把 token 本體回給主控台
_pass 只回 not_after(從 exp 換算)和 issuer(iss),不回 token 原文。build 回的欄位:
certificate:只有到期日與換證狀態;trust_anchors:ring.describe(),依檔頭寫法只有 kid/用途/指紋;enrollment:token_configured 是布林值,不是 token 本身;main_system:主系統網址。console_ticket.py、file_protect_sync.py
console_ticket 整支模組直接別名到 agent_auth。呼叫端 console/api/__init__.py:106-108 傳的 agent_uid 是 state.agent_uid,也就是本機註冊狀態,不是從請求讀來的,攻擊者控制不了。{"enabled": false},主控台下載就改給明文(:49-66)。這是 FR-169 D6 的設計:開關本來就由主系統決定,會記 log「關閉(主控台下載改給明文)」。:44-46),不會因為格式錯誤而誤關。sync/__init__.py:模組註冊、services() 持有什麼、背景元件誰起停
SyncServices 只持有 4 把鎖(:19-30),沒有帳密或其他狀態。config_pull.COMPONENT 和 agent_status.COMPONENT 在 enroll.py:376-380 接好設定、交進元件清單,由 platform 統一呼叫 start()/stop()。ConfigPull 自己開的保底計時器 thread,會在 stop() 裡收掉(config_pull.py:72-77)。clear_tenant_files(:113-119)用的路徑來自 env AGENT_CERT_DIR 加固定檔名,外部輸入控制不了。這三件都屬於「主系統是信任根」的既定設計。列出來是讓決策者清楚:主系統一旦被攻下,影響會放大到哪裡。
cloud_endpoint 和 CIDR 底線,而且 CIDR 只能收窄。其中「信任公鑰也能由主系統下發」最值得注意:主系統能換掉 Agent 信任的簽章鑰。這部分在範圍外的 cloud_trust.py,建議下一期專門看。default 值會流進 connector 的參數。第三期掃 connectors 時要對照:connector 有沒有把參數放進參數陣列、有沒有拼成字串。| 項目 | 值 |
|---|---|
| run ID | wf_8e2e3ef2-e8f |
| 掃描版本 | 63334f464215c18fa89c334b360a86f31679c1f8(工作目錄乾淨) |
| 強度與範圍 | low,10 檔 |
| verification.status | verified(reason_kind 為 null) |
| candidates(候選問題) | 0:沒東西可投,不等於乾淨 |
| panel_votes(檢查員投票) | 0 |
| researchers_dispatched / returned(派出/回來的研究員) | 1 / 1 |
| 耗時 | 約 7.6 分鐘,研究員約 8.4 萬 token |
| 原始產物 | docs/security-scan/FR-177/runs/CLAUDE-SECURITY-20261105-174550/ |