# FR-056 檢測工具整合平台 — 設計文件

> 狀態：設計定案（D1–D11 已拍板）｜建立日期：2026-07-26｜母案含 4 子需求 × 15 子任務
> 討論稿（含流程圖）：[`discussion.html`](./discussion.html)（Artifact 亦可線上瀏覽）

## 變更紀錄

| 日期 | 變更 | 對應 |
|------|------|------|
| 2026-07-26 | 初版設計定案，D1–D11 全數拍板（照建議） | FR-056 母案 |
| 2026-07-26 | T-2.3 落點修正：任務類型 UI 應加在 `ProjectPlanningView.vue`（規劃頁控制項實作 Tab 任務面板），非 `TaskSetupView.vue`（後者為斷頭半成品頁：ui_routes 無條目、無導覽點、fetchTree API 路徑在 BE 未註冊）。首次實作（FE `f7d4968`）revert 重做；BE 不受影響。TaskSetupView 是否清除列收尾待辦。 | T-2.3 返工 |
| 2026-07-26 | plan 遺漏補洞：`detection_tool_param_schemas` 零 seed（56.1 只 seed 連線欄位、漏掃描參數 schema），而 connector `params.hosts` 必填 + T-2.3 UI 動態拉 schema 渲染 → 現況整條鏈掃描必炸「缺少掃描目標」。T-2.3 加 Step 0.5 補 OpenVAS param_schema seed（hosts 必填 + timeout_sec/scan_config_id/scanner_id 選填，key 與 connector 消費端對齊）。另註記：連線 4 欄位對 python-gvm 已足，但 port 9390 TLS 直連有部署前提（Docker 版 GVMd 常只開 Unix socket），列真實環境驗證第一檢查項。 | T-2.3 Step 0.5 |

---

## 1. 需求背景與目標

客戶希望在專案任務中把「執行檢測工具」自動化：租戶設定好自家的檢測工具（第一個接 OpenVAS，未來 Nessus / SonarQube 等）→ 任務指定為「檢測工具執行」→ 按下開始 → 透過裝在客戶端的 Agent 觸發掃描 → 結果自動回收成該任務的證據 → 通知負責人 → 完成任務。目的是省掉 user 手動跑整條流程。

### 端到端流程

```
設定檢測工具（url/key 加密存 config schema）
  → 安裝 & 註冊 Agent（沿用 FR-039 既有 mTLS 註冊）
  → 任務設為「檢測工具執行」+ 設掃描參數（IP/網址/設備）
  → 按「開始執行任務」
  → Agent 心跳領到派工 → 呼叫工具（API/CLI）→ 掃描完成回傳報告
  → 報告存回 job_evidences（source=DETECTION_TOOL）
  → 發信通知負責人
  → 依「完成模式」flag：auto 系統自動按完成 / manual 任務留在 PROCESSING 等人工手動完成（不新增狀態）
```

---

## 2. 現況盤點（決定工作量落點）

| 項目 | 狀態 | 說明 |
|------|------|------|
| FE 檢測工具管理頁 | 純 mock | `ToolPluginManage.vue`：7 工具寫死、零 API、啟用只收一個 key 欄位、重整即重置。要接真後端重做。 |
| Agent 認證鏈 | 已完備 | FR-039 的 `evidence-agent`（獨立 repo）自我註冊 + mTLS 憑證 + 心跳輪詢已可用，`agent_type` 已預留多型。認證這層不用重做。 |
| Agent 任務下發 | 完全沒有 | 無 command queue / job dispatch，心跳 payload 不夾帶待辦；Agent 端也無 executor。全新範疇。 |
| 任務類型 | 要擴充 | `GrcJobType` 寫死 StrEnum（只 general / survey），任務顯示真相在範本 BPMN `userTask`（`actionType`/`actionInfo`）。 |
| 證據自動上傳 | 有現成 pattern | `job_evidences.source` 已區分 `SYSTEM_UPLOAD` / `DRIVE_SYNC`；`ImportDriveFileHandler` 用系統帳號自動寫證據。掃描轉證據照抄，加 `DETECTION_TOOL` 來源。 |
| config schema | 不存在 | 目前僅 compliance / oscal / public / survey 四 schema。最接近的 tenant 設定表是 `tenant_drive_integrations`（每租戶一筆、加密 token）——當加密範式參考。 |

---

## 3. 決策定案（D1–D11）

| # | 決策 | 定案 |
|---|------|------|
| **D1** | config schema 定位 | **typed 表**，非通用 key-value blob。config 作為「設定類 schema 命名空間」，裡面放明確 typed 表（detection_tools 等），未來別種設定各自開 typed 表，不塞單一 blob。 |
| **D2** | 憑證加密方式 | **沿用 app 層對稱加密**（mirror `tenant_drive_integrations` 的 `*_encrypted` 欄位），金鑰走環境變數 / 部署密鑰不進版控。不引入新 KMS。 |
| **D3** | Agent 派工機制 | **心跳夾帶待辦**（heartbeat 回應加 `pending_tasks`），改動最小、認證零改。結果走獨立 result endpoint。推播（SocketIO）列未來，不進第一版。 |
| **D4** | 報告是否 parse 成結構化 findings | **第一版只存原始報告當證據**。parse 成 findings / 自動配對控制項是更大題目，獨立成未來 FR，不混入。 |
| **D5** | 工具 connector 架構 | **三層 adapter**（工具 × 連線型態 × 共用 core），沿用 FR-043/044 registry 泛化 pattern。第一版只實作 OpenVAS，介面留好。 |
| **D6** | 停用/重置的「有任務正被配置」提醒 | **軟提醒**：停用/重置前查「引用此工具設定的任務數」，彈窗提醒數量 + 放行，不硬擋（比照 FR-051 軟提醒）。 |
| **D7** | 部門（org_unit）層級設定 | **表結構留 `org_unit_id` 欄位（預留），第一版邏輯只做租戶層級**。未來下放部門不用改 schema。 |
| **D8** | 完成模式預設值 | **預設 manual（系統不自動按完成）**。掃描結果通常需人確認才算數；auto 則掃完自動 complete_job。**注意：manual 不是新狀態，只是系統不主動呼叫既有 complete_job，任務留在 PROCESSING 等人工手動完成**——零 JobStatus 新增、不動 jedi_flow_engine。 |
| **D9** | 憑證怎麼到 Agent | **(a) 雲端解密後隨派工下發**（走 mTLS 通道，Agent 用完即丟不落地）。雲端仍是憑證唯一保管處（加密存放）。不採 Agent 自持——要客戶在 Agent 端自設不實際，雲端 UI 統一設定才符合產品體驗。 |
| **D10** | OpenVAS 整合方式 | **API（python-gvm，GVM protocol）**，非 CLI。程式化控制掃描比解析 CLI 輸出穩定；與 FR-056.1 seed 的 connection_type=`API` 一致。 |
| **D11** | 掃描報告的 evidence_type | **用既有 `FILE`**，不用 `REPORT`。REPORT 型別會牽動其他判斷證據檔案的地方、改動面大；第一版 FILE 最安全，要區分檢測報告類證據未來另開需求。 |

---

## 4. 架構總覽

- 新增 `config` schema 放租戶設定與工具目錄（D1 typed 表）。
- Agent（`evidence-agent` repo）長出 executor 執行能力。
- 掃描結果沿既有證據管線（`job_evidences`）回流（D4 只存報告）。
- DDD 分層、`@transaction`/session 紀律、authz 守門、error code 規範一律照 CLAUDE.md，不破例。

```
雲端 Guidant AI
├── 前端：檢測工具管理頁 · 任務設置頁 · 任務執行抽屜
├── 後端（DDD）
│   ├── config schema（新）
│   │   ├── detection_tools              工具目錄（取代 FE 寫死清單）
│   │   ├── tenant_detection_tool_configs 租戶憑證（加密）
│   │   └── detection_tool_param_schemas  動態欄位定義（版更用）
│   └── compliance schema
│       ├── agent_tasks（新）            派工 + 狀態機
│       ├── detection_executions（新）    執行歷史
│       └── job_evidences                證據（既有，加 DETECTION_TOOL 來源）
└── ⇅ mTLS+JWT 心跳夾帶派工

客戶端
└── Agent（evidence-agent）+ 新 executor 模組 → OpenVAS（未來 Nessus/SonarQube）
```

---

## 5. 資料模型

### config.detection_tools — 工具目錄（取代前端寫死清單）

| 欄位 | 型別 | 說明 |
|------|------|------|
| id / uid | bigint / uuid | 主鍵 |
| code | varchar | `openvas` / `nessus` / `sonarqube`（唯一） |
| name / description | varchar / text | 顯示名稱、說明（可 i18n） |
| connection_type | varchar | `API` / `CLI`——決定 executor 走哪條 |
| config_field_schema | jsonb | 設定頁要填的欄位定義（key/label/type/required/secret） |
| status | varchar | `available` / `coming_soon`（灰掉不給設定） |
| enabled | bool | 平台層總開關 |

### config.tenant_detection_tool_configs — 租戶各自的工具設定與憑證

| 欄位 | 型別 | 說明 |
|------|------|------|
| id / uid | bigint / uuid | 主鍵 |
| tenant_id / org_unit_id | uuid | 租戶隔離（org_unit 預留，D7 第一版只租戶層級） |
| detection_tool_id | FK | 指向 detection_tools |
| credentials | jsonb / text（加密） | url / key / token / 帳密——app 層加密（D2） |
| field_values | jsonb | 對應 config_field_schema 的非機敏值 |
| status | varchar | `enabled` / `disabled` |
| last_tested_at / result | timestamptz | 最後一次測試連線的時間與結果 |

> **FE 管理頁需要「列出本租戶所有 config 含狀態」的讀取 API**（`GET /detection-tools/configs`），FE 才能正確渲染卡片（哪些工具已設定、enabled/disabled）。回傳只含 `has_credentials`（bool），**絕不回憑證明文/密文**。細節見 implementation-plan.md T-1.3 Step 9。

### config.detection_tool_param_schemas — 任務執行參數定義（版更用）

| 欄位 | 型別 | 說明 |
|------|------|------|
| id | bigint | 主鍵 |
| detection_tool_id | FK | 指向 detection_tools |
| version | int | 欄位定義版本——調欄位不覆蓋舊版 |
| param_schema | jsonb | 參數欄位定義（掃 IP 清單 / 網址 / 設備群 / 掃描 profile…） |

### compliance.agent_tasks — 派工單 + 狀態機

| 欄位 | 型別 | 說明 |
|------|------|------|
| uid | uuid | 主鍵 |
| tenant_id / agent_id | uuid / FK | 派給哪台 Agent |
| job_execution_uid | FK | 對應哪個稽核任務 |
| detection_tool_id | FK | 用哪個工具 |
| params | jsonb | 本次掃描參數快照 |
| status | varchar | `pending → dispatched → running → succeeded / failed` |
| result_ref / error | jsonb / text | 結果檔參照 / 失敗訊息 |

### compliance.detection_executions — 執行歷史（一次執行一筆，可查可下載）

| 欄位 | 型別 | 說明 |
|------|------|------|
| uid | uuid | 主鍵 |
| agent_task_uid | FK | 對應派工單 |
| job_execution_uid | FK | 對應任務 |
| started_at / finished_at | timestamptz | 執行區間 |
| status / summary | varchar / jsonb | 成敗 + 摘要（如發現幾項） |
| report_file_id | FK | 原始報告檔（→ upload_files，供下載） |
| evidence_id | FK | 轉出的證據（→ job_evidences） |

> 表名/欄位為草案，正式建表依 `sql-migration` 規範定案（GRANT cm_app、sequence 權限、schema_migrations 登記）。

---

## 6. Agent 派工與執行（D3）

- 既有心跳為輪詢式（Agent 每 N 秒主動 POST heartbeat）。派工採**心跳回應夾帶 `pending_tasks`**，Agent 領走 → ack（task→dispatched）→ 執行 → 回報 running → `POST /agents/tasks/{id}/result` + 報告檔（task→succeeded）。
- 認證整條沿用既有 `common/util/agent_auth`（mTLS + JWT），新 endpoint 掛同一套，不重新設計。
- 已知取捨：輪詢有心跳間隔延遲（掃描是長工，通常可接受）。要更即時再評估縮短掃描任務輪詢間隔或推播（未來）。

### 結果轉證據——沿用 DRIVE_SYNC 模板

Agent 回傳報告後，雲端照 `ImportDriveFileHandler` 同一套路：報告存 Minio → 寫 `job_evidences`，`source` 從 `DRIVE_SYNC` 換成 `DETECTION_TOOL`，`created_user` 用系統帳號（如 `detection-agent@system`）。既有證據列表、刪除連動邏輯自動涵蓋。

### 執行抽屜、完成模式與重掃（D8）

**完成模式只是「系統要不要幫忙自動按完成鍵」的開關，不新增任何任務狀態。** 掃描完成、證據入庫、發 mail 後，依任務設定的完成模式 flag：
- **auto（自動）**：系統自動呼叫既有 `complete_job`，任務 PROCESSING → COMPLETED。
- **manual（人工，預設）**：系統**什麼都不做**，任務**停在既有 PROCESSING 狀態**。執行人員收到通知信，自行判斷證據可用否：
  - 可用 → 手動按「完成任務」（走既有 `complete_job`，跟一般任務完全一樣）。
  - 不可用 → 在執行抽屜自己再按「執行」重新掃描，或自己補上傳一份證據，再手動按完成。

**關鍵：不新增 JobStatus 狀態值、不動 jedi_flow_engine 套件。** manual＝系統不主動呼叫 complete_job；重掃＝PROCESSING 下既有的「再執行」能力（每次一筆 `detection_executions` 獨立紀錄，不覆蓋前次、可下載比對）；手動完成＝既有 complete_job。

### 任務執行流程（沿用既有 JobStatus，零新增狀態）

```
任務 PROCESSING（既有狀態）
  → 按「開始執行」→ 派工 → Agent 掃描 → 回報告
  → 證據入庫(job_evidences) + detection_executions 落一筆 + 發 mail 通知負責人
  → 完成模式 = auto   → 系統自動 complete_job → COMPLETED
  → 完成模式 = manual → 任務仍 PROCESSING，等人工判斷：
        · 證據可用   → 手動 complete_job → COMPLETED
        · 不可用     → 抽屜再按執行（重掃，留歷史）或補上傳證據 → 再手動完成
  掃描失敗 → detection_executions 標 failed（不 silent）→ 任務仍 PROCESSING，可重試
```

> 「完成模式」只影響掃描完成後系統要不要自動按完成；任務狀態機完全沿用既有 JobStatus（TODO/PROCESSING/COMPLETED/CANCEL），零改動。完成模式值用 `auto` / `manual`（原「人工覆核」措辭改為單純「manual＝系統不自動完成」）。

---

## 7. 命名決策

表用 `detection_*`（檢測），非 `scan_*`：
- 產品既有詞彙就是「檢測工具」（FE 路由 `tool-plugin-manage`、選單「檢測工具管理」）。
- 比 scan 廣、又不失義：SonarQube 是靜態分析、Nmap 是探測、OpenVAS 是弱點掃描，但都是「檢測工具」；未來滲透測試 / 組態稽核工具也塞得進來。
- 避開兩個地雷：`assessment_*`（被 OSCAL 的 AP/AR/assessment_object 佔用）、`integration`/`connector`（被 `cloud_integration` / `tenant_drive_integrations` 佔用）。

---

## 8. 階段拆分與子任務

每階段可獨立上線、獨立驗收。子任務切到「一個 session 跑得完、有明確產出、可獨立 commit」。

### 依賴關係

```
FR-056.1（工具管理 config schema）── 地基
   ├─→ FR-056.2（任務類型 + 參數）
   └─→ FR-056.3（Agent 派工 + executor）  ← .2 / .3 可並行
              ↓
        FR-056.4（執行編排 · 證據 · 通知）── 收尾整合
```

### FR-056.1 檢測工具管理（config schema）— 依賴：無

| 子任務 | 範圍 | 產出/驗收 | 依賴 | Repo |
|--------|------|-----------|------|------|
| T-1.1 | 建 config schema + 三表 migration（GRANT cm_app、schema_migrations 登記）；seed OpenVAS | migration 套進 DEV，查得到表與 seed | — | BE |
| T-1.2 | BE：detection_tools 目錄唯讀 API（DDD 全層）；清單改由 DB 提供 | GET 清單回 OpenVAS + coming_soon；有 pytest | T-1.1 | BE |
| T-1.3 | BE：租戶工具設定 CRUD + 憑證加密（沿用 drive 加密範式） | 建/改/停用；DB 內憑證密文；有 pytest | T-1.1 | BE |
| T-1.4 | BE：測試連線 endpoint + 停用/重置「引用任務數」提醒（D6） | 測試連線回成敗；提醒回引用數 | T-1.3 | BE |
| T-1.5 | FE：檢測工具管理頁重做（真 API、動態欄位、啟用/停用/重置、測試連線、coming_soon 灰掉） | 整頁走真後端；重整持久化；mock 全移除 | T-1.2/1.3/1.4 | FE |

### FR-056.2 任務類型「檢測工具執行」+ 參數 — 依賴：FR-056.1（可與 P1 部分並行）

| 子任務 | 範圍 | 產出/驗收 | 依賴 | Repo |
|--------|------|-----------|------|------|
| T-2.1 | BE：GrcJobType 加 `detection_tool` + BPMN userTask 同步線（actionType/actionInfo 回寫 template xml） | 任務可存 detection_tool 類型並同步範本；有 pytest | —（可與 P1 並行） | BE |
| T-2.2 | BE：param_schemas 版本化定義 + 任務綁定工具＋參數快照 + 完成模式 flag | 任務存下工具/參數/完成模式；重開還在；有 pytest | T-1.1/T-2.1 | BE |
| T-2.3 | FE：任務設置頁——選檢測工具執行 → 選工具 → 動態參數欄位 + 完成模式選擇 | 設定期完整可存；不影響 general/survey | T-2.2 | FE |

### FR-056.3 Agent 執行能力擴充 — 依賴：FR-056.1（可與 .2 並行）

| 子任務 | 範圍 | 產出/驗收 | 依賴 | Repo |
|--------|------|-----------|------|------|
| T-3.1 | BE：agent_tasks 表 + 派工 service + 狀態機 + Agent capability 標記 | 能建派工單、查狀態；有 pytest | T-1.1 | BE |
| T-3.2 | BE：心跳夾帶待辦 + ack + 結果回收 endpoint（沿用 mTLS+JWT） | 心跳領到任務、result 回收改狀態；有 pytest | T-3.1 | BE |
| T-3.3 | Agent 端（evidence-agent）：executor 模組骨架——領任務、回報 running、送 result；沙箱/權限邊界 | 能收派工並回報；不含具體工具邏輯 | T-3.2 | evidence-agent |
| T-3.4 | Agent 端：OpenVAS connector（三層 adapter，API/CLI 兩型；mirror FR-043/044） | 手塞派工→跑 OpenVAS→回報告全鏈通 | T-3.3 | evidence-agent |

### FR-056.4 執行編排 + 轉證據 + 通知 + 歷史 — 依賴：.1/.2/.3

| 子任務 | 範圍 | 產出/驗收 | 依賴 | Repo |
|--------|------|-----------|------|------|
| T-4.1 | BE：結果轉證據 handler（detection_executions 落一筆 + 報告存 Minio → job_evidences source=DETECTION_TOOL；沿用 DRIVE_SYNC 模板） | result 進來自動生執行史 + 證據；有 pytest | T-3.2 | BE |
| T-4.2 | BE：執行編排——開始執行串派工、完成模式分岔（auto 呼叫既有 complete_job / manual 不做事）、失敗狀態、發信通知負責人 | auto & manual 各驗一次；失敗不 silent；負責人收信 | T-2.2/T-3.1/T-4.1 | BE |
| T-4.3 | FE：任務執行抽屜——開始執行 / 手動重新執行 / 執行歷史列表（狀態+摘要+下載）；manual 模式靠既有「完成任務」按鈕（不新增 UI） | auto & manual 全鏈手測通；重掃留歷史可比對 | T-4.2 | FE |

**總計 15 子任務（5 / 3 / 4 / 3）**，跨三 repo（BE / FE / evidence-agent）。每子任務 = 一張 Notion 子卡，掛在對應 FR-056.x 下、再掛回母案。

---

## 9. Notion 追蹤結構（三層）

- **第 1 層 · 母案 FR-056**：總體目標、四階段索引、討論稿連結、D1–D11 定案。
- **第 2 層 · FR-056.1~.4**：四子需求各一張卡，關聯回母案，帶交付/依賴/驗收。
- **第 3 層 · T-x.y**：15 張子任務卡，掛對應子需求下——session 認領顆粒，帶範圍/產出驗收/依賴/repo。

每張卡填「需求編號」欄位。session 做完子任務：commit + 回寫該子卡狀態，下個 session 從 Notion 看依賴接續，交接不歪。

---

## 10. 未來延伸（本 FR 範圍外）

- 報告 parse 成結構化 findings + 自動配對控制項（D4 排除，獨立 FR）。
- Nessus / SonarQube 等其他工具 connector（D5 介面已留）。
- 部門（org_unit）層級工具設定下放（D7 欄位已預留）。
- 即時推播派工（D3 SocketIO，取代輪詢延遲）。
