# FR-059 檢測工具 Profile / Content 庫 — 全案收尾 SUMMARY

| 項目 | 內容 |
|------|------|
| 日期 | 2026-08-01 |
| 作者 | 小弟（收尾派工產出） |
| 狀態 | DEV 開發驗收完成；**三 repo 全部未 push、未進版、未上 STG / POC** |
| Branch | BE `feature/FR-058`（FR-059 沿用同 branch）／FE、agent 各自現行 branch |
| 設計文件 | [`../design.md`](../design.md)（P1–P7 + D1–D10 共 17 項決策全數拍板） |
| Notion | 母案 CM-1007、卡樹 CM-1008–1023；CM-992 範圍③「後台 content 管理機制」由本案承接 |
| 部署 handover | **STG / POC 上版一律照 [`../deploy-runbook.md`](../deploy-runbook.md) 執行**（自包含、給非開發者） |

---

## 1. 結論

FR-059 把檢測工具（第一期 CINC Auditor / GCB）的 profile / content 維運，從「四步人工鏈」（profile 進 repo → 人工 rsync 到每台 agent 主機 → 寫 migration 改 param_schema options → 依環境鐵律逐環境套）收斂為**後台一頁搞定**：管理者在「掃描設定檔管理」頁上傳 / 登記 profile，任務抽屜下拉即時動態列出，agent 派工時透過 FR-058.7 的 mTLS 取檔通道拉檔＋本機 cache。agent 只升級一次（0.2.27），之後 content 的新增 / 改版 / 停用全部在後台完成——不再重建 agent image、不再人工 rsync、不再為選項寫 migration。

design.md §7 的十條端到端驗收**全數在 DEV 通過**（§5），含封閉網路情境驗證（CM-992 限制①的直接解除）。三 repo 的 commit 全部保留本地未 push；STG / POC 的 migration / seed / agent / FE 部署一律等決策者明示放行（D10 環境異動鐵律），放行後照 `deploy-runbook.md` 執行。

## 2. 改動範圍與行為差異

### 2.1 新舊對照

| 面向 | 改版前（FR-058 止） | 改版後（FR-059） |
|------|--------------------|-----------------|
| profile 上架 | 四步人工鏈（repo 版控 → rsync 每台 agent → migration 改 options → 逐環境套），全程只有開發者做得到 | 後台「掃描設定檔管理」頁上傳（zip / tar / tar.gz / tgz，50MB 內）或登記 URL，下拉即時生效 |
| 下拉選項來源 | `param_schema` 裡死的 options 陣列（migration seed） | `options_source: "profile_library"` 宣告 → FE 動態呼 menu API（按工具分池，P7）；手填能力保留（P6） |
| agent 取 content | 依賴人工 rsync 進 `/data/content`（bind mount，**只投放過 DEV agent**） | 派工時 BE 展開 `profile:<uid>` 參照為 `_profile` payload → agent 走 058.7 mTLS 通道拉檔 → sha256 對帳 → 安全解壓進 cache `/data/content/cache/<uid>/v<version>/`；cache 命中不重拉、版更自然失效 |
| 租戶自有 profile | 不可能（封閉網路客戶無任何投放路徑，CM-992 限制①②） | Category C 雙軌：SYSTEM 公版（root 維護、人人可見）＋ TENANT 自有（上傳 / fork 公版），RLS + service guard + capability 三層防禦 |

### 2.2 各 repo 落地面

- **BE**：新表 `config.detection_tool_profiles`（雙軌 + P4 版本模型 + RLS 四段 policy）、管理 API 六動作（列表 / menu / 上傳登記 / 版更 / fork＋複製到另一工具 / 停用）、上傳驗證管線（四格式偵測、slip / bomb / entry 數 / 解壓比防護、`inspec.yml` 結構驗證——全 repo 首個壓縮檔安全處理實作）、搬遷 seed（10 筆 SYSTEM 公版）、param_schema 升版（v2 拿掉靜態 options）、派工展開、`profile_ref` 授權 resolver（append 進 058.7 可插拔清單）、選單路由登記。
- **FE**：系統管理下新頁「掃描設定檔管理」（雙軌列表 + 六動作，靠 scope 區分能力，公版對非 root 唯讀）；任務抽屜下拉偵測 `options_source` 宣告改走 menu API（無宣告欄位維持靜態行為，機制 tool-agnostic）。
- **agent（evidence-agent）**：profile cache＋拉檔（通道取檔 / sha256 對帳 / 四格式統一安全解壓）、inspec connector 接 `_profile` 三路分流（file 型餵 cache 解壓目錄；url 型 / 手填維持現行為）、部署前置（cache 掛載點）＋封閉網路驗證，版號 0.2.25 → **0.2.27**。

## 3. Commits 清單（三 repo，全部未 push）

### 3.1 BE（compliance-manager-be，branch `feature/FR-058`）— 12 個

| Commit | 內容 | Notion |
|--------|------|--------|
| `960abaec` | docs：設計定稿（討論稿 + design.md + FR 登記） | — |
| `65c6e501` | docs：resolver 介面契約定案對齊（`(agent_uid, file_uid)` callable + `_FILE_ACCESS_RESOLVERS`） | D6 |
| `9c95ae08` | T-1.1 表 + RLS + ORM + repo | CM-1012 |
| `854b0209` | T-1.3 上傳驗證管線（四格式 / slip / bomb / 結構） | CM-1014 |
| `d88c2e8e` | docs：§5.2 capability 勘誤（四個全 `is_platform=false` 比照 flow_template 平掛） | CM-1013 |
| `e1d50929` | T-1.2 管理 API 六動作＋三層防禦 | CM-1013 |
| `355de397` | T-1.4 搬遷 seed（8 支 TWGCB + 2 條 dev-sec 落 SYSTEM 公版） | CM-1015 |
| `2b1879ab` | T-2.1 param_schema 動態化（`options_source` 宣告＋升版拿掉靜態 options） | CM-1016 |
| `43b02c15` | T-4.1 選單登記（ui_routes + route_capabilities，BE 側） | CM-1022 |
| `c9c34d8c` | T-2.2 派工展開（`profile:<uid>` 前綴偵測＋`_profile` payload） | CM-1017 |
| `1d9e43e4` | T-2.3 `profile_ref` 授權 resolver | CM-1018 |
| `413ed121` | fix：公版 profile description 移除內部術語 | — |

### 3.2 FE（compliance-manager-fe）— 2 個

| Commit | 內容 | Notion |
|--------|------|--------|
| `7f06a3d` | T-4.1 掃描設定檔管理頁（雙軌列表＋六動作） | CM-1022 |
| `78f8e07` | T-4.2 下拉動態化（`options_source` 宣告接 Profile 庫 menu） | CM-1023 |

### 3.3 agent（evidence-agent）— 5 個

| Commit | 內容 | Notion |
|--------|------|--------|
| `4e12f8f` | T-3.1 profile cache＋拉檔（通道取檔 / sha256 對帳 / 四格式安全解壓） | CM-1019 |
| `6afe1ca` | T-3.2 connector 目錄交付（inspec 接 `_profile` 三路分流） | CM-1020 |
| `ce2228c` | T-3.3 部署前置（cache 掛載點 / 版號單一來源），bump **0.2.26** | CM-1021 |
| `4d0c43a` | T-3.3 封閉網路情境驗證紀錄 | CM-1021 |
| `0c47463` | fix：profile 根目錄使用時解析（macOS zip bug），bump **0.2.27** | CM-1019/1020 follow-up |

## 4. 與 design.md 的偏差紀錄（三條）

實作與設計定稿有三處刻意偏差，皆屬「實作期發現更優落點 / 實測發現的缺口」，設計意圖不變：

1. **派工展開點：`_collect_pending_tasks()` → `start_execution()`**。design.md §5.4 原定在心跳組 payload 階段（`agent_enrollment_service._collect_pending_tasks()`）展開庫參照；實作改在 `detection_orchestration_service.start_execution()` **建任務時展開一次、`_profile` 直接落 `agent_tasks.params`**。理由：展開結果不隨時間變（uid / version / sha256 在建任務當下即凍結），建任務時做一次優於每次心跳重查 DB；心跳路徑零改動、零額外查詢。`params.profile` 原值照常保留（使用者選了什麼的紀錄），`_profile` 靠 `_` 前綴既有剝除機制不落 FE 執行紀錄與稽核 log。
2. **D2 契約抽成共用模組 `common/util/detection_profile_ref.py`（單一真相）**。`profile:` 前綴的組裝（`build_profile_ref()`）與解析（`extract_profile_uid()`）、`_profile` key 名，收斂在這一個模組；menu DTO、seed script、派工展開全部 import 它，不允許任何呼叫端自己寫 `startswith("profile:")`——避免前綴契約散落成多份真相。
3. **驗證期發現 macOS zip 根目錄 bug → agent 補 `resolve_profile_root()` 使用時解析（0.2.27）**。使用者實測上傳 macOS 打包的 zip：BE 驗證通過入庫（結構驗證容忍「頂層或一層內有 `inspec.yml`」），但 agent 端 CINC 拿到 cache 根目錄時因多包一層目錄＋`__MACOSX` 垃圾而報「Don't understand inspec profile」——兩端對「一層深」的容忍度不對齊。修法：agent 在**使用時**解析實際 profile 根目錄（跳過垃圾 entry、下探一層找 `inspec.yml`），不改 cache 落地結構、不回頭收緊 BE 驗證（收緊會擋掉合法的 macOS 使用者）。

## 5. 驗收結果

**design.md §7 十條端到端驗收全數通過（DEV）**：四格式上傳＋惡意樣本全擋（①）、下拉即選＋分池（②）、派工取檔全鏈（③）、cache 命中與版更失效（④）、sha256 對帳防線（⑤）、三層取值相容（⑥）、雙軌守門＋RLS（⑦）、封閉網路情境（⑧）、下拉零回歸（⑨）、三環境節奏遵守（⑩——即本文「未上 STG / POC」的現況）。

重點佐證：

- **封閉網路驗證（⑧，CM-992 限制①直接解除）**：斷外網的 agent 僅靠平台 mTLS 通道取得 file 型 profile，完成 **825 項實檢**的 GCB 掃描全鏈；過程**零外網請求**；同 profile 再派**cache 命中零重拉**（agent log 佐證）。
- **使用者實測**：macOS zip 上傳 → 入庫 → 派工 → 掃描成功（§4 偏差③的修復即出自此輪實測）。
- **下拉零回歸（⑨）**：搬遷 seed 的 10 筆 SYSTEM 公版 name 逐字沿用原 param_schema options label，menu 集合與移除的靜態集合一一對應（seed script 內建零回歸驗證段，執行時實測通過）。

## 6. Notion 卡座標

| 卡號 | 內容 |
|------|------|
| CM-1007 | FR-059 母案 |
| CM-1008–1011 | 四子需求（FR-059.1 BE 地基 / .2 下發整合 / .3 Agent 端 / .4 FE） |
| CM-1012–1021 | 子任務卡（T-1.1〜T-3.3，對照 §3 commits 表） |
| CM-1022 / CM-1023 | FE 管理頁 / 下拉動態化 |
| CM-992 | 範圍③「後台 content 管理機制」由本案承接（限制①已解除見 §5；限制②由 P5「私有 profile 改打包上傳」解掉） |

## 7. 已知 follow-up

| # | 項目 | 說明 | 出處 |
|---|------|------|------|
| 1 | agent cache LRU 上限 | 第一期無上限＋手動清理指引（單支 profile 幾百 KB〜幾 MB，量級離磁碟壓力很遠；cache key 含 version 版更自然失效）；LRU 上限（如 2GB）列 follow-up | D9 |
| 2 | BE 裝 CINC pre-check | 上傳驗證第一期只驗「結構＋檔案安全」，profile 正確性由掃描執行結果反映（界線已明寫）；BE 端裝 CINC 跑 `cinc-auditor check` 列未來選項 | D8 |
| 3 | D5 舊路退役 | 過渡一版：新機制上線驗收通過後的**下一版**，拿掉 agent `/data/content` bind mount 與 param_schema 舊版靜態 options 路徑（手填容器路徑能力因 P6 永遠保留）。過渡期兩路並存 | D5 |

## 8. 部署 handover

STG / POC 上版（migration 四支 + seed + agent 0.2.27 + FE 新版）為**上版動作**，一律等決策者當次明確放行。放行後由部署執行者照 **[`../deploy-runbook.md`](../deploy-runbook.md)** 執行——該文件自包含（前提 / 指令 / 順序 / 驗收 checklist / 回滾要點），不需回讀 design.md 或本文。

執行順序骨幹（細節見 runbook）：BE 程式碼更新 → migration fr059-1 → fr059-2 → fr059-3（選單）→ seed script → fr059-3（param_schema 升版，**必須在 seed 之後**）→ agent 0.2.27（cache 掛載點先建）→ FE 新版 → 驗收 checklist。

## 9. 座標索引

| 項目 | 位置 |
|------|------|
| 設計文件 | `docs/features/FR-059-2608-detection-profile-library/design.md` |
| 決策討論稿 | `docs/features/FR-059-2608-detection-profile-library/discussion.html` |
| 部署 runbook | `docs/features/FR-059-2608-detection-profile-library/deploy-runbook.md` |
| Migration（4 支） | `scripts/sql/2026-08-01-fr059-*.sql` |
| 搬遷 seed script | `scripts/seed_2026-08-01_fr059_detection_profiles.py` |
| D2 契約共用模組 | `common/util/detection_profile_ref.py` |
| 派工展開 | `app/detection_tools/service/detection_orchestration_service.py`（`start_execution()` → `_expand_profile_ref()`） |
| profile 原始碼版控 | BE repo `content/detection-profiles/`（D5 過渡後不再是投放通道，保留作版控與產生器工作區） |
| agent 部署手冊 | evidence-agent repo `deploy/README.md` §6.1（cache 掛載點前置） |
