---
title: FR-165 掃描工具資源管理：設計
status: 定案（2026-10-26 決策者核可雛形＋ D1～D6 全部裁示）
---

# FR-165 掃描工具資源管理

> 🔴 **這份是設計決策，不是現況。** 內文的數字與待辦停在 2026-10-26 定稿當時，之後實作改動的結果不回頭改寫這裡。現況看 README 的總進度。

## 一句話

把「掃描設定檔管理」與「弱點資料庫」兩頁收進同一個入口「掃描工具資源管理」，兩個分頁對應兩種形狀：使用者要挑的「設定檔／基準」是一張表，全機一份沒人挑的「參考資料庫」是一卡一庫。資料層不合併、後端只加一支紀錄分頁端點。並定下規則：之後任何工具的設定檔或參考資料庫一律掛在這裡，不開新頁。

## 為什麼

決策者 2026-10-26 原話：「每次多一個工具又多一個功能頁面，實在太亂也四散各處，像這次新的弱點資料庫就是這樣，當初做的時候完全忘記有掃描設定檔管理。」

實查現況（DEV 本機 DB、FE `src/config/router/index.js`）：

- 掃描設定檔管理在合規稽核群組 `/plugin/detection-profile-manage`（sort 35），弱點資料庫在系統設定群組 `/system/vulndb`（sort 38）。兩頁各自一套選單、權限、前端。
- 兩者同屬「掃描工具要吃的內容」，但形狀不同：設定檔每工具多支、租戶可傳、有版本、派任務時選；弱點資料全機一份、不分租戶、只求新鮮、自動更新。
- FR-059 已把設定檔庫做成不綁工具（新工具 seed 一筆即可用）；弱點資料庫是第一個參考資料庫，做成專用頁與專用接線，下一個進來會複製一份。
- 我們自己帶在 Agent 裡的工具中，需要獨立更新資料的只有弱點比對這一組（Trivy、Grype、OSV-Scanner 加 KEV、EPSS，已合成一包）。CINC 吃設定檔、Syft 純解析、sonar-scanner 規則在客戶端。客戶自裝的 OpenVAS、Nessus、SonarQube、ZAP 的特徵庫由它們自己更新，不在範圍。

## 決策

| # | 決策 | 理由 | 被排除 |
|---|---|---|---|
| D1 | 入口頁名「**掃描工具資源管理**」，放**合規稽核群組**、接「掃描設定檔管理」原本的位子（sort 35，在「工具插件管理」與「第三方元件風險」之間） | 設定檔是稽核員挑來派任務的，不該藏在管理員的系統設定區 | 放系統設定群組 |
| D2 | **權限點名字第一期不改**：`detection.profile.*` 與 `system.vulndb.*` 都保留。入口選單可見條件＝任一組 read；各分頁各看自己那組 | 改名要連動出貨角色表、前端能力表、客戶既有角色 pattern，牽動大但沒換到行為 | 統一成 `detection.*` |
| D3 | **第二期（參考資料庫宣告驅動）等第二個參考資料庫立案再做**；第一期版面已照一卡一庫排好 | 為單一登記者蓋機制不值；實查短期內看不到第二個（Agent 自帶工具只有弱點比對需要獨立更新資料） | 現在就做宣告驅動 |
| D4 | FR-156 G1（CM-3000）正在寫的 SPEC 與手冊照寫不擋；本案收尾時改入口位置 | 決策者裁「等做完再更新文件就好」 | 先擋 G1 |
| D5 | 三條舊路由前端**永久轉向**到新入口對應分頁，不留別名 | 兩條路到同一頁會讓選單高亮與麵包屑亂掉 | 保留舊路由當別名 |
| D6 | Agent 同步面板**收進卡片**：卡上一行摘要（幾台已同步／落後／離線）＋「明細」展開每台 | 它是「這個資料庫送到 Agent 沒有」，屬於該資料庫；第二個資料庫進來各卡有各自的同步狀態 | 留在頁面層級 |
| D7 | 弱點資料卡**原頁功能一個不少**：資料日期與新鮮度色、連不到站提醒、來源與站台、自動更新開關、**檢查頻率下拉**、下次檢查、**四個來源明細常駐**、上傳進度、唯讀提示、Agent 同步、更新紀錄。驗收逐控制項對 `VulndbManage.vue` | 決策者審雛形抓到頻率下拉漏畫、要求來源明細常駐 | 精簡版卡片 |
| D8 | 更新紀錄改**對話框內分頁**，按鈕不帶筆數；後端加一支分頁端點 | 決策者「更新紀錄不用 count，點開有分頁就好」；現況 `status.recent` 固定 20 筆翻不了頁 | 留在頁面下方整塊 |
| D9 | **沒接上的不預告**：設定檔分頁「工具」下拉只列已接入的工具，參考資料庫分頁只列已登記的資料庫，不放「即將推出」或灰掉的選項 | 決策者「目前沒支援的就不開放給 user 看」 | 預告未來工具 |
| D11 | 設定檔分頁的工具下拉**只列有接設定檔庫的工具**：後端工具清單每筆回 `supports_profile_library`，由 FR-059 既有宣告（現行 param_schema 帶 `options_source: profile_library` 的欄位）算出，不寫死工具名；前端篩選／新增／複製三處照旗標過濾 | 決策者複測後裁「沒有的就先隱藏」；DEV 實查九支工具只有 inspec／gcb 有宣告，其餘七支選了也沒東西。沿用宣告機制，新工具 seed 加宣告即自動出現 | 前端寫死工具代號清單（新工具接入要改前端，違反 FR-059 零開發接入） |
| D10 | 分頁依**形狀**分不依工具分。判準：「派任務時使用者要不要選它」，要選進設定檔分頁、不用選進參考資料庫分頁；同一工具兩種都有就各進各的 | 工具再多也只有兩種版面 | 一工具一分頁 |

## 畫面（以雛形為準）

雛形：[prototype/index.html](prototype/index.html)（決策者 2026-10-26 核可）。

### 頁首與分頁

- 標題「掃描工具資源管理」＋一句說明。
- PrimeVue `TabView` 兩個分頁：「設定檔／基準」、「參考資料庫」。沒權限的分頁整個不出現；只剩一個分頁時仍顯示分頁列。網址 `?tab=profiles|dbs` 記住分頁。

### 分頁一：設定檔／基準

掛既有 `DetectionProfileManageView.vue`，**零改動**。控制項子頁 `DetectionProfileControlsView.vue` 路徑改掛在新入口底下。

### 分頁二：參考資料庫

一卡一庫，版面固定（新元件 `ReferenceDbCard.vue` 提供外觀）：

1. 標題列：名稱、給哪個工具用、狀態膠囊（自動更新中／手動／無資料）。
2. 連不到站提醒（`hint-bar--warn`，只在 `last_check_skipped` 時出現）。
3. 資訊列（`dl.kv`）：資料日期（30 天黃、90 天紅）＋幾天前、來源＋站台、自動更新開關、檢查頻率 `Dropdown`（選項來自 `system_menus` 的 `vulndb_check_interval`）、下次檢查、Agent 同步摘要＋「明細」展開。
4. 四個來源明細表常駐（Trivy、Grype、KEV、EPSS 各自日期、版本、筆數，來自 `status.sources`）。
5. 上傳進度（上傳中才出現）。
6. 底列：檢查更新、上傳更新包、更新紀錄（開對話框，內有分頁）。唯讀者底列換成「無權限」提示。

今天只有弱點資料一張，內容照 `VulndbManage.vue` 搬。

### 設計系統自檢

`grep border-left` 為零；卡片一律 `dark-card`；字級全站基準；狀態只出現一次。

## 資料與後端

### 選單與路由（一支主線 migration）

| 動作 | 對象 |
|---|---|
| 新增 `ui_routes` | `scan-tool-resources`，`/plugin/scan-tool-resources`，父 `group-compliance-audit`，sort 35，icon `pi pi-box`，description「掃描工具資源管理」 |
| 新增 `route_capabilities` | 綁 `detection.profile.read` 與 `system.vulndb.read`，requirement **ANY** |
| 改 `ui_routes` | `detection-profile-manage` 與 `vulndb` 兩筆 `enable=0`（不刪：升級客戶的 `route_capabilities`、手冊、稽核事件都還指著） |
| 改 `ui_routes` | `detection-profile-controls` 的 `url` 改 `/plugin/scan-tool-resources/profiles/versions/:versionUid/controls`，`pid` 改指新入口 |
| bump | `CAPABILITY_CATALOG.VERSION` 加一 |

能力點不新增、不改名（D2）。屬主線 `phase=active`，回寫時明講「出貨基線待重產」。

### 更新紀錄分頁端點（唯一的後端改動）

`GET /api/1.0/system/vulndb/updates?page=&size=`，照專案分頁慣例（`RequestMetaSchema` 進、`EnvelopeSchema` 出、`meta` 帶總筆數）。守門同 `status`（`system.vulndb.read`）。

- jedi-source-scan：`VulndbUpdateRepo` 加 `list_page(offset, limit) -> (rows, total)`；`VulndbUpdateService` 加 `list_updates(page, size)`，走既有 `fill_user_names` 補 `created_user_name`。開發期走 poetry path dependency（`pyproject.toml:128` 取消註解），**path 改動不 commit**，發版等令。
- 主專案：`VulndbSettingsAppService.list_updates(page, size)`；`api/source_scan/routes/vulndb_route.py` 加 `VulndbUpdatesRoute`；`api/source_scan/__init__.py` 加 `add_resource`；serializer 加 `VulndbUpdateListRequestSchema`／`VulndbUpdateListResponseSchema`（沿用 `VulndbUpdateRecordSchema`）。
- `status.recent` 保留（Agent 同步與「連不到站」判定仍靠它），FE 對話框改打新端點。

### 前端

| 檔 | 動作 |
|---|---|
| `src/views/plugin/ScanToolResourcesView.vue`（新） | 殼頁：標題、TabView、依 `hasCap` 決定分頁、`?tab=` 記住 |
| `src/views/detection-profile/DetectionProfileManageView.vue` | 不改，被殼頁當子元件掛進分頁一（947 行已超標，不往裡加） |
| `src/views/vulndb/VulndbManage.vue` | 改成卡片：拔頁首與整頁 `card` 外殼，內容塞進 `ReferenceDbCard` 的 slot；更新紀錄改對話框打分頁端點；Agent 同步收進卡（D6）。上傳、檢查、開關、頻率邏輯全部不動 |
| `src/components/vulndb/ReferenceDbCard.vue`（新） | 卡片外觀：slot 標題列／提醒／資訊列／明細／底列。弱點資料是第一個使用者，第二期各資料庫共用 |
| `src/components/remote-agent/AgentVulndbSyncPanel.vue` | 改成「摘要一行＋明細展開」兩種呈現，供卡片用 |
| `src/views/vulndb/VulndbUpdateHistory.vue` | 改成對話框內容，接分頁 |
| `src/config/router/index.js` | 新路由 `/plugin/scan-tool-resources`（name `scan-tool-resources`）、`.../profiles/versions/:versionUid/controls`（name `detection-profile-controls` 沿用）；舊三條 `redirect`（D5） |
| `src/config/api/api.js` | 加 `VULNDB_UPDATES` |
| `src/service/VulndbService.js` | 加 `listUpdates(page, size)` |
| `src/config/locales/i18n/{zh-tw,en}/menu.json` | 加 `scan-tool-resources` 標題與描述 |
| `src/config/locales/i18n/{zh-tw,en}/vulndb.json` | 補分頁、卡片用鍵 |

## 拆卡

七張，五批。一卡只改一個 repo（工作坊規範），所以分頁端點拆成套件卡與主專案卡。

| 卡 | 做什麼 | Repo | 批 | 驗收 |
|---|---|---|---|---|
| FR-165.1 選單與路由 migration | 新入口選單、兩筆舊選單停用、控制項子頁改掛、能力綁定 ANY、bump | 主 | 1 | DEV 套完 `ui_routes` 多一筆、兩筆 enable=0；只有 `system.vulndb.read` 的帳號看得到新選單；回寫「出貨基線待重產」 |
| FR-165.2 前端殼頁與弱點資料卡片化 | 殼頁＋TabView＋權限分頁；`VulndbManage` 縮成卡片；`ReferenceDbCard`；Agent 同步收進卡；三條舊路由轉向；i18n；對話框先接 `status.recent` 做分頁 UI | fe | 1 | 對照雛形逐項；設定檔分頁功能與今天完全一樣；舊網址轉向；只有一組權限的帳號只看到一個分頁 |
| FR-165.3 套件分頁查詢 | jedi-source-scan `VulndbUpdateRepo.list_page`、`VulndbUpdateService.list_updates`＋套件測試 | jedi-packages | 1 | 套件測試綠，含突變驗證 |
| FR-165.4 更新紀錄分頁端點 | 主專案 app service、route、serializer、`add_resource`，path dependency 吃 .3 | 主 | 2 | curl 帶 page/size 回正確筆數與 meta；守衛測試綠 |
| FR-165.5 收尾文件 | SPEC 兩頁入口位置、使用手冊、`frontend-overview.md` 加規則 | 主 | 3 | 等 FR-156 G1（CM-3000）收完才派；grep 兩份 SPEC 無「系統設定 → 弱點資料庫」 |
| FR-165.6 工具清單加旗標 | jedi-detection `list_tools`／`get_tool` 每筆算 `supports_profile_library`，抽共用判斷式、批次查現行 schema、entity 與 serializer 各加一欄、測試 | jedi-packages | 4 | 實打 `/detection-tools` inspec／gcb True、其餘 False；突變測試紅 |
| FR-165.7 設定檔分頁照旗標過濾 | `DetectionProfileManageView.vue` 的 `toolOptions` 只留旗標為真；`toolNameByCode` 用全部；旗標缺席時退回全列 | fe | 5 | 三個下拉只剩 CINC／GCB；表格工具欄既有資料名稱正常 |

批 1 三張不撞檔：.1 只動 `scripts/sql/`，.2 只動 FE，.3 只動套件。.4 依賴 .3 的套件方法。.2 的對話框先打 `status.recent` 做分頁 UI；.4 端點好了之後由首腦驗收時把 `VulndbUpdateHistory.vue` 的資料來源換成新端點（一行），或併入 .5。

## 不做

- 不合併 `config.detection_profiles` 與 `public.vulndb_updates` 兩套表。
- 不改能力點名字（D2）。
- 不做參考資料庫宣告驅動（D3，第二期）。
- 不在畫面預告任何未接入的工具或資料庫（D9）。
- 不碰 Agent 心跳與拉包通道。

## 第二期輪廓（不在本期做）

參考資料庫分頁改成宣告驅動：各工具插件在契約登記「我有哪個參考資料庫、支援五個動作中的哪幾個」（看狀態、上傳、檢查更新、自動更新、Agent 同步）；後端開 `GET /scan-resources/reference-dbs` 回清單；前端照清單長 `ReferenceDbCard`；Agent 心跳與拉包改帶資料庫代號。不把 `vulndb_updates` 改通用表，契約只規範「回最近 N 筆的形狀」。觸發條件：任何「我們自己帶、資料要獨立更新」的工具進排程。
