# FR-056 檢測工具整合平台 — Arc SUMMARY（手測驗收 + 收尾完成）

| 項目 | 內容 |
|------|------|
| 完成日期 | 2026-07-27 |
| Branch（三 repo 同名） | `feature/scan-plugin-integration` |
| Arc 範圍 | 四子需求（56.1~56.4）+ 五張手測子修正 case（CM-930~934）+ 本次收尾工作包（CM-935：STG migration / 使用手冊正式化 / SPEC 更新 / SUMMARY） |
| 收尾裁決 | user 2026-07-27 手測驗收全數通過，明確下令走收尾工作包 |
| push 狀態 | 三 repo 皆**未 push**（push 永遠等 user 明示） |

---

## 1. 一句話總結

Guidant AI 現在可以直接調度外部檢測工具（首發 OpenVAS）完成弱點掃描全自動化：任務按下開始執行 → 雲端派工給內網 Agent → Agent 調度 OpenVAS 掃描 → 報告（PDF）+ 發現統計自動拉回登錄為任務證據 → 通知負責人 → 依完成模式（manual/auto）決定是否自動完成任務。重新執行有並行防護與真中斷取消機制；規劃頁「開始執行任務」會自動派發首次掃描。

## 2. Commits 清單（三 repo，依 FR-056 全部歷史）

### compliance-manager-be（本 repo）

```
28188b4d docs(fr056): 更新 6 頁功能 SPEC 對齊檢測工具整合平台現況
102f6194 docs(fr056): 使用手冊正式化——刪修正過程敘事、對齊 0.2.8 現況
3a9a9c05 feat(fr056.6): CM-931 重新執行需確認並真中斷上一次掃描（取消機制）
5c0ba4da fix(fr056.7): 轉證據 handler 沿用 agent 回應的檔名/content_type，不再寫死 xml
318b9625 fix(fr056): CM-930 執行歷史排序移到 BE repo 層（ORDER BY started_at DESC, id DESC）
2dc572f1 feat(fr056.5): openvas 參數 schema 補 port_list_id 選填欄位（v1 → v2）
e2eb1c2c fix(fr056.5): api_log 記錄明文憑證，違反 D9「解密憑證不落地」
538d8da0 feat(fr056.5): 規劃頁發佈時自動派 detection 任務的第一次掃描
a0d593ab fix(fr056.5): 派工憑證改 fallback 反查，修真實掃描永遠拿到空憑證
cadec5a5 feat(fr056.5): 測試連線改走 Agent（雲端直推 probe），移除 BE OpenVasProbe
dd44cdef docs(fr056): 交接文件——FR-056.5 probe 任務化 + 端到端手測收尾
05bab666 fix(fr056): heartbeat handler refreshes agent_version from payload
6c73e530 fix(fr056): annotate on_scan_failed caller-scope requirement (T-4.2 follow-up)
88b91f45 fix(fr056): add org_unit_id to detection_executions per D7 tenant-scoped convention (T-4.1 follow-up)
80b2392a feat(fr056): execution orchestration + completion mode + notify (T-4.2)
a4b6f4b9 feat(fr056): detection result → evidence handler + execution history (T-4.1)
21e961a8 docs(FR-056): 主導 session 中繼交接 v2
37451cd2 feat(fr056): enrich control-tree jobs with detection-tool binding (T-2.3 rework)
8efbb6b2 feat(fr056): seed OpenVAS param schema (T-2.3 step 0.5)
afdb9b44 fix(fr056): jedt RLS policy delimiter + is_super_admin bug (T-2.2 follow-up)
ea661321 fix(fr056): agent_tasks state machine reject invalid/terminal transitions (T-3.1 follow-up)
c51bd884 fix(fr056): expose detection-tool binding on job read path (T-2.2 follow-up)
d378596e feat(fr056): job-detection-tool binding + param schema read (T-2.2)
1086ee94 feat(fr056): heartbeat task dispatch + ack + result endpoints (T-3.2)
580c80ac docs(FR-056): 主導 session 中繼交接 handoff
aa916792 feat(fr056): agent_tasks table + state machine + agent capabilities (T-3.1)
a5a7cf4c feat(fr056): add detection_tool job type + BPMN sync (T-2.1)
b26f83f1 fix(fr056): test_connection must merge field_values with decrypted credentials (T-1.4)
9a22142f feat(fr056): add missing GET list tenant configs endpoint (T-1.3 follow-up)
e49c0eb0 docs(FR-056): 補 GET /detection-tools/configs 清單 endpoint
07d21822 docs(FR-056): 討論稿同步最新決策——完成模式改 manual/auto flag + 補 D9–D11
2258e143 feat(fr056): connection test + referencing-task count stub (T-1.4)
447c5720 docs(FR-056): 討論稿內嵌 mermaid.js
a57af11b feat(fr056): tenant detection tool config CRUD + credential encryption (T-1.3)
097e0160 feat(fr056): detection_tools catalog read-only API (T-1.2)
77f6d4e4 feat(fr056): add config schema + detection tool tables (T-1.1)
88e79baf docs(FR-056): D9–D11 決策定案
661fb0e2 docs(FR-056): 檢測工具整合平台設計 + 四階段實作計畫
```

（前兩筆為本次收尾工作包產出，其餘 34 筆為 T-1.1 起的功能開發歷史）

### compliance-manager-fe

```
e986b88 feat(fr056.6): CM-931 重新執行時若有掃描進行中，跳確認框並真中斷
f2ba9b7 feat(fr056): CM-930 任務抽屜執行紀錄區塊可收合 + 區塊內滾動
6febb2b fix(cm933): v-tooltip 直接掛在 PrimeVue Dropdown 上會炸頁面
da4b422 fix(cm933): 我的任務頁輪次篩選連動專案篩選
b56580e fix(fr056.5): MyTasksView 補 detection_tool 任務類型 badge + 完成條件 tooltip
dbea4a5 fix(fr056.5): 移除設定 dialog 內的測試連線按鈕，只留卡片上
6380818 feat(fr056.5): 測試連線顯示 Agent 回傳的失敗原因 + FR-056 實測 bug fix
058d5d5 fix(fr056): remove unused i18n keys in execution drawer (T-4.3 follow-up)
ce59486 feat(fr056): detection task execution drawer (T-4.3)
5914df2 feat(fr056): rebuild detection_tool job type UI in ProjectPlanningView (T-2.3 返工)
fd2e837 Revert "feat(fr056): add detection_tool job type to task setup page (T-2.3)"
f7d4968 feat(fr056): add detection_tool job type to task setup page (T-2.3)
06c1fb5 fix(fr056): sync DetectionToolsErrorCode to FE zh-tw/en error-code.json
0d18938 feat(fr056): rebuild detection tool management page with real API (T-1.5)
```

> HEAD 目前多一筆非 FR-056 的獨立修正（`5a2d2b5 fix(remote-agent): CM-928 在線狀態欄改以健康檢查即時結果為準`），與本 arc 無關，一併存在於同一 branch。

### evidence-agent

```
e59c9fc chore(agent): 版號五處補齊 0.2.8（三輪 runner bump 只改了 pyproject）
703b59a fix(fr056.8): CM-934 OpenVAS target/task 命名撞名（Target exists already）
a7bad25 feat(fr056.6): CM-931 新增取消端點 + OpenVAS 真中斷掃描（0.2.6 → 0.2.7）
60ad41f feat(fr056.7): OpenVAS 掃描報告改用 PDF 當證據 + 補發現項目統計
3707d8c chore(agent): bump 0.2.4 → 0.2.5（#9 啟動即心跳 + #10 hosts 正規化）
e8f667f feat(fr056.5): 心跳改「先跳再等」，啟動即報到不空等一個 interval
cdd76e8 fix(fr056.5): hosts 字串被當 list，create_target 收到逐字元拆解的垃圾
345f8f8 chore(agent): bump 0.2.3 → 0.2.4（#6 心跳信封 + #8 port_list_id）
3515320 fix(fr056.5): create_target 缺 port_list_id，GVMd 回 400 掃描建 target 即死
9731774 chore(fr056.5): bump agent version 0.2.0 → 0.2.3
f1b6629 fix(fr056.5): 心跳解析錯信封層級，pending_tasks 永遠讀不到
6df02e2 fix(fr056.5): probe 失敗訊息去技術化，翻成使用者看得懂的說法
3ed125f feat(fr056.5): agent 端 probe —— connector.probe() + /detection/probe 端點
05726c0 fix(fr056): heartbeat carries agent_version so upgrades refresh cloud-side version
eb016a6 chore(fr056): rename image evidence-agent → guidant-ai-agent
5a9a317 chore(fr056): rename compose service file-agent → guidant-ai-agent
e61679b fix(fr056): dockerignore runtime dirs under deploy/ (pgdata/filedata/certs)
5c7e988 chore(fr056): bump agent version 0.1.0 → 0.2.0 + add rebuild.sh
35847df feat(fr056): OpenVAS connector (T-3.4)
16f7462 feat(fr056): agent task executor skeleton (T-3.3)
```

## 3. 十個 bug 修復鏈一覽（真實 OpenVAS 端到端手測發現）

程式審查（56.1~56.4）全數放行後，第一次對真實 OpenVAS 端到端跑通時，連續挖出 10 個先前審查沒抓到的 bug（詳細脈絡見 `handoff/2026-07-27-fr056-manual-test-mid-arc-handoff.md` §1）：

| # | Case | 問題 | 修法 |
|---|------|------|------|
| 1 | CM-929（FR-056.5） | 測試連線用 BE httpx 直連 OpenVAS，架構死（BE 連不到客戶內網） | 改「雲端直推」：BE mTLS POST agent `/detection/probe`，agent 用 python-gvm 探測 |
| 2 | （同案追加） | 派工 `tenant_config_id` 從未寫入 → 憑證永遠解不到 | fallback 用 `(tenant_id, detection_tool_id)` 反查 config |
| 3 | （同案追加） | 心跳 API log 記錄明文憑證，違反 D9 | api_log middleware 對 secret 類 key 通用遮罩 |
| 4 | （同案追加） | 規劃頁「開始執行任務」不會自動派 detection 任務首次掃描 | `start_task_execution` 尾端加自動派工 |
| 5 | （同案追加） | agent 心跳解析錯信封層級，`pending_tasks` 永遠讀不到 | `body.get("data", body)` 容錯（真根因，堵死真實掃描） |
| 6 | （同案追加） | `create_target` 缺 `port_list_id`，GVMd 400 | 補預設 port list UUID + params 可覆寫 |
| 7 | （同案追加） | hosts 是字串被當 list，逐字元拆解成垃圾 | connector 邊界正規化為 list |
| 8 | （同案追加） | 心跳「先等滿一輪才第一跳」，升級部署要空等 | 改「先跳再等」 |
| 9 | CM-930 | 任務抽屜「執行紀錄」一律全展開，多筆時佔位 | 加可收合 toggle + 智慧預設展開（running/failed 才展開）+ 區塊滾動 + BE 排序移到 repo 層 |
| 10 | CM-931 | 「重新執行」無確認框，掃描中按會疊出並行掃描 | 方案 C 真中斷：確認框 → agent `/detection/cancel` → `gmp.stop_task()` 真停 → 才派新工單；取消後舊掃描若仍回報不轉證據/不通知/不觸發 auto |
| 11 | CM-932 | 報告存 XML 使用者看不懂；`summary` 永遠空 | 同一 report_id 取 PDF（證據）+ XML（解析 `<result_count>` 填 summary，不上傳） |
| 12 | CM-933 | `/project/task-manage` 輪次篩選與專案篩選不連動 | 未選專案 disabled；選定後拉該專案完整輪次清單 |
| 13 | CM-934 | 同批發佈多個同目標任務會撞名（`Target exists already`） | `create_target`/`create_task` 命名從秒級 timestamp 改用 agent task_uid（天然唯一） |

**版號軌跡**（evidence-agent）：0.2.0（T-3.4 首版）→ 0.2.5（CM-929 鏈路修正累積）→ 0.2.6（CM-932 PDF）→ 0.2.7（CM-931 取消）→ **0.2.8（CM-934 撞名，目前部署版本，已於本次收尾補齊五處版號同步）**。

## 4. 手測驗證結果（2026-07-27，user 拍板通過）

| 驗證項 | 結果 |
|--------|------|
| 端到端鏈路 | exec id=5（15:02→15:20，152 掃描 succeeded）為第一次成功基準 |
| 同批多任務派工（CM-934 撞名修） | 3 任務 17:55 同秒派工全部成功，零 `Target exists already`（exec 9/10/11） |
| auto 完成模式 | job 13370「自動完成任務」掃完自動轉 COMPLETED，無人工介入 |
| manual 完成模式 | job 13372 掃完維持 PROCESSING（等人工完成），對照正確 |
| PDF 證據（CM-932） | 證據＝`OpenVAS掃描報告_192.168.50.151_20260727.pdf`（application/pdf ~130KB），source=DETECTION_TOOL，storage_type=remote_agent |
| summary 統計（CM-932） | `{"findings": 28, "log": 25, "low": 3, "high": 0, "critical": 0}` 寫入 `detection_executions.summary` |
| 取消機制（CM-931） | exec 12 標 cancelled（18:08），新掃描 exec 13 接續 running——確認框 → agent stop_task → 重派，實測通過 |
| 執行紀錄收合/排序（CM-930） | user 手測通過 |
| 輪次篩選連動（CM-933） | user 手測通過 |

## 5. 行為差異總覽（FR-056 前 vs 後）

| 面向 | 前 | 後 |
|------|-----|-----|
| 檢測工具管理頁 | 前端 mock 占位頁，工具清單 hardcoded，無 API/持久化 | 真後端 CRUD，工具目錄 DB 驅動，租戶設定加密憑證存 DB，測試連線雲端直推 Agent |
| 任務類型 | `general` / `survey` | 新增 `detection_tool`——選工具+動態掃描參數+完成模式 |
| 掃描觸發 | 無此功能 | 規劃頁「開始執行任務」自動派首次掃描；抽屜「開始執行/重新執行」可手動補派/重試 |
| 掃描結果 | 無此功能 | Agent 調度 OpenVAS 掃描 → PDF 報告+發現統計自動入庫為任務證據（來源=DETECTION_TOOL）→ 通知信 |
| 完成收尾 | 僅人工按完成 | 新增 `completion_mode`：manual（人工按完成，預設）/ auto（掃完自動完成） |
| 重新執行 | 無此功能 | 無執行中→直接派工；有執行中→確認框→真中斷（agent stop_task）→標記舊筆 cancelled→才派新工單；未帶 force 直打 API 有 running 則 409 |
| 執行歷史 | 無此功能 | 抽屜「執行紀錄」區塊，可收合、最新在上、running/最新失敗預設展開、四態（running/succeeded/failed/cancelled） |
| Agent 能力模型 | `capabilities=["file_storage"]` | 支援 `detection_scan`，派工/測試連線只挑具此能力的 Agent |

## 6. 部署 Handover

- **evidence-agent 0.2.8 已部署在 123**（`ubuntu-lab-03`），雲端 DB 確認 `agent_version=0.2.8`、`status=active`
- **STG migration 已套用**（本次收尾工作包完成）：11 支 fr056 SQL migration 全數套到 `guidant_ai_stg`，3-way diff 驗證 DEV=STG（詳見 §7）
- **未 push 清單**：三 repo（BE/FE/agent）working tree 皆乾淨但都有大量未 push commits，push 時機與範圍等 user 明示
- **POC 環境**：本次未套 migration（不在 user 指令範圍內），未來需要時另行處理

## 7. STG Migration 套用紀錄

**diff 結果**（套用前，DEV vs STG）：STG 缺 11 支 fr056 migration（與計畫清單一致，另有 2 支不相關的既有歷史落差 `2026-07-07-fr048-phase4a-capability-seed.sql` / `2026-07-20-*-cleanup.sql`，不在本次範圍）。

**套用順序**（依實際 DB 依賴關係，非檔名日期序——`jedt-fix-rls-delimiter` 依賴 `job-execution-detection-tools` 先建表）：

```
2026-07-26-fr056-3-agent-tasks.sql
2026-07-26-fr056-3-remote-agents-capabilities.sql
2026-07-26-fr056-2-job-execution-detection-tools.sql
2026-07-26-fr056-2-jedt-fix-rls-delimiter.sql
2026-07-26-fr056-2-openvas-param-schema-seed.sql
2026-07-26-fr056-4-job-evidences-detection-source.sql
2026-07-26-fr056-4-detection-executions.sql
2026-07-27-fr056-4-detection-executions-add-org-unit.sql
2026-07-27-fr056-5-openvas-param-schema-port-list.sql
```

（另 2 支 `2026-07-26-fr056-1-detection-tools-config-schema.sql` / `2026-07-26-fr056-1-fix-rls-delimiter.sql` 為第一批獨立套用，無依賴問題）

全部 `--single-transaction -v ON_ERROR_STOP=1` 用 `cmmgr` 帳號套用，皆成功、無 rollback。

**套用後驗證**：
- 3-way diff（`schema_migrations` 表比對）：DEV/STG 除 2 支不相關舊落差外完全一致 ✅
- seed 資料驗證：`config.detection_tools` 3 筆種子（openvas/nessus/sonarqube）✅；`config.detection_tool_param_schemas` OpenVAS v1→v2 兩版本（v2 含 `port_list_id`，`is_current=true`）✅，與 DEV 結構完全一致

## 8. 已知 Follow-up（未實作，供未來排入）

- **OpenVAS target/task 殘留清理策略**：每次掃描都新建 target + task，OpenVAS 側正在累積殘留物，CM-934 卡內已提及但明確標「本次不實作」——需要 user 決定是否另開 case（清理時機注意與報告取得的互動，報告仍需可讀時不能先刪 task）
- **Agent 對外暴露安全性強化**：防火牆規則、只放行雲端來源 IP——僅在對話中口頭提過，**未開 case**，需要 user 決定優先度
- **Nessus / SonarQube 等其他檢測工具的 connector 實作**：平台架構已預留（工具目錄 DB 驅動、動態 schema），本輪全程只測 OpenVAS
- **STG/POC 環境的 BE/FE 部署與 agent 安裝**：本次收尾只套了 STG DB migration，BE/FE 程式碼與 agent 尚未部署到 STG/POC
- **失敗情境手測（Step 6）未逐項跑**：手測劇本原定驗證「hosts 填不存在 IP → 失敗訊息可辨識」情境，本輪因時間因素未逐項確認，行為理論上已由既有錯誤處理涵蓋但未實測驗收
- **`remote_agents.capabilities` 無管理頁 UI**：目前只能直接改 DB 設定某 Agent 具備 `detection_scan` 能力，見 [檔案 Agent 管理 spec §12](../../../docs/specs/current/evidence/remote-agent-manage.md)

## 9. 收尾工作包本身完成項（CM-935）

- ✅ 工作包 3：STG migration（見 §7）
- ✅ 工作包 1：使用手冊正式化（`user-manual.html`，commit `102f6194`）
- ✅ 工作包 2：6 頁 SPEC 更新 + render_html build（commit `28188b4d`）
- ✅ 工作包 4：本 SUMMARY + 原 handoff CLOSED 標頭

---

**Notion 座標**：母案 CM-907（`3a9346da-4cd0-81a5-be6f-f0ddd6d770c6`）；收尾工作包 CM-935（`3aa346da-4cd0-8147-978a-c67d8e570b22`）。
