FR-077 · 需求索引 · 本頁由 build 掃資料夾生成

FR-077 遠端 Agent 控制鏈資安掃描

狀態:⏸ 暫停(決策者 2026-09-08 裁,改從其他套件開始掃)。R1 掃完 7 條(1C 2H 3M 1L)已驗收;其餘四棒與 4 張修正卡卡都開好、範圍寫死,隨時可續 文件 1 份

用 Claude Code 的 claude-security plugin 掃「遠端 Agent 控制鏈」,找資安問題。 只掃不修——產出是問題清單,修正另外開卡。


⏸ 本 arc 已暫停(2026-09-08)

決策者裁定:FR-077 暫停,改從其他 jedi- 套件開始掃。*

理由:R1 雖然找到 7 個真問題,但掃描工具的驗證面板因額度用盡全滅,工具的正式產出是空的、覆蓋率無法宣稱,靠人工補核才有東西——這樣的產出無法用來證明「這個套件檢查過了」。與其在同一個套件上反覆試工具極限,不如先掃風險更高、範圍天然較小的套件。

已完成的部分(R1 的 7 條發現)依然有效——每條都經 runner 逐條開檔核對+首腦複核關鍵條,修正卡是照它開的,依據紮實。不可信的是「只有這七條」。

要續行時:其餘四棒(R1b 10 檔/R2a 18 檔/R2b 13 檔/R3 17 檔)的卡都開好了、範圍與重點都寫死在卡裡,檔數已按新尺(每棒 ≤15 檔)調過,隨時可派。四張修正卡(CM-1595~1598)同樣待派——其中 CM-1595 是 CRITICAL,首腦建議不該跟著 arc 一起停

交接與全套件排名見 security-scan-STATE.md


🔴 一頁看完:R1 掃到什麼

第一階段(R1,42 個檔)掃完了,找到 7 個問題。其餘四棒(R1b/R2a/R2b/R3)已開卡但暫停。

§1

最重要的一件事

我們發給客戶、裝在他們機房的 agent,跟雲端之間的四條通訊管道,完全不檢查對方是誰。

程式碼裡寫著「不用檢查,因為外層 nginx 會驗客戶端憑證」——但出貨的 nginx 設定根本沒有那一行(三個設定檔全部找不到)。所以實際情況是:你在請求裡自己寫「我是哪台 agent」,系統就信你

最嚴重的後果:打一個 HTTP 請求過去,系統會回你那台 agent 的待辦工作,裡面包含解密後的明文帳號密碼。而且那不是我們產品的密碼,是客戶自己機器的密碼(客戶的 SonarQube token、客戶伺服器的 SSH/WinRM 帳密)。

§2

七個問題一覽

# 嚴重度 白話:這是什麼問題 出事會怎樣 要先有什麼才打得到 修正卡
F1 🔴 最嚴重 「報到」這條管道不檢查身分,你說你是哪台,系統就信 拿走客戶自己機器的帳號密碼(SonarQube token、SSH/WinRM 帳密) 只要連得到 API,不用帳號密碼;再知道任一台 agent 的編號(一般員工在畫面上就看得到) CM-1595
F3 🟠 嚴重 同一條管道,連「這台 agent 是不是我們家的」都沒查 A 公司能拿走 B 公司的帳密,多租戶隔離在這條路上等於不存在 同 F1,再知道別家一台 agent 的編號 CM-1595
F2 🟠 嚴重 「回報工作做完了」這條管道,不檢查這份工作是不是你的 可以偽造或抹掉稽核證據——把沒做過的掃描標成「通過」,或把真的發現問題的結果蓋掉。客戶拿這些紀錄去應付外部稽核 只要連得到 API,不用帳號密碼;再知道一個任務編號 CM-1595
F5 🟡 中等 同上,只是換個角度看的同一個洞 同 F2 同 F2 CM-1595(併)
F4 🟡 中等 管理頁「測試連線」按鈕,填什麼網址就打什麼 可以拿我們的伺服器當跳板去掃客戶內網,回傳結果還會告訴他哪台通、哪台不通 需要管理員帳號(所以比上面低一級) CM-1596
F6 🟡 中等 安裝用的通行碼永久有效、整間公司共用一組;拿著它就能宣稱「我是那台機器」把別台搶過來 把別台 agent 的連線位址改成攻擊者的機器,之後雲端要資料就送到他那裡 拿到該公司的安裝通行碼(裝在他們每一台機器上)+知道另一台的編號 CM-1597
F7 ⚪ 低 有個設定檔(.env)不小心進了版控 目前裡面只有 localhost 預設值、沒有真實密碼。風險是以後有人填了真的密碼會靜默外洩 不適用(現在還沒東西可洩) CM-1598

越界說明:F7 的檔案不在這一棒的掃描範圍內(掃描工具為了追脈絡讀到隔壁套件)。事實成立,但要記得它不是本棒範圍的產出。

§3

決策者已經裁了什麼(2026-09-08)

  • F1 升到「最嚴重」(原本 runner 判「嚴重」)。理由:不用帳號密碼就打得到 + 能跨公司 + 洩漏的是客戶自己的密碼,三個條件全中。
  • F1/F2/F3/F5 併成一張卡(CM-1595)修——它們是同一個毛病,修法也是同一套。
  • nginx 那條設定併進同一張卡,不另開。因為光補 nginx 沒用:nginx 只驗「這是不是我們發的憑證」,驗不出「你是不是你說的那台」。
  • 要補掃一小棒(CM-1599):這次八個重點只碰到一個,其中「簽憑證」「簽 token」兩塊是加密核心,掃出來零問題——但複審沒跑,分不出是「看過沒問題」還是「根本沒看」。
  • R2/R3 現在就派
§4

這份結果可信嗎?

可以拿來當修正依據,但不能拿來說「其他地方都乾淨」。

掃描工具的「三人複審」這次全部因為額度用完沒跑成(105 個複審 agent 全滅)。少掉的是「幫忙抓誤報」這一環。

但這七條的複審由人做了兩遍:runner 逐條開檔案核對行號,首腦(本 session)再親自核 F1/F2/F3 的行號、自己 grep 三個 repo 確認 nginx 真的沒有那行設定、追明文密碼的流向。每一條都是「打開檔案,那幾行就是那樣寫的」——這是事實陳述不是推論,不需要投票來確認它存在。

不可信的是「只有這七條」:複審沒跑,加上研究員只有兩個,範圍內漏掉的機率比正常一棒高。所以才要補掃 R1b。


§5

為什麼要做

客戶端機房裡跑著我們發出去的 agent,它會主動撥回雲端要工作、下載檔案、回報結果。 這條鏈從來沒有被系統性掃過:security-scan-2607 只掃主專案 BE 與 FE、FR-075 掃 jedi-common 與 jedi-iam、FR-076 掃 License 鏈。Agent 控制鏈是完全空白。

這條鏈的風險形狀跟前兩個 arc 都不同:

  • 控制面四個端點刻意不掛任何 user 認證/agents/register/agents/heartbeat/agents/tasks/<uid>/ack/agents/tasks/<uid>/result)。這是設計,不是疏漏—— agent 不帶 user JWT,身分靠 mTLS 在上游 nginx 終結。但BE 自己拿不到 client 憑證, 所以「是哪一台 agent」全靠 payload 或 header 自報。自報的東西能不能被冒充,是本次的核心問題。
  • agent 是裝在客戶機房的東西。它拿得到 enroll token、拿得到雲端簽的憑證、拿得到 JWT 公鑰。攻擊者若能拿到一台 agent(或它的 token),能做到什麼程度,要盤清楚。
  • 這條鏈會下載檔案GET /agents/files/<uid> 回的是源碼壓縮包 binary 串流, 授權判定在 jedi-detection 的 resolver 清單裡。
§6

產品時程脈絡

第一個對外版本是落地版(Host),SaaS 版機制預留。裁決修正優先序時用 docs/claude/memory/project_first_release_is_host_saas_reserved.md 的判準—— 落地版打得到的優先。Agent 鏈本身就是落地版的核心組件(客戶機房裝 agent), 故本 arc 的發現原則上都算「落地版打得到」。

§7

怎麼拆(三棒)

這條鏈被 repo 邊界切成三段,掃描的 scanRoot 只能指一個目錄,故跨 repo 無法同一棒。

範圍 scanRoot 檔數
R1 agent 身分與註冊 jedi-remote-agent 的 agent_auth + remote_agent 全鏈 + 三支管理 app service + route + plugin.py jedi monorepo 42
R2a 任務下發與狀態機 jedi-remote-agent 的 agent_task 全鏈(狀態機、派工單、payload provider port) jedi monorepo 18
R2b 檔案取用與 agent 連線 jedi-detection 的 agent 相關全部(含檔案下載授權 resolver)+重疊帶入 agent_task 兩支 jedi monorepo 13
R3 主專案宿主接線 主專案 BE 的 api/remote_agent/app/remote_agent/adapter/infra/remote_agent/、DI container、agent_auth_settings、資料面 mTLS adapter compliance-manager-be 17

R3 檔少但密度最高——admin_required 實際疊了什麼、檔案下載端點的宿主側、 RemoteAgentAdapter 的 mTLS 實作、CM-1559 fail-open 的落點,全在這 17 檔裡。 規模與 FR-075 S1(11 檔)、FR-076 L1(10 檔)同級。

建議順序:R1 先跑(身分是其他棒的前提,已完成),R2a/R2b/R3/R1b 可平行(不同檔)。

R2 為何拆成兩棒(2026-09-08):R1(42 檔)的驗證面板因 session 額度用盡全滅(105 個 verifier 一個沒活、21 票 0 投),證明該量級撐不住;手上唯一一次面板跑完是 FR-076 L1(10 檔)。故把原 R2(29 檔)拆成 18+13。R2b 額外重疊帶入 R2a 的 agent_task_domain_service.pyagent_task_repo_impl.py 兩支——因為檔案下載授權判的是「這個檔案是不是該 agent 名下未完成任務引用的」,問題前半在 R2b、後半在 R2a,直接對半切會讓這個接縫沒人看(違反本 arc「按業務模組垂直切、不水平切」的原則)。重複報的部分由首腦驗收時挑掉。

§8

共同設定(沿用 FR-075/076 教訓)

項目 為什麼
runner 主 session 模型 Opus 5 (1M context) Sonnet 5(200K)跑 40 檔以上必被 180 秒 stall 門檻砍掉,S4~S7 首輪四棒共燒 29.6 小時零產出
effort low medium 展開成矩陣派 40+ agent,同檔被 8 個研究員各讀一遍
focus attack-surface 跳過 tests/dist/docs

啟動前必驗 scope 檔數(git ls-files 對上卡片數字才啟動)。

§9

進度總表

狀態 報告 發現數(嚴重度分佈) 首腦驗收
R1 agent 身分與註冊(42 檔) CM-1592 ✅ 掃完(面板未跑成,額度全滅,runner 逐條開檔核對+首腦複核) scan-R1-agent-identity.md 7 條(3 HIGH/3 MEDIUM/1 LOW 越界),F1 經決策者升 CRITICAL → 1C 2H 3M 1L ✅ 2026-09-08
R1b 補掃密碼學核心(10 檔,common/agent_auth/ CM-1599 待派
R2a 任務下發與狀態機(18 檔) CM-1593 待派(原 29 檔已收窄
R2b 檔案取用與 agent 連線(13 檔) CM-1601 待派(由原 R2 拆出
R3 主專案宿主接線(17 檔) CM-1594 待派
§10

R1 發現摘要(技術版,含程式碼位置)

根因一句話:四個控制面端點(/agents/register/agents/heartbeat/agents/tasks/<uid>/ack/agents/tasks/<uid>/result)完全不掛任何認證,程式碼註解說身分靠 mTLS 在上游 nginx 終結, 但出貨的 nginx.onprem.confnginx.confnginx.e2e.conf 三檔全部 grep 不到一行 ssl_verify_client。 於是 agent 身分只剩「請求自己說它是誰」,任何能連到 API 的人都能冒充任一台 agent, 拿走該租戶掃描工具的明文帳密(SonarQube token、WinRM/SSH 帳密等,通往客戶自己的基礎設施)。 加上控制面無 user context、session_scope 走 super admin 繞 RLS(機制重複 CM-1559, 但在這條鏈上的具體後果是新發現),偽造與外洩都能跨租戶。

白話版見上方「一頁看完」;本節是給工程師的技術對照,完整核對過程與每條的建議修法見 scan-R1-agent-identity.md,此處只列摘要,不重抄 602 行報告

ID 嚴重度 一句話 觸發前提 併進哪張修正卡
F1 🔴 CRITICAL(決策者升) 心跳無認證,自報 agent_uid 即取走該租戶明文工具憑證 能連到心跳端點(不需憑證)+知道一個 agent_uid(低權限使用者即可從 UI 取得) CM-1595
F3 HIGH 心跳身分解析可跨租戶(與 F1 同根因,heartbeat() 查詢無 tenant_idregister() 有) 同 F1+知道另一租戶某台 agent 的 uid CM-1595
F2 HIGH ack/result 無認證且不查任務歸屬,可偽造或抹除稽核證據 能連到 ack/result 端點(不需憑證)+知道一個任務 uid CM-1595
F5 MEDIUM ack/result 缺歸屬檢查(F2 的授權面,同檔同根因) 同 F2 CM-1595(併 F2)
F4 MEDIUM 健康檢查 base_url 未過濾直進 httpx.get,可當內網探測代理(SSRF) 需租戶管理員權限(比 F1/F2 低一級的原因) CM-1596
F6 MEDIUM 持 enroll token+知道另一台 agent 的 uid,即可接管該 agent 註冊列、改 base_url 持有該租戶 enroll token(整租戶共用一組,無到期無次數上限)+知道同租戶內另一台 agent 的 uid CM-1597
F7 LOW,⚠️ 越界 jedi-common/.env 受版控,.gitignore 對已追蹤檔無效 不適用(結構性風險,非當下可利用) CM-1598
§11

業界對照(首腦 2026-09-08 給決策者的分析)

同類產品怎麼做「agent 證明自己是誰」

我們的產品形態——雲端管理台+裝在客戶機器上的 agent 定期撥回——在業界很成熟, 代表性產品有 Elastic Agent(Fleet)、Wazuh、Datadog Agent、CrowdStrike Falcon、 Tenable Nessus Agent、AWS SSM Agent,以及 Kubernetes 的 kubelet 加入叢集流程。 它們解法細節不同,但共同骨架幾乎一致:

環節 業界共同做法 我們現在
註冊用的通行碼 通行碼只用來換身分,用完即失效或短效(Kubernetes bootstrap token 預設 24 小時;Elastic 的 enrollment token 可撤銷、可設一次性;Wazuh 建議註冊密碼配合 IP 白名單) 整個租戶共用一組、永不過期、無次數上限
註冊後的身分 每台 agent 拿到自己獨有的金鑰/憑證,之後每次通訊都用它證明身分(Elastic 每台一組 API key;Falcon/Datadog 每台獨立 agent ID+金鑰;Kubernetes 每個節點一張自己的憑證) 有簽憑證給 agent,但雲端從來不驗它——心跳只看請求裡自報的字串
誰負責驗 應用層自己驗是底線;TLS 層的客戶端憑證驗證是加分項,不是替代品 應用層不驗,全押在 nginx,而 nginx 也沒設
一台 agent 能碰到什麼 只能碰自己名下的東西(Elastic Fleet 的 policy 綁 agent;SSM 的 instance 只拿自己的 command) 知道別台的編號就能拿別台的任務、替別台回報結果、跨租戶也行
給 agent 的秘密 盡量不給明文;給的話綁定「這台、這次、短效」(Vault agent 的 response wrapping、SSM 走 Secrets Manager 的權限) 心跳回應直接夾解密後的客戶帳密

一句話總結差距:業界的通行碼是「換身分證的臨時號碼牌」,我們的通行碼是「永久通行證」; 業界每台 agent 有自己的身分證且每次查驗,我們發了身分證但門口從不看。

對到業界的嚴重度標準

  • F1/F3 心跳不驗身分+跨租戶:CWE-306(關鍵功能缺認證)+ CWE-862(缺授權)。 在 SaaS/多租戶產品的漏洞賞金計畫裡,「無需認證即可跨租戶讀取他人機密」一律是最高級(Critical)。 而我們洩漏的還不是自己的機密,是客戶第三方系統的帳密——業界叫「credential exposure leading to lateral movement」,出事時是客戶被打穿,責任回到我們。
  • F2 偽造任務結果:CWE-306+資料完整性。對合規產品特別致命——客戶拿這些紀錄去應付外部稽核, 等於核心產出物可被任意人塗改。業界一般判 High,對「稽核證據」類產品會上調。
  • F6 拿安裝碼搶 agent:業界叫 enrollment token abuse,Elastic/Kubernetes 都靠「token 短效+ 撤銷」壓低它。我們的 token 永久有效,所以這條實際比一般情況重。
  • F4 SSRF:標準 Medium,需管理員權限。
  • F7 .env 進版控:Low,結構性風險。

業界標準修法(五層)

我們其實已經有 80% 的零件(註冊時會簽憑證給 agent、會發 JWT 公鑰、agent 端也真的用 cert= 出示憑證),缺的是「雲端反過來驗 agent」這一段。

  1. 應用層自己驗(治本,業界底線):agent 每次心跳/ack/result 用私鑰簽請求, 雲端用存的公鑰驗。驗不過就拒絕——fail closed。這是 Kubernetes kubelet、Elastic Agent 的做法。 agent_uid 只能當交叉比對,不能當身分來源。
  2. 每個動作綁「你自己的東西」:ack/result 檢查 agent_tasks.agent_idtenant_id; 心跳的指紋 fallback 加 tenant_id
  3. 通行碼降級成「臨時號碼牌」:enroll token 加到期(業界 24 小時~7 天)+次數上限; 重註冊宣告既有 uid 須出示原憑證。
  4. nginx 補客戶端憑證驗證(加分項):補 ssl_verify_client。但業界共識是這不能當唯一防線—— 它只證明「持有我們發的某一張憑證」,證明不了「是你宣稱的那台」。Zero-trust 的原則就是不信網路層。
  5. 減少給 agent 的明文秘密(中期 follow-up):業界做法是短效、綁單次任務的 wrapped secret。
§12

決策紀錄(決策者 2026-09-08)

事項 裁決 理由
F1 嚴重度 CRITICAL 無認證+跨租戶+洩漏第三方憑證三條件全中;UUID 會出現在 UI/日誌/封包,業界從不把它當秘密
A/B 併卡 併一張(CM-1595) 修法是同一套 agent 認證機制,三端點掛同一驗證、共用測試;分兩張會讓兩個 runner 各寫一套
nginx 併進 CM-1595 當「連帶」,不另開卡 加分項非主修法;獨立開卡會讓人誤以為「補了 nginx 就算修好」。驗收條件明寫應用層 fail closed 為必要
補掃 開 R1b(CM-1599) 密碼學核心(CA/JWT)零發現在面板未跑下不可信;sign_csr() 沿用 agent 自帶 subject、SAN 從自報 base_url 推導、帶 CLIENT_AUTH、無 CRL 都可疑
R2/R3 現在就派 掃不同檔,與本批決策無關;R3 掃的正是 nginx 之外的另一半宿主設定,結果回饋到 CM-1595
§13

修正卡清單

卡號 修什麼 嚴重度 優先序 依賴 狀態 commit
CM-1595 控制面四端點驗 agent 簽章、fail closed、任務綁歸屬(F1+F2+F3+F5)+ nginx 連帶 🔴 CRITICAL 1 待派
CM-1597 enroll token 到期/次數+重註冊持有證明(F6) MEDIUM 2 依賴 CM-1595;動同一支檔,序列做 待派
CM-1596 健康檢查 SSRF(F4) MEDIUM 3 可與 CM-1595 平行 待派
CM-1598 jedi-common/.env 受版控(F7 越界) LOW 3 獨立 待派
CM-1599 補掃 R1b 密碼學核心 10 檔 掃描 與 R2/R3 平行 待派

修完 CM-1595 這條鏈才算達到業界底線;CM-1597 做完才算追上主流。

§14

follow-up(不在本 arc 修)

  • 心跳回應夾明文客戶憑證——業界做法是短效、綁單次任務的 wrapped secret,非本 arc 治本範圍(見上「業界標準修法」第五層)。
  • 憑證無 CRL/OCSP——已 revoked 的 agent 憑證對 nginx 端仍視為有效,R1 未觸及 common/agent_auth/ca.py,留給 CM-1599 補掃判斷。
  • 憑證到期無告警——既有 memory docs/claude/memory/followup_agent_cert_expiry_no_renewal.md:agent mTLS 憑證無 renew/rotate 邏輯,過期即無聲斷線,非本 arc 新發現。
§15

相關

  • 首腦手冊:.claude/skills/security-scan-lead/SKILL.md
  • 現況(三個 arc 共用):docs/features/FR-075-2609-jedi-package-security-audit/handoff/security-scan-STATE.md
  • 方法論與失敗紀錄:docs/features/FR-075-2609-jedi-package-security-audit/scan-methodology.md
  • 卡片骨架:docs/claude/notion-card-templates.md
§16

文件

以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。

其他文件

文件 類型 標題 最後更新
scan-R1-agent-identitymd 文件 R1 掃描結果:Agent 身分與註冊(jedi-remote-agent) 2026-09-08
§17

Notion 卡

卡片內容(決策紀錄、驗收條件)以 Notion 為準,本頁只記座標。

關係 卡號 標題 狀態
母案 CM-1591 FR-077 遠端 Agent 控制鏈資安掃描(3 棒,只掃不修)
子卡 CM-1592 R1 agent 身分與註冊(42 檔,jedi monorepo) 修正待驗證
子卡 CM-1593 R2a 任務下發與狀態機(18 檔,jedi monorepo;原 29 檔已收窄) 待派
子卡 CM-1601 R2b 檔案取用與 agent 連線(13 檔,jedi monorepo) 待派
子卡 CM-1594 R3 主專案宿主接線與資料面 adapter(17 檔,BE repo) 待派
子卡 CM-1595 修 控制面四端點驗 agent 簽章、fail closed、任務綁歸屬(F1+F2+F3+F5,CRITICAL) 待派
子卡 CM-1596 修 健康檢查 SSRF(F4,MEDIUM) 待派
子卡 CM-1597 修 enroll token 到期/次數+重註冊持有證明(F6,MEDIUM,依賴 1595) 待派
子卡 CM-1598 處置 jedi-common/.env 受版控(F7 越界,LOW) 待派
子卡 CM-1599 R1b 補掃密碼學核心(10 檔,jedi monorepo) 待派
子卡 CM-1600 文件:彙整 R1 結果與裁決進 FR 主檔並 build HTML 修正待驗證