Guidant AI · FR-058 需求討論稿

FR-058 · Detection Tool Integration — 四工具接入

檢測工具擴充:ZAP / InSpec / GCB / Nmap

FR-056 建立的檢測工具整合平台是 tool-agnostic 的,加一個工具原則上只要一條 SQL seed,實際工作量集中在 evidence-agent 的 connector。本案接入四個工具,並補上平台唯一的結構性缺口——任務層敏感參數(讓 ZAP 能做登入後掃描)。交付上採分批出貨、ZAP 優先上線(2026-07-30 拍板,見 §10.2),不是四個工具一起收。2026-07-31 更新:原移出本案的 SonarQube 經決策者裁示重新納入為 FR-058.5(pull-snapshot 拉取快照型,D13–D16 定案),本稿維持原四工具敘事不重寫,FR-058.5 詳見 design.md §4.6。

決策全數已拍板(D1–D16;D10 為 tombstone) 2026-07-30 初版 · 2026-07-31 實作後更新 FR-056 → FR-057 → FR-058 分批出貨・ZAP 優先 D4 改版:ZAP 證據 PDF→HTML D9 改案:Nmap CLI→SSH(NPSL) D12 追加:SPA 登入掃描營運風險 5 子需求 + 1 橫向項 + 追加平台能力 吸收 Notion CM-953
§1

背景:平台已經具備什麼

這一節先確立「哪些事情已經不必再做」。FR-056 把工具知識完全逐出後端,FR-057 補齊了 SSH 型態與多檔證據回收——本案是站在這兩層地基上做接入,不是重建管線。

1.1 FR-056 建立的平台架構

資料層:config schema 三表

detection_tools(工具目錄)/tenant_detection_tool_configs(租戶層憑證,Fernet 加密)/detection_tool_param_schemas(版本化參數 schema)。工具=一筆 DB row 加一份 JSONB schema。

後端:完全 tool-agnostic

BE 對任何具體工具零知識,不存在 if (tool.code == "openvas") 這類分支。新增工具不必改 BE 程式碼。

前端:純 schema 驅動

ToolPluginManage.vue 依 schema 渲染,DetectionConfigField.vue 支援七種欄位型態(text / textarea / password / number / boolean / select / select_or_text)與 condition 條件顯示,同樣沒有工具專屬分支。

派工與憑證下發

走 FR-039 agent 心跳夾帶 pending_tasks(預設 300 秒輪詢)。憑證在派工組裝當下才解密下發(FR-056 D9),agent 端用完即丟、不落地。

證據回收

connector 回 ScanResult(content bytes + filename + content_type + summary)→ task_executor 走既有 blob 通道上傳,寫成 job_evidencessource=DETECTION_TOOL)。

FR-057 追加的能力

connection_type='SSH' 第三型態、setup_guide markdown 欄位、多檔證據(result_ref.upload_uids),以及 host_failurescontent_mismatch_hosts 兩條「靜默假成功」防護通道。

1.2 evidence-agent 的 connector 架構

core/task_executor_connectors/base.py 定義 DetectionConnector 抽象類別,介面只有兩個方法:

方法回傳語意
run()ScanResultlist[ScanResult]執行掃描並回收證據;FR-057 起支援多檔
probe()None(失敗拋例外)測試連線——只驗「連得到 + 帳密正確」,不建立任何掃描資源

connector 可用的注入點:cancel_event(CM-931 任務取消)、host_failures(CM-952 逐台失敗回報)、content_mismatch_hosts(CM-954 內容錯配回報)。

既有結構性弱點 · 本案會放大

factory core/task_executor_connectors/__init__.py_TOOL_ID_TO_CODE = {1: openvas, 2: nessus, 3: sonarqube, 4: openscap} 硬編 id 到 code 的映射——agent 不連雲端 DB 查工具目錄,只靠這張表認工具。目前四個工具還能勉強維持,一次再加四個工具會讓「三環境 seed id 必須完全一致,錯一位就整條派工錯配」的脆弱性顯著放大。處理方式見決策 D11

§2

四工具優先序(已拍板)

順序不是依重要性排的,而是依「摸索成本」與「相互依賴」排的——先把能被複用的引擎做出來,後面的工具才不必重複扛同一份工程。這個優先序在 2026-07-30 改採分批出貨後維持不變,變的只是四個工具改為序列分批交付(ZAP 先上線),不再四個一起收;批次安排見 §10.2。

工具類型排這個位置的理由
1ZAPDAST 網頁動態掃描有可參考的既有實作(前同事的 auto-pentest 專案),API 呼叫序列與踩過的坑都已成文,摸索成本最低
2InSpec / CINC組態檢測(Windows / Linux)先做這個,GCB 的引擎就是現成的——讓 GCB 只剩驗證鏈路一件事,不必同時扛引擎
3GCB政府組態基準引擎複用第 2 棒;本案只做引擎與一個 Windows 最小 Demo profile,content 產製與後台更新機制已裁示不納入(見 D7)
4Nmap網路埠掃描技術上最單純(呼叫 CLI 加 XML 解析)。原以「排除平台零憑證阻礙」為主要工作,2026-07-30 改案為 SSH 型後(D9),主要工作變成複用 OpenSCAP 既有的 SSH 連線模式

2.1 四工具的整合形態分類

以「誰驅動掃描」與「連線型態」兩軸攤開,可以看出這四個工具分屬幾種完全不同的整合模式——差別在於 connector 面對的是遠端服務 API、目標主機的遠端 shell,還是 agent 本機的引擎。

實作後的兩處更正(2026-07-31 回填)

Nmap 不再是本機 CLI 型——2026-07-30 D9 改案改走 SSH(登入客戶的執行主機再掃目標),與 OpenSCAP 同構。connection_type='CLI' 因此至今仍無任何實際使用者

InSpec / CINC 實際上是第四種形態(設計時歸在「遠端登入目標主機」,方向其實相反):引擎裝在 agent 本機、由它自己主動連出去掃 agentless 的目標主機;OpenSCAP 則是引擎裝在目標主機上。connection_type 仍宣告 SSH / WinRM——那是目標主機開放的協定,不代表 connector 自己建連線。這個差異是 D6「雙 transport 邊際成本很低」的真正原因,也是 GCB content 外部載入接口的架構前提(若誤以為 content 要送到目標主機,設計會做錯且要到端到端驗收才會發現)。

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
graph TD
  ROOT["evidence-agent
connector"] ROOT --> API["① 連遠端服務 API"] ROOT --> REMOTE["② 遠端登入目標主機
(引擎裝在目標主機)"] ROOT --> LOCALENG["②′ agent 本機引擎
由引擎自己連出去
(目標主機 agentless)"] ROOT --> CLI["③ agent 本機 CLI
(至今無使用者)"] API --> OV["OpenVAS
(已上線)"] API --> ZAP["ZAP
FR-058.1"] REMOTE --> OSCAP["OpenSCAP
SSH · Linux (已上線)"] REMOTE --> NMAP["Nmap
SSH · FR-058.4
(D9 改案,原定 ③)"] LOCALENG --> INSPEC["CINC Auditor
SSH + WinRM · FR-058.2"] LOCALENG --> GCB["GCB
複用 CINC 引擎 · FR-058.3"] CLI --> NONE["尚無實際使用者"] classDef mode fill:#EEF2F3,stroke:#C7D1D2,color:#14201F; classDef shipped fill:#E4F1EA,stroke:#2E7D5B,color:#14201F; classDef newtool fill:#E2F0F1,stroke:#0E7C86,color:#14201F; classDef root fill:#F6EBD5,stroke:#9C6B12,color:#14201F; classDef empty fill:#FBFCFC,stroke:#C7D1D2,color:#7A8A8C; class ROOT root; class API,REMOTE,LOCALENG,CLI mode; class OV,OSCAP shipped; class ZAP,INSPEC,GCB,NMAP newtool; class NONE empty;
整合形態分類(2026-07-31 依實作結果更正):綠底為已上線工具,藍底為本案新增。Nmap 由 ③ 改列 ②(D9),CINC 與 GCB 自 ② 移入新識別出的 ②′——引擎在 agent 本機、方向與 OpenSCAP 相反。③ 至今仍無實際使用者
§3

任務層敏感參數:平台缺口(ZAP 的硬前置)

這是本案唯一需要動平台本體的項目,而且必須先於 ZAP 完成。方案已拍板採選項 A,此處三案並陳說明取捨。

3.1 問題:現有設計有一格是空的

使用情境是「任務可輸入要掃描的網站,有可能提供一組登入帳密讓 ZAP 做登入後掃描,沒有的話就一般掃描」。但現有設計把憑證與參數分成兩處放,而只有憑證那一條有加密

層級存放位置加密適合放什麼
租戶層tenant_detection_tool_configsFernet 加密ZAP 服務本身的位址與 API key——全租戶共用、設定一次就不動
任務層job_execution_detection_tools.tool_params明文掃描目標、profile 這類非敏感參數
任務層敏感目前不存在 ——「這次掃 A 網站用 A 的帳密,下次掃 B 網站用 B 的帳密」正好落在這一格

若直接塞進 tool_params 會發生什麼

被掃網站的帳密會明文躺在資料庫。② FE 執行紀錄視窗會顯示任務參數——BE 現有的 secret 剝除機制是針對租戶層設定的 secret 定義寫的,任務層參數沒有這套保護,等於帳密直接顯示在畫面上。

這不是 ZAP 專屬問題:Nmap 若要做認證掃描、GCB 若每台主機帳密不同,都會撞上同一格。因此當成平台級能力補上,不做 ZAP 專屬的權宜處理。

3.2 三個評估過的選項

選項做法評估
A · 任務層敏感參數
已拍板
param_schema 支援 "secret": true;BE 存 tool_params 前對這些 key 加密(複用既有 Fernet 鏈),派工時解密下發,FE 執行紀錄不顯示。 一次補平台缺口,四個工具都受益,與現有 secret 機制同一套邏輯。成本約半天到一天(BE 加解密接線 + FE 欄位渲染 + 剝除規則)。
B · 掛租戶層設定
不採
ZAP 設定頁多填一組「目標網站測試帳號」。 一個租戶只能存一組,不符「不同任務掃不同網站」的實際情境。
C · 掃描目標憑證庫
不採,日後可再評估
另開一張表管理被掃目標的憑證,任務發佈時挑選引用。 最完整,但工程量大很多。適合日後被掃目標數量成長後再做。

3.3 ZAP 的 param_schema(採用選項 A 之後)

keylabeltyperequiredsecretcondition
target_url掃描目標網址text必填
scan_mode掃描模式select必填被動 / 主動 / 登入後主動
login_url登入頁網址text條件scan_mode = authenticated
login_username測試帳號text條件scan_mode = authenticated
login_password測試密碼password條件scan_mode = authenticated
logged_in_indicator登入成功特徵字串text條件scan_mode = authenticated

沒有選登入模式時,後四欄整組不顯示。這對上「有帳密就深掃、沒有就一般掃描」的需求,而且用的是 FE 現成的 condition 機制,不需要新元件

實作時的修正 · 登入四欄改為「條件必填」

初版把這四欄做成純選填,結果是使用者少填一欄,錯誤要等到派工後 5 分鐘 agent 執行期才爆。改為條件必填(選了登入模式即為必填)後,錯誤提前到 FE 表單存檔當下

這是本案反覆出現的一種形狀:同一個錯誤,在越靠近使用者的地方爆越便宜。另兩個同形的修正——probe 漏傳 tool code(原本靜默走 fallback,改成 payload 缺欄位即可查)、ZAP 爬取狀態誤判(原本回一個看不懂的字串,改成回非數字即刻拋錯)。

3.4 端到端流程(以 ZAP 登入後掃描為例)

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
  participant U as 稽核員
  participant FE as 前端
  participant BE as 雲端 BE
  participant AG as evidence-agent
  participant Z as 客戶 ZAP daemon
  participant W as 被掃網站

  U->>FE: 發佈任務:填 target_url、選「登入後主動」、填測試帳密
  FE->>BE: POST 任務參數
  BE->>BE: 依 param_schema 對 secret:true 欄位 Fernet 加密
  BE->>BE: 寫入 tool_params(敏感欄位為密文)
  AG->>BE: 心跳(mTLS)
  BE->>BE: 組裝派工時解密敏感參數
  BE-->>AG: pending_tasks(含明文參數,僅傳輸中存在)
  AG->>Z: 設定 context + 登入認證 + logged_in_indicator
  AG->>Z: ajaxSpider.scan(爬取)
  loop 輪詢爬取狀態
    AG->>Z: ajaxSpider.status
  end
  AG->>Z: ascan.scan(主動掃描)
  loop 輪詢掃描進度(支援 cancel_event / timeout)
    AG->>Z: ascan.status
    Z->>W: 送出測試請求
  end
  AG->>Z: core.htmlreport(HTML 給人看)
  AG->>Z: core.jsonreport(JSON 供統計解析)
  Z-->>AG: 報告內容(記憶體,不落地)
  AG->>BE: ScanResult(HTML bytes + summary)
  BE->>BE: 走既有 blob 通道寫 job_evidences
  BE-->>FE: 執行紀錄顯示參數(敏感欄位已剝除)
  U->>FE: 於證據附件檢視 ZAP 報告
      
端到端時序:敏感參數在資料庫為密文、僅在派工傳輸與 agent 執行期間為明文,FE 執行紀錄一律剝除
§4

ZAP(第一優先)

身分定位

原 OWASP ZAP,現歸 Checkmarx 維護,仍為 Apache 2.0 開源授權。屬 DAST(動態應用程式安全測試)——對執行中的網站主動送出測試請求,找出網頁層弱點。

整合形態

走 API / daemon 模式(與 OpenVAS 同型),不走 baseline CLI 模式。理由:API 模式能做「登入後掃描」,那是稽核場景真正有價值的能力;CLI baseline 只能被動掃表層,對合規證據的價值有限。

connector 骨架照抄 openvas.py 的形狀:連客戶既有的 ZAP 服務(不自己拉容器)、輪詢進度、雙格式取報告(core.htmlreport HTML 給人看 + core.jsonreport JSON 給程式解析統計;2026-07-30 D4 改版,見 §8 D4)、支援 cancel_event 與 timeout。

技術要點:ZAP 2.17.0 相容性

使用者公司已安裝 2.17.0,以下四點在實作前必須對齊。

項目內容影響
Python 套件版本 要用 zaproxy 0.6.0(2026-06 發布)。舊參考碼用的 0.4.0(2025-01)太舊;0.5.0 與 ZAP 2.17.0 同日(2025-12-15)發布以對齊新版 API。 ZAPv2 類別仍是進入點,呼叫風格不必改寫
警報去重 2.17.0 加入警報去重與 Systemic alert 支援(Issue 9067 / 9097)。 alert 數量會比舊版少,統計解析要對齊新語意,不能沿用舊版計數假設
報告時間欄位 JSON / XML 報告新增 ISO 8601 格式的 created 欄位;舊的 generatedString 官方已預告未來移除。 解析器不要依賴 generatedString
暫存訊息保留 headless 模式下,主動掃描產生的暫存 HTTP 訊息預設不再保留(節省磁碟)。 對我們的取報告流程無影響

沒有破壞性的 API 端點移除,主線呼叫序列可以沿用。

4.1 既有參考實作的三欄取捨

前同事寫的 auto-pentest 專案位於 ~/Documents/Projects/Jedicogy/auto-pentest/(DDD 分層 + MinIO 儲存)。它的價值不在架構,而在踩過坑才整理得出的那幾份知識

可直接搬的知識必須丟棄的部分必須修的問題
API 呼叫主線序列
ajaxSpider.scan → 輪詢 ajaxSpider.status(回字串 'running')→ ascan.scan → 輪詢 ascan.status()(回百分比數字)→ 取報告(舊碼用 reports.generate;本案依 2026-07-30 D4 改版改用 core.htmlreport / core.jsonreport 取 bytes)。
注意 兩種輪詢的回傳型態不同,這個細節在 2.17.0 沒有改變。
整個 DDD 分層
domain service / app service / entity / MinIO storage adapter 全不需要——我們的 connector 是扁平單一類別,上傳走 task_executor 既有 blob 通道,沒有 storage adapter 的位置。
硬編憑證 紅線
zap_auth.pyzap_domain_service.py 內有寫死的帳號密碼與固定 IP。憑證禁入版控是本專案紅線,一律改走 self.credentials(FR-056 D9 解密下發)與 self.params
12 種報告模板 + sections / themes 相容矩陣
auto_pentest/common/enums/zap_report/report_enum.pyTEMPLATE_CONFIGS2026-07-30 D4 改版 主線不再走 reports.generate 模板體系(改用 core.htmlreport / core.jsonreport 直接取 bytes),此矩陣降為背景知識,不再搬去當 FE 下拉選項來源。
自起 Docker 容器
ZAPContainerDomainService 那一套。ZAP 應視為「客戶已安裝好的服務」,connector 只負責連上去(與 OpenVAS 同一模型)。
沒有取消機制、沒有 timeout 上限
while status == 'running' 是無限等待。必須支援 cancel_event 與 timeout 上限。另外 print 當 log 要換成 logger
四種登入認證設定方式
form-based / json-based / script-based / Selenium-based,加上 set_logged_in_indicator(見 auto_pentest/domain/zap/service/zap_auth.py)。登入後掃描是整個 ZAP 接入最難的部分,這裡有完整的 API 序列可參考邏輯。
報告落地檔案系統再上傳
reports.generate(reportdir=...) 會寫檔到磁碟——而且寫的是 ZAP daemon 主機的磁碟,遠端部署模型下 agent 根本取不到(2026-07-30 D4 改版的直接動因,issue #7821)。本案改用 core.htmlreport / core.jsonreport 直接取回 bytes,記憶體轉手不落地
Oracle Nashorn script engine
JDK 15 之後已移除,ZAP 新版預設改用 Graal.js。舊碼的 upload_script() 已改用 Graal.js,但 java_script() 還停在 Nashorn——是會踩到的雷。

本案工作內容

4.2 登入後掃描的 SPA 限制(D12,2026-07-30 實測後追加)

這一節是本案嚴重度最容易被低估的發現。設計階段原本把它記成「SPA 掃不進登入後頁面、報告涵蓋範圍侷限」——實測證明那個記載是錯的,真實後果高出一個層級。

實測後果更正 · 不是掃不到,是會打掛整台共用 ZAP

對 SPA 使用登入後掃描,掃描根本不會完成,而且傷害會擴散到其他人的任務。完整因果鏈:

  1. logged_in_indicator 誤判——ZAP 比對的是 HTTP 回應的原始內容,而 SPA 初始 HTML 是空殼、登入後文字要靠 JS 渲染才進 DOM,原始回應裡永遠找不到特徵字串。
  2. ZAP 因此認定「還沒登入」,反覆重跑登入——這是無限迴圈,不是重試幾次就放棄
  3. 認證失敗次數持續累積,觸及 ZAP 2.17.0 Insights 機制的門檻(100 次)
  4. ZAP daemon 主動關閉自己——不是被 agent 中止,也不是逾時。
  5. ZAP daemon 是全租戶共用的單一服務,因此單一 SPA 登入任務會連帶中斷該台 ZAP 上其他正在執行的掃描。實測當下即發生:另一個任務在 ZAP 重啟的空檔收到 Connection refused 而失敗。

這已不是報告品質問題,而是營運層級風險——單一任務可造成全域服務中斷。

daemon log 摘錄(根因只有 insight.auth.failure : 100 那一行):

[ZAP-Insights] INFO org.parosproxy.paros.control.Control - Shutting down ZAP due to High Level Insight:
HIGH : EXCEEDED_HIGH : http://192.168.50.151 : insight.auth.failure : 100
[ZAP-Insights] INFO ... - Discard Session
... (後續大量 Selenium NoSuchSessionException,為 ZAP 死後的餘波,非原因)
ZAP 2.17.0 terminated.
zap exited with code 2 (restarting)

讀 log 時容易誤判的一點NoSuchSessionException 洗版看起來像 Selenium 出問題,但它發生在 Shutting down ZAP 之後,是 ZAP 已死造成的下游噪音,不是根因。

範圍界定(避免過度解讀)

僅限登入後掃描模式,被動與主動完全不受影響

被動掃描與主動掃描不建立 ZAP context、不設定任何認證、不比對登入特徵字串,因此不會產生認證失敗、不會累積計數、碰不到 Insights 門檻。第一批出貨的被動與主動兩種模式完全不受影響,兩者皆已實測通過。

為什麼 SPA 支援不做半套(D12 定案)

SPA 的登入後掃描要通,三件事必須同時解決,缺一則等於沒做:

#SPA 為何不通要補什麼
表單登入無效——SPA 的登入是 JS 呼叫 API 取 token,頁面上沒有傳統表單提交;ZAP 的 form-based 認證是對登入頁 POST 帳密欄位,對 SPA 不會發生作用。json-based 認證(對登入 API 送 JSON payload)
token 不會自動帶——SPA 多用 JWT 存 localStorage、靠 Authorization header 帶;ZAP 預設 session 管理是 cookie-based,不會把 token 塞進後續請求。script-based session management(要撰寫並上傳 ZAP script)
logged_in_indicator 不可靠——即上述無限重試的直接原因。改變登入成功的判定方式

被排除的三個折衷走法: 本案一併擴充 json 認證 + script session——那是「認證方式擴充 + script 管理 + 測試站台」三件獨立工作,塞進第一批會拖住已可出貨的部分,而第一批的核心價值(ZAP 接入、HTML 證據進證據池、任務層敏感參數平台能力)不依賴登入後掃描 只做 json 認證、session 管理留待日後——沒有中間地帶:認證送出去了但 token 帶不進後續請求,一樣掃不進登入後頁面,結果與完全不做相同,卻已付出工程成本並在 UI 上留下「看起來支援了」的誤導。 只換掉 logged_in_indicator 的判定——那只解第三個原因,前兩個原因未解則掃描本來就進不去,判定準不準沒有意義。

本案的處置 · 以文件與 setup_guide 擋,不做程式防護

已做setup_guide 明白標示「請勿對 SPA 使用登入後掃描」;本案文件(本節與 D12)記載完整因果鏈與實測佐證。

裁示不做(只記錄):connector 端的自我保護——在登入模式下自行偵測認證失敗異常累積,逼近 Insights 門檻前主動中止任務並回明確訊息(如「目標網站可能是單頁式應用,登入後掃描不適用」)。這道防護可獨立於 SPA 支援先做(它不需要 json 認證與 script session 完成),即使 SPA 支援永遠不做,對「使用者誤把 SPA 當表單登入站台來掃」的情境仍然有效。決策者 2026-07-30 裁示本案只記錄不實作。

後續案:Notion CM-984(FR-058 後續:ZAP 登入後掃描支援 SPA)。實作時注意 ZAP script engine——Oracle Nashorn 在 JDK 15 後已移除、新版預設 Graal.js,舊參考碼 zap_auth.pyjava_script() 還停在 Nashorn。

實測數據(2026-07-30,對一個 Next.js SPA 站台)

面向實測結果
覆蓋範圍掃到 10 個 URL,全部為登入前靜態資源——根路徑、_next/static 下的 JS / CSS chunk、favicon;無任何 API 端點、無任何登入後頁面
產出HTML 報告 57 KB——證據上傳鏈路本身正常(D4 的雙格式取報告已驗證可行)
alert 分佈2 High / 8 Medium / 7 Low / 32 Informational,性質集中在站台層級組態問題(安全標頭、CSP、cookie 屬性、資訊洩漏),非應用邏輯漏洞

這組數字同時是匿名掃描價值邊界的具體證據:對 SPA 而言,未解決登入即等同匿名掃描——能查出組態層問題但碰不到應用邏輯。這正是登入後掃描在稽核場景的價值所在,也是它值得列為獨立後續案、而非在第一批做半套的理由。

§5

CINC Auditor(第二優先)

原稱「InSpec / CINC Auditor」,2026-07-31 統一為「CINC Auditor」——採用的就是 CINC,名稱裡掛著會混淆授權判斷的 InSpec 字樣沒有好處(見 D5)。本節保留 InSpec 字樣之處均為指涉上游專案或 profile 格式,非產品內的工具名稱。

身分定位

組態合規檢測引擎,用 Ruby DSL 撰寫檢測規則(profile)。本案接入它是為了解決 Windows 主機組態檢測這個既有缺口。

要解的問題

FR-057 的 OpenSCAP connector 是 SSH 加 Linux 寫死的(/usr/bin/oscap/etc/os-release、sudoers),無法處理 Windows 主機。這個缺口已記錄於 Notion CM-953(「FR-057 後續:異質 OS 環境(Windows 等非 Linux 主機)的檢測支援方案」,狀態=討論)。

案件歸屬

本 FR 吸收 CM-953,不另開平行卡片。CM-953 於本案子需求 FR-058.2 落地時一併結案。

5.1 為什麼不是繼續用 OpenSCAP

OpenSCAP on Windows 是死路

OpenSCAP 官方已正式聲明放棄 Windows native 支援——官方 docs/windows.md 明文標示 no longer usable。最後一個勉強可用的版本是 1.3.4,且不再修 bug。

「Linux 掃描機遠端掃 Windows」引擎做不到

不是設定問題,是能力缺失:OpenSCAP 沒有 WinRM transport、沒有 Windows OVAL probe。無論怎麼配置都掃不到 Windows 目標。

CIS-CAT Pro 要付費

唯一能做「單機遠端 WinRM 掃多台 Windows」的 SCAP 系工具是 CIS-CAT Pro,但需要 CIS SecureSuite 付費會員資格。

免費的 SCC 遠端能力弱

SCC 偏「目標本機安裝 agent」模式,與我們 agentless 的既有模型不合。

5.2 拍板路線:InSpec / CINC Auditor

理由說明
agentless目標主機不必安裝掃描器,與現有 OpenSCAP-SSH 是同一種遠端模型,架構一致。
雙 transport 單一介面SSH(Linux)與 WinRM(Windows)走同一套 CLI 與 profile 格式--reporter json 統一輸出,connector 不必寫兩套解析。
授權成本CIS-CAT 要付費才能做的事,CINC Auditor 免費(Apache 2.0)就能做
一個 connector 三條線Windows 組態檢測 + Linux GCB + Windows GCB,三者共用同一個 connector。

授權關鍵 · 必須用 CINC Auditor 而非官方 InSpec

採用 CINC Auditor——Apache 2.0 授權的自由重建版,是 Progress 官方認可的 drop-in 替代品。不要用官方 InSpec 6+ 商業 binary,後者需要接受 Chef EULA 並持有 license key。這一點在採購與部署文件上都要寫明。

5.3 實作揭露的架構事實(2026-07-31 回填)

CINC 裝在 agent 本機、由它自己連出去——與 OpenSCAP 方向相反

設計階段把 CINC 歸在「遠端登入目標主機執行」,與 OpenSCAP 同一類。實作後發現方向是相反的

  • OpenSCAP:引擎(oscap)裝在目標主機上,connector 用 SSH 登入後在對端執行。
  • CINC Auditor:引擎裝在 agent 本機/usr/bin/cinc-auditor,隨 agent image 出貨),由它自己主動連出去掃目標;目標主機完全 agentless、不必安裝任何東西。

connection_type 仍宣告為 SSH / WinRM——那是目標主機開放的協定,不代表 connector 自己建連線(連線是 CINC 引擎建的)。

為什麼這件事非記不可:① 這才是 D6「雙 transport 邊際成本很低」的真正原因——兩個 transport 都是同一支 CLI 的參數,不是兩套連線程式碼。② D7 的 content 外部載入接口直接建立在此架構假設上——若誤以為 content 要送到目標主機,設計會做錯,而且要到端到端驗收才會發現。③ 原記載的「對照 openscap.py 的 SSH 模式」需更正:openscap.py 只有多目標處理與 host_failures 可借鏡,連線層完全不同

實作時建立的三個約定

約定理由
憑證走 --config、不進 argvargv 在目標主機的行程表上人人看得到,等於把密碼公開
授權紅線以 exit 172 硬失敗釘住CINC 若誤跑成需接受 EULA 的官方 InSpec,退出碼會是 172;把它當硬失敗處理,避免授權問題靜默通過(D5 的程式層防線)
probe 的憑證檢查排在引擎檢查之前先報「你沒填憑證」比先報「引擎不存在」更貼近使用者真正要修的東西

本案工作內容

驗收結果 雙 transport 皆實跑通過

SSH(Linux 目標 192.168.50.151)與 WinRM(Windows 目標 192.168.50.160各自實跑一輪完整掃描,非僅測試連線:151 為 pass 131 / fail 60、160 為 pass 300 / fail 472D6 的雙 transport 定案已由實跑證實。

另註:顯示名稱已由「InSpec / CINC Auditor」統一為「CINC Auditor」——採用的就是 CINC,名稱裡掛著會混淆授權判斷的 InSpec 字樣沒有好處(D5)。

§6

GCB(第三優先)

身分定位

政府組態基準(TWGCB),我國公務機關資通設備的組態安全基準。屬合規稽核情境的高需求項目。

整合形態

GCB 與 OpenSCAP 一樣要分 Windows 與 Linux 兩邊,差別只在 content 不同;而 FR-058.2 的 CINC Auditor 本來就是 SSH 加 WinRM 雙 transport 的同一套引擎,因此兩邊一次由它承擔,GCB 不另立引擎、不另寫 connector。這部分已拍板,見決策 D7 引擎層。

釐清 · Linux GCB 為何不沿用既有的 OpenSCAP

技術上 Linux GCB 確實可以用 OpenSCAP 執行——它是 Linux 原生工具,FR-057 也已經有可用的 SSH connector。不選它的原因不在 Linux 這一側,而在維護成本:Windows GCB 只有 CINC 這條路走得通(OpenSCAP 的 Windows 支援已被官方放棄,見 §5)。若 Linux 走 OpenSCAP,同一份 GCB 基準就要維護兩種 content 格式——Linux 寫成 XCCDF + OVAL、Windows 寫成 InSpec profile,同一條 TWGCB 要求要用兩種語法各寫一次,TWGCB 改版時也要改兩份。

引擎統一的目的是 content 格式統一,這才是 D8 定案 InSpec profile 的真正理由。

OpenSCAP 的定位不變,兩者按「基準來源」分工而非按 OS 分工:OpenSCAP 繼續服務 CIS / STIG 這類有官方 SCAP content(SSG)可直接取用的國際基準;CINC 負責 GCB 這種沒有官方 content、必須自產,且 Windows 端別無選擇的基準。同一台 Linux 主機要查 CIS 就派 OpenSCAP 任務、要查 GCB 就派 CINC 任務,兩個任務可掛在同一個控制項底下、證據進同一個證據池——這正是 CM-953 已定的「一任務一工具,靠多任務處理異質情境」模型,不需要改架構。

本案範圍 · 只做引擎與一個最小 Demo

決策者已裁示:本案只做引擎,並拿 Windows 最單純的一個基準當 Demo,證明端到端鏈路可運作即可。content 產製(把每一條 TWGCB-ID 轉成檢查規則)與後台 content 更新機制都列為後續,不在本案範圍。完整裁示見決策 D7

這樣切的理由是:FR-058.3 因此有明確且短的終點,而且它驗證的正是風險最高的部分——WinRM 連線、認證、profile 執行、結果解析。content 反而是最沒有技術風險的部分(純人力活),一條規則也能證明鏈路通

設計約束(本案必須做到):content 不可硬編進 connector 或 agent image,必須設計成可從外部載入,否則日後要接後台更新機制會很難改。

6.1 官方實際提供什麼(2026-07-30 查訪盤點)

以下盤點不是本案的工作清單,而是後續評估 content 投入時的背景資料;在本案的作用只有一個——解釋為何 Demo 平台選 Windows。資料來源為 NICS 兩個官方頁面:GCB 說明文件(頁面標示 2026/6/26 更新)與 GCB 部署資源(頁面標示 2026/3/30 更新)。

說明文件頁:作業系統基準(只有 DOCX + PDF 兩種格式)

TWGCB 編號基準對象版本提供格式
TWGCB-01-010Microsoft Windows 11v1.1僅 DOCX / PDF
TWGCB-01-007Microsoft Windows Server 2016v1.3
TWGCB-01-009Microsoft Windows Server 2019v1.2
TWGCB-01-011Microsoft Windows Server 2022v1.1
TWGCB-01-008Red Hat Enterprise Linux 8v1.3
TWGCB-01-012Red Hat Enterprise Linux 9(伺服器)v1.2
TWGCB-01-013Red Hat Enterprise Linux 9(工作站)v1.2
TWGCB-01-014Ubuntu 22.04 LTSv1.2
TWGCB-01-015Apple macOS 15v1.0(2026/6 才發布)

另有瀏覽器說明文件:Google Chrome、Mozilla Firefox、Microsoft Edge、Safari——同樣只有 DOCX 與 PDF。

部署資源頁:機器可讀檔(全部是 ZIP,且只有 Windows 與瀏覽器)

檔案涵蓋對象更新日期
作業系統 GPO 檔Windows 11/Windows Server 2016/2019/20222025/11/7
瀏覽器 GPO 檔Google Chrome、Microsoft Edge2026/3/30
Mozilla Firefox 部署設定檔Firefox2022/12/26
Windows 11 新增政策範本檔Windows 112025/11/7
Google Chrome 政策範本檔Chrome2026/3/30
Microsoft Edge 政策範本檔Edge2024/12/13
LocalGPO 安裝程式Windows 共用工具2022/12/28

關鍵落差 · 本次查訪最重要的發現

Linux 與 macOS 在部署資源頁完全沒有任何檔案。說明文件有 RHEL 8/RHEL 9(伺服器 + 工作站)/Ubuntu 22.04/macOS 15 的完整基準,但沒有任何對應的機器可讀部署檔——連 shell script 都沒有,只有 DOCX 與 PDF

這證實並擴大了 NICS FAQ v1.6 §7.1「未提供 RHEL8 等平台自動化檢測工具」的說法:實際情況是除 Windows 之外,所有平台都沒有機器可讀來源。日後評估 content 投入時,成本不是一個平均值,而是分裂成兩個量級完全不同的世界。

6.2 三類平台的落差(後續評估用)

平台可用來源產製方式自動化程度
Windows
4 個 OS + 2 瀏覽器
GPO backup ZIP(含 registry.pol + GptTmpl.inf 可寫產生器解析產出骨架,再人工補判定邏輯 約七成
Linux
RHEL 8/RHEL 9 ×2/Ubuntu 22.04
只有 DOCX / PDF 逐條人工閱讀文件、手寫檢查規則 幾乎零
macOS 15 只有 DOCX / PDF 同 Linux;且現有 connector 未曾處理 macOS 幾乎零

這份落差就是本案 Demo 選 Windows 的理由:Windows 有 GPO 檔這個結構化來源可直接參考,寫一個最小 Demo profile 的成本最低;Linux 只有 DOCX,連一條 Demo 規則都要先人工翻譯文件敘述。要再次強調——這是選 Demo 平台的理由,不是要在本案做 Windows content 工程

澄清一個容易誤解的點

常見的提問是「是不是要把官方 PDF 轉成 content」。實際情況是:Windows 不需要——有 GPO 檔可解析;Linux 需要,但那不是「轉檔」,而是逐條人工把文件敘述翻譯成檢查邏輯。content 工程已不在本案範圍,此處僅為背景說明。

Windows 特有的陷阱 · tattoo settings

GPO 設定分兩種位置:HKLM\Software\Policies 底下屬「正規政策區」,套用前會先清空;不在此位置的設定則會殘留(業界稱 tattoo settings)。

對檢查規則的實際風險是:規則若讀取非 Policies 路徑,可能讀到上次殘留的舊值而誤判為通過,實際上政策根本沒生效。這種誤判在稽核情境屬嚴重問題(報告說合格、實際不合格)。日後撰寫 content 時必須處理,本案的 Demo profile 也應避開此陷阱。同列於 §9 風險表。

6.3 引擎與 content 的責任切分

面向Linux GCBWindows GCB
引擎統一走 FR-058.2 的 CINC Auditor——Linux 用 SSH transport、Windows 用 WinRM transport,同一套 CLI 與 profile 格式(OpenSCAP 在 Windows 走不通,不作為 GCB 的引擎選項)
引擎層工作量已由 .2 承擔 GCB 本身不需要新的 connector
本案 content不做一份最小 Demo profile 僅用於驗證鏈路
content 載入方式一律從外部載入,不硬編進 connector、不打包進 agent image(本案設計約束,見 D7)
後續 content 產製列為後續 與後台 content 更新機制一併另案評估

本案工作內容(範圍已由 D7 裁示收斂)

6.4 D7 content 外部載入接口:改為「驗證 + 寫文件」(2026-07-31 裁示)

原規劃的抽象層裁示不做——那個能力本來就在

原本 T-3.2 規劃要「做一個 content 外部載入接口」。實作前查證發現:CINC 原生就支援三種 content 來源,而 params.profile 直接進 cinc-auditor exec 的 argv——外部載入能力本來就存在,另做一層抽象只是把上游能力重新命名一次,而且會限縮它(上游支援的來源形式會被我們的抽象層擋掉一部分)。

裁示:不做抽象層,改為逐條驗收 D7 的三條約束並寫成文件。三條約束——不硬編不打包進 agent image換 content 零程式改動——皆已實跑驗證通過(兩份公開 content 對 SSH 與 WinRM 各跑一輪,error 0)。

實跑踩到的坑 · 裸 GitHub 網址走的是 git fetcher,不是 tarball 下載

把裸 GitHub 網址當 profile 來源時,CINC 走的是 git fetcher(需要主機上有 git),而不是直接下載 tarball。agent image 原本沒裝 git,首次實跑就炸在 profile 解析階段——已於 agent 端補裝。

做了對照實驗確認:tarball 形式對 git 零相依——把 /usr/bin/git 移走後,裸網址失敗、tarball 照常跑通。

兩個已知限制(併入後台 content 更新機制的評估範圍)

驗收結果 · 鏈路通過,但 Demo profile 不足以對客戶展示

鏈路驗收通過:WinRM 連線 → 認證 → profile 執行 → 結果解析 → 證據上傳整條走得通,符合 D7 定的驗收條件(鏈路可運作,不是涵蓋規則數)。

但 Demo profile 只有 2 項檢查——決策者實測後表示不能拿去給客戶 Demo。這不推翻 D7 的範圍裁示(content 產製本來就不在本案),而是確認了「鏈路驗證用的最小 Demo」與「可對外展示的 Demo」是兩件事。後續案:Notion CM-992

§7

Nmap(第四優先)

身分定位

網路埠與服務探測工具。在合規情境對應「開放服務盤點」「非必要服務關閉」這類控制項的證據。技術上是四個工具裡最單純的一個。

整合形態 2026-07-30 D9 改案:CLI 型 → SSH 型

(改案後定案)走 connection_type='SSH':nmap 由客戶安裝在自己指定的一台執行主機上,agent 透過 SSH 登入該主機執行掃描,與已上線的 OpenSCAP 完全同構。連帶結果——nmap 需要 SSH 憑證requires_credentials=TRUE),不再是零憑證工具

(原設計,已被取代)原定「我們驅動、agent 本機執行 CLI」,agent 直接呼叫 nmap -oX,並以此當作 connection_type='CLI' 型態的第一個實際使用者。改案後該型態至今仍無任何使用者(FR-056 定義後未曾被用過)。

7.1 D9 為什麼改案:兩個獨立的理由同時成立

理由一 · 技術:容器化矛盾——「不 bundle」與「agent 跑得到」互相打架

實作前才發現原 CLI 設計有個隱含假設站不住:agent 跑在 Docker container 內,看不到主機上安裝的 nmap(容器有獨立檔案系統)。於是「不 bundle 進 image」與「客戶自備、agent 呼叫得到」兩個條件互相矛盾——客戶把 nmap 裝在主機上,容器裡的 agent 依然找不到它。

理由二 · 授權:查證 NPSL 後確認不能 bundle,而且理由比原記載更強

原記載只寫「NPSL 授權不可 bundle,NPSL 對依賴使用者既有安裝副本的程式有豁免條款」。查證 NPSL v0.95 §3(出處 https://svn.nmap.org/nmap/LICENSE)後,實情是:

①「只用子行程呼叫、不連結函式庫」這個 GPL 慣用的免責論點,在 NPSL 底下不成立。§3 明文列舉的「衍生作品」定義包含這一條:

Is designed specifically to execute Covered Software and parse the results
(as opposed to typical shell or execution-menu apps, which will execute
anything you tell them to).

我們的 connector 正是「專門呼叫 nmap 並解析其 XML 輸出」,正中此條——該條款是特意加來堵掉這個論點的(NPSL 基於 GPLv2 但加了額外限制)。GPLv2 的 mere aggregation 條款也救不了,因為它只適用於「not based on the Program」的作品,而 §3 已把我們定義為 based on。官方 Legal Notices 另明說免費授權不允許 nmap 被用於商業軟體或硬體產品的再散布(明列 appliances / virtual machines / traditional applications),並為此販售 Nmap OEM Edition。

② 但 §3 最後一段自己留了出口,而 SSH 型正好落在裡面。該段表明:軟體若解析的是使用者提供的結果、或執行的是「使用者早已安裝在自己系統上的 nmap」,授權方不主張控制Licensor does not purport to control through this license any software which does not require the rights granted herein)。SSH 型態就是這個描述——nmap 是客戶裝在自己主機上的,我們只是遠端下指令。

評估過並排除的三個替代方案

方案排除理由
掛載主機 binary 進容器
-v /usr/bin/nmap:/usr/bin/nmap:ro 加共享函式庫目錄
nmap 非靜態連結,依賴 libssl / libpcap / liblua 等一串共享函式庫。掛主機的 lib 目錄進容器是在與容器內 glibc 版本賭運氣,且可能蓋掉容器自身函式庫把 agent 弄壞;架構也綁死 x86_64。這把「客戶自備」變成一個需要客戶理解共享函式庫的任務,故障時難以遠端診斷。
客戶自建一層 image
以我方 image 為 FROM,自行 apt install nmap 後 build
技術上可行且乾淨(函式庫由套件管理員解決),法律上也成立(nmap 由客戶裝入,非我方散布)。排除的是持續性負擔:客戶每次 agent 升版都要改 Dockerfile 版號並重新 build;且「誰把它裝進去」雖在法律上是關鍵,技術結果與我方 bundle 完全相同,這種形式上的區隔在交付溝通上彆扭。
購買 Nmap OEM 授權 本案不採,未評估成本。若日後產品策略需要零客戶設定的 nmap 整合,這是唯一能合法 bundle 的途徑,列為備查。

拍板走 SSH 型的理由 · 「最沒爭議」

法律上落在 NPSL 自留出口內;技術上與已上線的 OpenSCAP 同構(可複用 SSH 連線處理、私鑰暫存、host_failures 逐台回報);對客戶只需在指定主機 apt install nmap無任何 docker 操作

改案的連帶影響

本案工作內容(依 D9 改案後)

驗收結果 實跑通過

對真實網段實跑一輪,HTML 證據可產出並進入證據池

§8

決策 D1–D12

決策全數拍板——原先唯一保留的 D7 content 層已於 2026-07-30 由決策者裁示收斂範圍,本案不再有開放問題。(原 D10 隨其對應工具一併移出本案,其餘編號維持不變,避免與先前溝通過的編號錯位。2026-07-31 更新:SonarQube 已重新納入為 FR-058.5,決策以 D13–D16 定案、D10 編號維持 tombstone 不重用,詳見 design.md §2 與 §4.6,本稿不重列。)實作期間有兩次改案與一項追加D4 改版(ZAP 證據 PDF→HTML)、D9 改案(Nmap CLI 型→SSH 型)、D12 追加(ZAP 登入後掃描的 SPA 限制,實測後補列)。

D1 · 任務層敏感參數的實作範圍 已拍板

問題:只做加密存取,還是連 FE 執行紀錄的顯示剝除與稽核 log 一起做?

影響範圍:BE 加解密接線、FE 執行紀錄渲染、稽核事件內容。

定案:一起做。否則等於「加密了卻在 UI 洩漏」——資料庫是密文,但畫面上照樣看得到帳密,防護實質等於零。稽核 log 同理,寫入敏感值等於在另一張表留下明文副本。

D2 · ZAP 掃描深度的預設值 已拍板

問題:三種模式——被動 baseline(安全快速)/主動掃描(會產生實際攻擊流量,需目標方授權)/登入後主動(最有價值但需要測試帳號)。預設給哪一個?

影響範圍param_schema 的預設值、FE 警語文案。

定案:schema 三種都支援,預設給被動。主動類選項在 FE 加明確警語(會產生攻擊流量,請確認已獲目標方授權)。預設值選錯的代價不對等——預設被動最多是掃得淺,預設主動則可能在使用者未察覺的情況下對正式環境送出攻擊流量。

D3 · ZAP 服務的部署形態 已拍板

問題:客戶自備已安裝的 ZAP daemon(同 OpenVAS 模型)/ agent 端自帶 ZAP container?

影響範圍:agent image 大小與維護責任、setup_guide 內容。

定案:客戶自備。與 OpenVAS / OpenSCAP 一致,agent 不背負拉容器的責任。setup_guide 欄位寫清楚 daemon 啟動指令與 API key 設定方式。使用者公司已安裝 2.17.0,這條路本來就是現況。

D4 · ZAP 證據格式 已拍板 2026-07-30 改版

問題:人可讀格式(給人看)+ JSON(解析統計)雙取,或只存單一格式?

影響範圍:connector 取報告邏輯、證據池內容。

定案(2026-07-30 改版):雙取,精神對齊 OpenVAS connector 既有模式——core.htmlreport() 回傳的 HTML bytes 上傳當證據(檔名 ZAP掃描報告_<target>_<date>.html)、core.jsonreport() 回傳的 JSON 只在記憶體解析出 summary 不上傳。這樣證據池維持人可讀,統計數字又有結構化來源,且不會在證據池塞入兩份等價內容,ZAP 端零設定、維持 D3 部署模型。

改版緣由:原定案「PDF 上傳當證據(reports.generate)」實作時發現不可行——ZAP 的 reports API 只把檔案寫進 ZAP daemon 主機的磁碟、回傳路徑字串,官方沒有回傳檔案內容的端點(issue #7821 至今未實作);而 D3 定的部署模型是遠端 daemon,agent 取不到那個路徑的檔案。

改版時被排除的走法:①「PDF + 要求客戶開檔案傳輸通道」——違反 D3 部署模型,等於從後門把部署負擔帶回來;②「檔案傳輸為主、HTML 自動退回」——兩條路徑的長期維護與測試成本換一個罕用 fallback,違反本案「不做無意義測試」紀律。

已知風險與處置:證據池 PDF 預覽鏈不吃 HTML,接受「可下載、不可預覽」為第一批出貨標準;若日後要求可預覽,可在 agent 端用既有 LibreOffice --convert-to pdf 補(ZAP 端仍零設定),但轉檔品質未驗證故不預做。

D5 · InSpec 版本選擇 已拍板

問題:官方 InSpec 商業 binary(需 Chef EULA + license key)/ CINC Auditor(Apache 2.0)?

影響範圍:授權合規、agent image 內容、客戶部署文件。

定案:CINC Auditor。授權乾淨(Apache 2.0),且是 Progress 官方認可的 drop-in 替代品,功能等價。採用商業 binary 會把 license key 管理問題帶進每一個客戶部署。

D6 · InSpec connector 的 transport 範圍 已拍板

問題:只做 WinRM(Windows)/ SSH + WinRM 雙 transport 一次到位?

影響範圍:FR-058.2 工作量、FR-058.3(GCB)的可複用程度。

定案:雙 transport 一次到位。同一套 CLI 與 profile 格式,多做一個 transport 的邊際成本很低,而 GCB 的 Linux 線立刻就能複用。只做 WinRM 的話,GCB Linux 那條線之後還要回頭補一次。這條同時是 D7 引擎層的實作依據——GCB 兩邊都掛在這個 connector 上。

D7 · GCB 的引擎優先與 content 後置 已拍板

釐清:GCB 本質上與 OpenSCAP 一樣要分 Windows 與 Linux 兩邊,差別只在 content 不同。這其實是兩個不同層次的問題引擎覆蓋範圍(兩邊都要掃得到)與 content 產製(每條 TWGCB-ID 都要人工轉成檢測規則,且要跟著改版維護)。前者是一次性工程、後者是持續性投入,兩者不必綁在同一個決策裡——本案的裁示就是把它們拆開,只做前者。

引擎層 已拍板

Windows 與 Linux 雙邊一次到位,由 FR-058.2 的 CINC Auditor connector 承擔,GCB 不另立引擎。CINC Auditor 本來就是 SSH 加 WinRM 雙 transport 的同一套引擎(見 D6),GCB 的兩邊本來就該由它一起處理,不需要拆成兩條路徑。這一層跟著 FR-058.2 走,不產生額外的引擎工程。

content 層 已裁示:不納入本案

決策者裁示:本案只做引擎與一個最小 Demo profile,證明端到端鏈路可運作即可。Demo 取 Windows 最單純的一個基準,驗證 WinRM 連線 → 認證 → profile 執行 → 結果解析 → 證據上傳整條走得通。不做 content 工程。

以下兩件事都列為後續,日後另案評估

  • content 產製:把 TWGCB-ID 逐條轉成檢查規則(含 Windows GPO 產生器、Linux 人工撰寫)。
  • 後台 content 更新機制:上傳 / 版更 / 套用,讓 content 可獨立於程式版本更新,不必為了換一份基準就重新發版 agent。

這樣切的理由:FR-058.3 因此有明確且短的終點,而且它驗證的正是風險最高的部分——WinRM 連線、認證、profile 執行、結果解析。content 反而是最沒有技術風險的部分(純人力活),一條規則也能證明鏈路通

範圍裁示 · macOS 15 一併排除 已排除

macOS 15(TWGCB-01-015)不納入本案範圍。理由:基準本身 2026/6 才發布、版本尚新,且現有 connector 未曾處理 macOS。日後另議。

本案的設計約束 · content 必須可外部載入 已拍板

content 不可硬編進 connector 或 agent image,必須設計成可從外部載入。這是本案要做到的設計約束,不是後續事項——雖然 content 產製與後台更新機制都不在本案,但接口現在就要留。

理由:content 本質上是會持續變動的資料,不該綁在程式版本裡。日後要接後台更新機制時,若當初把 profile 打包進 agent,等於每次更新 content 都要重新發版並更新所有客戶站點的 agent這個接口現在留,成本極低;事後補,要改動 connector 結構。

定案:引擎先行、content 後置,並在本案就把外部載入接口留好。FR-058.3 的驗收條件是「鏈路可運作」,不是「涵蓋多少規則」。

D8 · GCB 引擎選擇 已拍板

問題:InSpec profile / XCCDF + OVAL(沿用 OpenSCAP)?

影響範圍:內容格式、後續維護的技能需求。

定案:InSpec profile。Ruby DSL 比 XCCDF + OVAL 好寫也好維護,而且 Windows GCB 只有這條路走得通——選 XCCDF 等於同一份基準要維護兩種格式(Linux 用 XCCDF、Windows 用 InSpec),長期成本明顯較高。這與 D7 引擎層的定案一致:既然兩邊都由同一套 CINC Auditor 執行,content 自然也只需要維護 InSpec profile 一種格式。

D9 · Nmap 的整合型態與零憑證放行 已拍板 2026-07-30 改案

原問題:放寬 start_execution 的憑證檢查(依工具是否宣告需要憑證判斷)/為 Nmap 建立一筆空的租戶設定當佔位?

影響範圍detection_tools 表結構、start_execution 檢查邏輯、Nmap 的 connection_type 與 agent connector 形狀。

定案(原有部分,仍然成立):放寬檢查。detection_tools 增加「是否需要憑證」的宣告欄位(requires_credentials),讓平台知道零憑證是一種合法狀態,而不是塞假資料繞過檢查。塞空設定當佔位會留下一筆語意不明的資料,日後看到的人無法判斷那是刻意還是遺漏。此為平台級能力,改案後仍然正確且保留——只是 nmap 不再是它的第一個使用者。

改案(2026-07-30):Nmap 由 connection_type='CLI' 改為 'SSH'原定案是「agent 本機執行 CLI、客戶自備 nmap、不 bundle 理由僅記『NPSL 授權』」。改案後:nmap 由客戶安裝在自己指定的執行主機,agent 透過 SSH 登入該主機執行,需要 SSH 憑證requires_credentials=TRUE)。

兩個獨立的改案理由:① 技術矛盾——agent 跑在 container 內看不到主機上的 nmap,「不 bundle」與「客戶自備、agent 呼叫得到」互相打架;② 授權查證——NPSL v0.95 §3 的衍生作品定義明文涵蓋「專門執行本軟體並解析其結果」的程式(Is designed specifically to execute Covered Software and parse the results),我們的 connector 正中此條,「只用子行程呼叫」這個 GPL 慣用免責論點在 NPSL 底下不成立;但同條末段自留出口——不主張控制「執行使用者早已安裝在自己系統上的 nmap」的軟體,SSH 型正落在此描述內

被排除的替代方案:掛載主機 binary 進容器(共享函式庫版本賭運氣、可能弄壞 agent)/客戶自建一層 image(法律成立但每次升版都要重 build,且技術結果與 bundle 相同、交付溝通彆扭)/購買 Nmap OEM 授權(本案不採,列為備查——若日後要零客戶設定的整合,這是唯一合法 bundle 途徑)。完整推導見 §7.1

連帶影響connection_type='CLI' 至今仍無任何實際使用者;agent connector 範本從「本機子行程」改為對照 openscap.py 的 SSH 多目標模式。

D11 · _TOOL_ID_TO_CODE 硬編映射的處理 已拍板

問題:維持硬編(每加一個工具就要改 agent 並重新發版)/改為由派工 payload 夾帶 tool code?

影響範圍:agent factory、派工 payload 結構、三環境 seed 一致性要求。

定案:改為派工夾帶 code。一次加四個工具會讓硬編映射的脆弱性放大——DEV / STG / POC 三環境的 seed id 必須完全一致,錯一位就整條派工錯配到別的 connector(FR-057 的 seed SQL 檔頭已留有此警告:例如 ZAP 任務被送給 OpenSCAP 執行),而且這種錯誤不會拋例外、只會安靜地跑錯工具。趁這次一併解決。

界線說明:這項改動不會讓新增工具完全免於動 agent——connector 實作本身仍必須裝進 agent(那是實際執行掃描的程式碼),因此每加一個全新工具,agent 仍需更新一次。D11 消除的是另一個更脆弱的問題:額外維護一份 id 對照表、且三個環境的 seed id 必須完全一致

D12 · ZAP 登入後掃描的 SPA 支援範圍 已拍板 2026-07-30 實測後追加

問題:本案的登入後掃描要不要涵蓋 SPA(單頁式應用)?

影響範圍:ZAP param_schema 的登入四欄語意、setup_guide 警語、共用 ZAP daemon 的服務穩定性。

定案:本案(第一批)只支援傳統表單登入(form-based),param_schemalogin_url / login_username / login_password / logged_in_indicator 四欄即為此模型。SPA 的登入後掃描列為後續另案(Notion CM-984),本案文件與 setup_guide 明白標示此限制,不做半套。

技術原因有三,且三者必須同時解決才會通:① 表單登入無效(SPA 靠 JS 呼叫 API 取 token,需要 json-based 認證);② token 不會自動帶(SPA 多用 JWT 存 localStorage 靠 header 帶,ZAP 預設是 cookie-based session,需要 script-based session management);③ logged_in_indicator 對 SPA 不可靠(ZAP 比對 HTTP 回應原始內容,SPA 初始 HTML 是空殼、文字要 JS 渲染才進 DOM)。

🔴 實測後果更正(2026-07-30,嚴重度高於原記載一個層級):原記載「掃描仍會完成、只是涵蓋範圍侷限於登入前頁面」是錯的。第 ③ 項的重跑登入是無限迴圈——認證失敗持續累積,達到 ZAP 2.17.0 Insights 機制門檻後 ZAP daemon 主動關閉自己,掃描根本不會完成。log 關鍵行:Shutting down ZAP due to High Level Insight: HIGH : EXCEEDED_HIGH : insight.auth.failure : 100。由於 ZAP 是全租戶共用的單一服務,單一 SPA 登入任務會連帶打斷同一台 ZAP 上其他正在執行的掃描(實測同時發生:另一任務在重啟空檔收到 Connection refused)。這已非報告品質問題,而是營運層級風險。完整因果鏈與實測數據見 §4.2

範圍界定僅限登入後掃描模式。被動與主動模式不建 context、不設認證、不比對特徵字串,不累積認證失敗,碰不到 Insights 門檻——兩者皆已實測通過、完全不受影響

被排除的三個折衷:① 本案一併擴充 json 認證 + script session(三件獨立工作,會拖住已可出貨的部分,而第一批核心價值不依賴登入後掃描);② 只做 json 認證、session 管理留待日後(沒有中間地帶——認證送出去但 token 帶不進後續請求,結果與完全不做相同,卻已付出成本並在 UI 留下「看起來支援了」的誤導);③ 只換掉 logged_in_indicator 的判定(只解第三個原因,前兩個未解則本來就進不去)。

已知但本案不做(只記錄):connector 端的自我保護——登入模式下自行偵測認證失敗異常累積、逼近門檻前主動中止並回明確訊息。可獨立於 SPA 支援先做,決策者 2026-07-30 裁示本案只記錄不實作。

§9

風險與既有技術債

以下不是待決策事項,是實作時會遇到、或本案會放大的既有問題。列出來是為了在拆任務時不被意外絆住。

項目現況本案的關聯
tenant_config_id 從未被寫入 job_execution_detection_tools.tenant_config_id 永遠是 NULL,實際靠 (tenant_id, detection_tool_id) 的 UNIQUE 約束回退查詢。 加工具不會惡化,但這個欄位存在卻不被使用,日後看到的人容易誤判其用途
證據關聯靠字串前綴 證據與執行的關聯是靠 job_evidences.description 的字串前綴 [檢測工具] {agent_task.uid}——脆弱的字串耦合。 四個工具都會走這條路徑,任何改動 description 格式的需求都會全面波及
id=3 SonarQube 佔位列 detection_tools 內仍有 FR-056 留下的 id=3 sonarqube 佔位列,status='coming_soon'config_field_schema 為空。 (2026-07-31 更新)原「本案不處理維持 coming_soon」已被推翻——SonarQube 重新納入為 FR-058.5,此列改由 T-5.1 UPDATE 成可用工具(不是 INSERT 新列,詳見 design.md §4.6 seed 特殊點)。原記載「硬編 id 表中無對應 connector 的一格」問題在 D11 解耦 + T-5.2 補 connector 後消失
_pick_agent 無負載平衡 直接取 agents[0] 工具變多後同一站點併行任務量上升,單一 agent 被打滿的機率提高
心跳延遲 5 分鐘 agent 心跳預設 300 秒,任務發佈到實際開跑最長會有 5 分鐘延遲。 UX 這是設計上的正常行為,但 UI 要讓使用者理解「按下開始後沒有立刻動」不是故障
FE 硬編死碼工具清單 WorkflowSetupEditor.vue 有一段 toolsMenu = ["Nessus","Nmap","OWASP ZAP","Wireshark"],與 detection_tools 完全脫鉤。 會造成混淆 本案加入 ZAP 與 Nmap 之後,畫面上兩處都出現 Nmap,但其中一處是死的。建議一併清掉——已列入本案收尾的技術債清理(清掉前須先確認該段是否只是選單裝飾,若有實際功能要改成讀 API 而非直接刪)
ZAP daemon 為全租戶共用單一服務 不是每個任務一個實例。實測已證實單一任務可造成全域中斷(SPA 登入掃描撞 Insights 門檻 → daemon 自關 → 其他任務收到 Connection refused)。 營運層級 本案以 setup_guide 警語與文件記載處置,connector 端自我保護裁示不實作(見 D12 與 §4.2)。這條風險在 SPA 支援(CM-984)落地前持續存在
config.detection_tools 無 i18n 機制 name / description / setup_guide 與 param_schema 的 label 全是中文寫死在 DB,切換英文介面整頁仍是中文。FR-056 建表時就存在。 平台級缺口 本案一次加四個工具、又大幅擴充八張卡片的描述文案,缺口被放大但未惡化結構。決策者裁示先不動
Windows GPO tattoo settings GPO 設定分兩種位置:HKLM\Software\Policies 底下屬正規政策區、套用前會先清空;不在此位置的設定會殘留(業界稱 tattoo settings)。 誤判風險 檢查規則若讀取非 Policies 路徑,可能讀到上次殘留的舊值而誤判為通過,實際上政策根本沒生效。在稽核情境屬嚴重問題(報告說合格、實際不合格)。日後撰寫 content 時必須處理,本案 Demo profile 也應避開此陷阱
文件債 docs/claude/database-schema.md 完全沒有 config schema 與 detection 三表的記載(grep 零命中)。 本案要動這三張表,是補上記載的合適時機
§10

階段拆分與分批出貨

依已拍板的優先序切成 5 個子需求加 1 個橫向項。2026-07-30 決策者進一步裁示分批出貨、ZAP 優先上線——不再四個工具一起收,改成三個批次序列推進,批次結束才做一次端到端測試。子需求本身的內容與優先序不變,變的是交付節奏。

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
graph LR
  subgraph B1["第一批 · ZAP 上線 (CM-958〜970)"]
    PX["橫向項 FR-058.X
_TOOL_ID_TO_CODE 解耦
(D11 已拍板)"] P0["FR-058.0
任務層敏感參數
(平台前置)"] --> P1["FR-058.1
ZAP connector"] PX --> P1 end subgraph B2["第二批 (CM-971〜979)"] P2["FR-058.2
CINC Auditor connector
SSH + WinRM"] --> P3["FR-058.3
GCB
(引擎 + Windows Demo)"] end subgraph B3["第三批 (CM-980〜983)"] P4["FR-058.4
Nmap
SSH 型 (D9 改案)"] end B1 -->|"批次結束測一次"| B2 B2 -->|"併第二批一起測"| B3 classDef pre fill:#F6EBD5,stroke:#9C6B12,color:#14201F; classDef chain fill:#E2F0F1,stroke:#0E7C86,color:#14201F; classDef indep fill:#E4F1EA,stroke:#2E7D5B,color:#14201F; classDef cross fill:#E1ECF7,stroke:#2B6CB0,color:#14201F; class P0 pre; class P1,P2,P3 chain; class P4 indep; class PX cross;
分批出貨:三個批次序列推進(框與框之間的箭頭),批次內部維持原有依賴(.0 → .1、.2 → .3)。黃底為前置、藍底為有依賴鏈、綠底為獨立、淺藍為橫向項。第三批併第二批一起測,實際只測兩輪
子需求大致工作內容依賴批次 / 節奏涉及 repo
FR-058.0
任務層敏感參數
BE:param_schema 支援 secret: true、存 tool_params 前加密、派工組裝時解密。FE:欄位渲染與執行紀錄剝除。依 D1 決定是否含稽核 log。 第一批 Session 1 BE FE
FR-058.1
ZAP connector
SQL seed(API 型態 + config/param schema + setup_guide);agent zap.py(依 scan_mode 分支、雙格式取報告、cancel/timeout);對齊 zaproxy 0.6.0 與 2.17.0 行為變更。 FR-058.0 第一批 Session 2;僅登入後掃描需等 .0 BE agent
FR-058.2
InSpec / CINC connector
SQL seed;agent inspec.py(SSH + WinRM 雙 transport、--reporter json 統一解析);agent image 加入 CINC Auditor。吸收並結案 Notion CM-953 第二批 BE agent
FR-058.3
GCB/Windows 組態檢測端到端驗證
SQL seed;引擎直接複用 .2 的雙 transport connector(Windows 走 WinRM、Linux 走 SSH,不另立引擎);跑通 Windows 一條最小 Demo profile 的完整鏈路(WinRM 連線 → 認證 → profile 執行 → 結果解析 → 證據上傳);content 外部載入接口(不硬編進 connector 或 agent image)。驗收=鏈路可運作,不是涵蓋規則數。content 產製與後台更新機制依 D7 不納入本案。 FR-058.2 第二批 .2 完成後 BE agent
FR-058.4
Nmap
平台調整:start_execution 放行零憑證工具(依 D9,平台能力保留);SQL seed(SSH 型 + requires_credentials=TRUE——D9 改案,原定 CLI + 零憑證);agent nmap.pySSH 登入執行主機-oX + XML 解析,對照 openscap.py)。 第三批 併第二批一起測 BE agent
橫向項 FR-058.X
_TOOL_ID_TO_CODE 解耦
派工 payload 夾帶 tool code(欄位名 detection_tool_code),agent factory 改依 code 取 connector,移除硬編 id 映射(D11 已拍板)。 第一批 與 ZAP 同批,不另開 session BE agent

10.1 原排程建議(已被分批出貨取代,保留備查)

已於 2026-07-30 被下方分批出貨計畫取代

以下這段是「三條線並行」時期的排程建議。決策者改採分批出貨、ZAP 優先上線後,並行前提已不成立,此段不再是執行依據,保留以還原推導軌跡。取代它的安排見 §10.2。

若人力允許並行,FR-058.0 與 FR-058.2 同時起跑是最有效率的開局:前者解鎖 ZAP,後者解鎖 GCB,兩條依賴鏈同時前進。橫向項(_TOOL_ID_TO_CODE 解耦)建議排在第一個新 connector 落地之前做掉——等四個 connector 都寫完再回頭改,要重測的範圍會擴大四倍。

10.2 分批出貨計畫(2026-07-30 拍板)

改成三個批次序列推進,每批結束才做一次端到端測試。優先序本身(ZAP → InSpec → GCB → Nmap)沒有變,變的是「分批出貨而非三線並行」。

批次內容卡號範圍端到端測試
第一批
ZAP 上線
ID 解耦(FR-058.X)+任務層敏感參數(FR-058.0)+ZAP(FR-058.1) CM-958〜970 批次結束測一次
第二批 InSpec / CINC(FR-058.2)→ GCB(FR-058.3) CM-971〜979 批次結束測一次
第三批 Nmap(FR-058.4) CM-980〜983 併第二批一起測

為什麼 ID 解耦仍然先做(理由已換)

原本把橫向項排在最前面的理由是「三條線同時開工會搶同一個 factory 檔」。改成序列執行後這個理由已不成立,但結論不變,換成另一組更硬的理由:

為什麼 FR-058.0 敏感參數留在第一批

技術上 ZAP 的被動與主動掃描不需要登入帳密,只有「登入後掃描」才需要 .0,理論上可以先出不含登入的 ZAP。仍決定留在第一批:

第一批的 session 切分(按技能與 repo,非按卡片順序)

Session負責卡號
Session 1
平台側(BE + FE)
T-X.1 派工 payload 加欄位 + .0 全部四個子任務 CM-959CM-962〜965
Session 2
agent 側
T-X.2 factory 改寫 + ZAP 四個子任務 CM-960CM-967〜970

兩個 session 可大幅重疊:Session 2 採「有 code 用 code、無 code 退回 id」的 fallback 寫法,不被 Session 1 擋住;只有最後的 T-1.4(登入後掃描)真正需要等 Session 1 的解密接線(CM-963)。欄位名稱固定為 detection_tool_code(§詳細設計已定),兩邊照此不需即時協調。

10.3 測試紀律

真正耗時的不是跑測試本身,而是重建 agent image、部署三台、等 300 秒心跳。因此測試動作依成本分兩類:

時機做什麼
做中持續做
便宜,不要省
import/語法檢查;對假 XML/JSON fixture 跑解析邏輯的單元測試;SQL 套 DEV 後 SELECT 確認 seed 進去。
留到批次結束才做
貴,不要重複
BE 重啟;agent 重建部署;真實掃描端到端;多環境套 migration。

每個子任務做完不要各自跑一次端到端

一輪至少 10 分鐘起跳(重建 image → 部署三台 → 等心跳),那正是分批出貨要避免的重複成本。端到端只在批次結束做一次;第三批併第二批一起測,全案實際只測兩輪。

10.4 Notion case 樹座標

母案:CM-957

子需求母卡子任務
FR-058.X ID 解耦CM-958CM-959 / CM-960
FR-058.0 任務層敏感參數CM-961CM-962〜965
FR-058.1 ZAPCM-966CM-967〜970
FR-058.2 InSpec / CINCCM-971CM-972〜975
FR-058.3 GCBCM-976CM-977〜979
FR-058.4 NmapCM-980CM-981〜983

27 張卡,Case No 連號無斷號。另有 CM-953(異質 OS 檢測方案)已由 FR-058.2 吸收並結案。實作期間另補開 CM-984CM-986CM-988CM-992 等卡——見 §11。

§11

實作期間追加的平台級能力(2026-07-31 回填)

以下四項不在原規劃的 27 張卡內,是實作與實測過程中暴露出來的平台缺口。共同特徵是:都由某個具體工具觸發,但都不是那個工具的特例——因此一律做成宣告驅動或平台級機制,而非在程式碼裡寫工具分支。

11.1 互斥憑證組別(credential_group_schema

觸發者:CINC Auditor 是平台第一個「一個工具帶兩組互斥憑證」的工具——SSH 憑證用於 Linux 目標、WinRM 憑證用於 Windows 目標,使用者通常只填打算掃的那一組。GCB 會是第二個,所以做成平台能力。

#關掉的缺口症狀解法
測試連線不知道該用哪組憑證 BE 下發的 probe 參數只有 {"host": ...}不含 transport;agent 的 _resolve_transport() 未指定時預設 SSH。只填了 WinRM 憑證的使用者按「測試連線」,收到「本任務的連線方式為 SSH(Linux 目標),但未填寫 SSH 稽核帳號」——訊息本身很清楚,但它回答的是使用者沒問的問題 新增 credential_group_schema 宣告欄位,FE 在測試連線對話框讓使用者明確選一組
設定頁 11 個欄位平鋪一列 看起來全部必填,使用者看不出「這幾個一組、那幾個一組,只填一組就夠」 config_field_schema 逐欄加 group + hint,FE 依宣告分區渲染(本輪 UI 再改為左右並排獨立面板,Dialog 依組數動態加寬)

兩個關鍵設計決定(後續要改的人先讀這段)

① 不做自動偵測,由使用者明確選。「看使用者填了哪組就猜」在兩組都填齊時無從分辨——而那正是真實情境(客戶同時有 Linux 與 Windows 目標)。猜錯產生的恰恰是本項要消滅的誤導訊息。多問一題的代價遠低於答錯一題。未選定時測試連線鈕保持 disabled,不落回預設值;宣告了組別卻沒選 / 選了不存在的 key 一律明確回失敗,不預設挑第一組,也不記 last_test_result(沒真的測過)。

② FE 只送組別 key,probe 參數由 BE 依自己的 DB 宣告組出。request body 只收 credential_groupFE 不得帶 probe 參數的 key 或 value——probe 參數會一路流到 agent connector 決定程式路徑(其他工具則用來組檔案路徑),讓客戶端自由命名參數等於開一個注入面。FE 側也不硬寫任何協定字面值(transport / SSH / WinRM),全部由後端宣告驅動。

零影響保證NULL / 缺席=該工具無互斥組別,測試連線與設定頁行為與加這欄之前完全相同。實查確認目前只有 inspec 一列有宣告,其餘工具全為 NULL。agent 側零 code 異動——_resolve_transport() 本來就吃 probe params,缺的只是 BE 沒傳。

Schema 形狀:{probe_param_key, prompt_label, groups:[{key, label, description, probe_param_value}]}為什麼不塞進 config_field_schema:那是一個 JSONB 陣列,加頂層 metadata 會把形狀從陣列變成物件,所有讀取端都要跟著改。Notion CM-986

11.2 憑證部分更新改 merge 語意

Bug · 實測已發生,非理論風險

兩端對「沒送的 secret 欄位」理解不一致:FE buildPayload() 只送有填值的 secret(配合 UI 對使用者的承諾「留空=沿用原值」),BE update_tenant_config() 卻是整包 encrypt 覆蓋——沒送的 key 就消失了。

單一 secret 欄位的工具碰不到(只有一個 key,送不送都正確)。CINC 是第一個有三個 secret 欄位的工具(SSH 私鑰 / SSH 密碼 / WinRM 密碼),問題才被採出來——只填 SSH 私鑰存檔後,WinRM 密碼確實遺失、需重新填寫

危險之處在於它不會報錯——存檔看起來成功,直到下一次用到那組憑證才發現它不見了。修法:改 merge 語意(解密既有 secret dict → 新送來的 key 逐個覆蓋 → 整包重新 encrypt 回寫)。刻意不支援「清空單一 secret」——merge 語意下「把某個 secret 改成空」與「不動它」在 payload 上完全同形,要支援得引進額外的明確清空標記,而該需求實際不存在;整組清空走既有 reset 路徑,不受本改動影響。Notion CM-987

11.3 取消執行中的檢測任務

觸發者:CINC 對 Windows 的完整 baseline 掃描動輒十分鐘起跳(實測 894 項檢查),發現參數填錯時沒有任何止損手段

新增 endpoint POST /detection-tools/jobs/<job_uid>/cancel復用 CM-931 已建立的取消鏈路(不另寫一套)。FE 在任務執行抽屜內,每筆 running 狀態紀錄旁加「取消執行」按鈕(confirm → 成功 toast → 刷新,loading 防連點)。

設計要點 · 成功才標 cancelled,agent 離線就回 409 不標記

這是刻意的——若「標完就算取消」,agent 連不上時會出現「畫面顯示已取消、實際上那個掃描還在目標主機上跑」的假取消。既有機制的行為是直推 agent 真中斷,本項沿用該語意。新 error code DETECTION_TOOLS_409005DETECTION_TOOL_NO_RUNNING_EXECUTION)。

順手補齊:FE 從 CM-931 時代就漏了 DETECTION_TOOLS_409002409004 三條 i18n——取消失敗時畫面顯示不出原因(使用者只看到錯誤碼)。一併補上。Notion CM-988

11.4 requires_credentials / requires_target_host 宣告欄位

兩個宣告欄位,共同目的是把「這個工具需要什麼」從程式碼的硬判改成 DB 的宣告

欄位解什麼預設值與理由
requires_credentials D9 的零憑證放行——讓 start_execution() 知道「這個工具本來就不需要租戶層憑證」是合法狀態,不是設定遺漏。 預設 TRUE(往嚴的方向失敗):新工具忘了宣告時行為等同現況,不會意外被放行。目前所有工具皆為 TRUE——第一個真正的零憑證使用者尚未出現(原定的 Nmap 在 D9 改案後改走 SSH 型)。
requires_target_host 解除 FE 原本硬判 connection_type === 'SSH' 的耦合。測試連線需要問主機的真正原因是「租戶層憑證不含 host」(FR-057 D1 定的),與走哪種協定無關——用協定當代理判準遲早會錯。 預設 FALSE(同樣往嚴的方向):忘了宣告不會拿一個使用者答不出來的問題擋在測試連線前面。

Notion CM-981。這兩欄與 §11.1 的 credential_group_schema 是同一種手法——平台對具體工具維持零知識,靠工具自己的宣告行事,這正是 FR-056 tool-agnostic 精神的延伸。

11.5 四工具驗收狀態總表

工具code連線型態驗收狀態
ZAPzapAPI(客戶自備 daemon) 被動 /主動 登入後對 SPA (會打掛共用 ZAP,見 D12 / §4.2;後續案 CM-984
CINC AuditorinspecSSH(Linux)+ WinRM(Windows) ✓ 雙 transport 實跑通過151 為 pass 131 / fail 60、160 為 pass 300 / fail 472
NmapnmapSSH(登入客戶的執行主機再掃目標) ✓ 實跑通過HTML 證據可產出
GCBgcb同 CINC(複用引擎,D7) ✓ 鏈路實跑通過但 Demo profile 僅 2 項、僅供鏈路驗證,不足以對客戶展示CM-992

上版前必須處理 · 非 DEV 環境的 agent 版本落差

POC / STG 上這四個工具已顯示 available,但兩地的 agent 停在舊版、沒有對應的 connector——使用者若在那邊設定並派工,會拿到跑不動的結果。目前無人使用(租戶設定 0 筆、派工 0 筆),但上版時必須一併處理

這一條同時是本案的一則教訓:seed(讓工具在 UI 上出現)與 connector(讓它真的跑得動)是兩件事,分屬雲端與 agent 兩個部署單位。migration 同步到三環境時若沒有同步 agent,就會製造出「看得到、按得下、跑不動」的狀態。