# FR-114 資安修正：合回主線前的整理（給決策者）

> 首腦第 4 棒整理，材料來自 BOARD／STATE／LOG／各工作區 commit，未逐行核 diff；決策依據以 Notion 各卡「首腦驗收」段為準。
>
> 母卡：[CM-2019](https://app.notion.com/p/SUMMARY-145-worktree-3e2346da4cd0816a87fde3fc8a7eb10c)。這份只寫「現在是什麼狀態、合回要怎麼做、要測什麼、要你裁什麼」，每棒怎麼走到這裡的沿革在 `FR-114-LOG.md`。

---

## 1. 一句話現況

**53 張卡做完 50 張，程式面第 1～5 批全部驗收過（Done）。** 所有改動都還放在四個工作區（worktree，也就是跟主線隔開的副本資料夾）的 `fix/security-b1` 分支上：

| 工作區 | 相對主線多了幾筆 commit |
|---|---:|
| BE `compliance-manager-be/.claude/worktrees/wt-fix-security` | 63 |
| 套件 monorepo `jedi-python-package/.claude/worktrees/jedi-wt-fix-security` | 41 |
| FE `compliance-manager-fe/.claude/worktrees/wt-fix-security` | 9 |
| 代理程式 `evidence-agent/.claude/worktrees/wt-fix-security` | 2 |

**還沒合回主線、套件還沒發新版、任何環境（含 DEV 的服務本身）都還沒部署這批程式。** DEV 資料庫已套過本批六支 migration（資料庫結構異動腳本），首腦 09-23 深夜實查 `schema_migrations` 六支全在（最晚一支 09-23 12:50）；這是開發期正常狀態。

剩下沒做的 3 張：
- [CM-2061](https://app.notion.com/p/FR-114-5B-5-M07-SUMMARY-89-129-130-131-132-3e2346da4cd081edba34d56242426214)（5B-5）：等舊版證據分類退場（CM-1849）做完，才能確認那五件是否跟著消失。
- [CM-2071](https://app.notion.com/p/FR-114-6-0a-project_id-SUMMARY-41-3e3346da4cd081a1a7d6dc738fd77864)、[CM-2072](https://app.notion.com/p/FR-114-6-0b-uid-id-SUMMARY-109-3e3346da4cd08150ba80e05ae911cc9d)（第 6 批）：要動資料結構，原本就排在「第 1～5 批合回、測過一版」之後。

---

## 2. 改了什麼（按使用者看得到的行為分）

標「**操作會變**」的，是使用者畫面或流程會有感的地方，總測時要特別看。

### 2.1 登入後看不到不該看的

以前很多功能只問「你有沒有登入」，不問「這筆資料跟你有沒有關係」。現在補上了：

- **檔案**：稽核證據、SSP 佐證、意見回饋附件等檔案的下載、預覽、換發下載通行證、刪除，都要先確認你是這個檔案所屬專案或單據的相關人；不是的話一律回「找不到這個檔案」，不回「沒有權限」，避免別人拿編號一個一個試（[CM-2033](https://app.notion.com/p/FR-114-2-1-SUMMARY-3-3e2346da4cd0815db1bbf8db72bf32c9)）。**操作會變**：系統認不出歸屬的舊檔（孤兒檔）會變成下載不了。
- **問卷**：任務問卷的列表、答案、填答歷史、討論串，改成「該專案任一成員可讀」；空白查詢不再回整張表，直接拒絕（[CM-2034](https://app.notion.com/p/FR-114-2-2-SUMMARY-5-6-48-49-50-51-3e2346da4cd0818cacdaeda01aed1694)）。
- **專案內的各種查詢**：流程留言、稽核退回歷程、流程範本、專案與控制項成員、任務指派、摘要報告、稽核輪次、工作項，都會反查「這筆是不是屬於網址上的那個專案、你是不是成員」（[CM-2035](https://app.notion.com/p/FR-114-2-3-SUMMARY-7-8-52-53-115-3e2346da4cd0817db731fbd7012f1aa0)、[CM-2036](https://app.notion.com/p/FR-114-2-4-SUMMARY-54-55-58-59-3e2346da4cd08112a839cd06c723a7e9)、[CM-2037](https://app.notion.com/p/FR-114-2-5-SUMMARY-56-57-3e2346da4cd08181995fe483cb06be9d)、[CM-2039](https://app.notion.com/p/FR-114-2-7-SUMMARY-63-3e2346da4cd0812b8c4ffddc15cb17c1)）。**操作會變**：摘要報告清單頁只列「我參與的專案」，習慣從這頁看全局的管理者會少看到東西。
- **公告**：新增「發送範圍」（全公司或指定部門），讀取時依範圍過濾，點進看不到的公告回「找不到」；既有公告一律標「全公司」，行為不變（[CM-2026](https://app.notion.com/p/FR-114-1-5a-SUMMARY-42-111-3e2346da4cd08116bb87d0563c5405a7)、[CM-2027](https://app.notion.com/p/FR-114-1-5b-migration-SUMMARY-42-111-3e2346da4cd081c69496e4dd44e15fd6)、[CM-2028](https://app.notion.com/p/FR-114-1-5c-FE-SUMMARY-42-111-3e2346da4cd08113a504c4db9a0e47a2)）。**操作會變**：公告表單多一個必選的「發送範圍」，選「指定部門」時部門欄才必填。
- **資產與掃描設定的下拉選單**：設備、資訊系統、掃描設定檔的讀取改成要有對應的讀取權限（`device.read`、`information-system.read`、`detection-profile.read`），缺權限的角色由 migration 自動補上（[CM-2029](https://app.notion.com/p/FR-114-1-6-SUMMARY-43-112-3e2346da4cd081cdba6cf445c435b269)、[CM-2032](https://app.notion.com/p/FR-114-1-9-migration-SUMMARY-44-43-3e2346da4cd081068270df638de9d042)、[CM-2042](https://app.notion.com/p/FR-114-2-10-SUMMARY-4-47-113-3e2346da4cd081a58a25c2fb98ae476c)）。**操作會變**：真的沒權限時，下拉選單不再「安靜變空」，而是顯示「沒有權限」並鎖住欄位（[CM-2082](https://app.notion.com/p/FR-114-X-1-FE-403-1-5-3e3346da4cd0817eaf28d778365ce28a)）。
- **AI 儀表板與意見回饋**：AI 儀表板 26 支查詢各自申報要什麼權限，拿掉「一律當管理者」的後門（[CM-2038](https://app.notion.com/p/FR-114-2-6-AI-SUMMARY-60-61-62-114-3e2346da4cd081079545e539427d7c20)）；意見回饋清單多一個「查看範圍」參數（只看我的／看全部），管理頁明確送「看全部」（[CM-2030](https://app.notion.com/p/FR-114-1-7-SUMMARY-45-3e2346da4cd0815e9c9ee1f8ff4fe4eb)）。

### 2.2 動不了不是自己的資料

- **任務完成與退回**：只有該任務的被指派人與專案 manager 能按；弱點檢測的八個按鈕（開始、取消、刪除執行紀錄等）同一套規則（[CM-2021](https://app.notion.com/p/FR-114-1-1-SUMMARY-37-3e2346da4cd081c3a3b1d7e1eedb333a)、[CM-2022](https://app.notion.com/p/FR-114-1-2-SUMMARY-37-3e2346da4cd081d297dded4c9558599a)）。**操作會變**：一般成員按別人任務的「完成」會收到 403（拒絕存取）提示。掃描跑完自動完成的那條路維持可用。
- **只有作者能改刪**：問卷討論（刪除改成軟刪除，畫面看得出被移除而不是整筆消失）、公告、意見回饋，都比對作者本人（[CM-2024](https://app.notion.com/p/FR-114-1-4a-SUMMARY-38-39-3e2346da4cd0816f90ded586e31b0f60)、[CM-2025](https://app.notion.com/p/FR-114-1-4b-migration-SUMMARY-39-3e2346da4cd081ee9385f563604a4f90)、CM-2026、CM-2030）。
- **寫入時反查填進來的東西**：專案成員與任務指派寫入時，確認填的任務、群組、控制項屬於這個專案（[CM-2023](https://app.notion.com/p/FR-114-1-3-SUMMARY-41-109-110-3e2346da4cd08190a3e2d00c6ed9e32e)）；範本資源庫三條寫入路徑補歸屬、建立端加次數上限（[CM-2040](https://app.notion.com/p/FR-114-2-8-SUMMARY-46-65-3e2346da4cd081ea8777ef3851d26d50)）；SSP 參考文件刪除要確認文件真的屬於這份計畫（[CM-2041](https://app.notion.com/p/FR-114-2-9-SSP-SUMMARY-64-3e2346da4cd081e7abf0d755fa59a53e)）；問卷資料夾與即時填答的身分改用登入身分，不信請求裡帶的名字（[CM-2060](https://app.notion.com/p/FR-114-5B-4-SUMMARY-85-86-126-127-3e2346da4cd081c8a98be7ddba166230)）。
- **全公司共用設定只有總部能改**：LDAP、SMTP、登入政策、日誌轉送四處，子公司管理員改會被拒（[CM-2054](https://app.notion.com/p/FR-114-5A-3-SUMMARY-22-19-134-3e2346da4cd0811fa8b9c07be81b8cbe)）。**操作會變**：子公司管理員存檔時收到 403。
- **流程範本**：驗證端點要權限且有長度與節點數上限；範本新增、修改一定經過檢查；起稽核輪次要求範本已發布（[CM-2031](https://app.notion.com/p/FR-114-1-8-SUMMARY-40-3e2346da4cd081039a5ff2ebcc5db356)、[CM-2059](https://app.notion.com/p/FR-114-5B-3-SUMMARY-16-87-88-128-3e2346da4cd081e38206f8c9e84ca87a)、[CM-2083](https://app.notion.com/p/FR-114-X-2-88-SUMMARY-88-3e3346da4cd08135bbb5e02f39f02892)）。**操作會變**：用草稿範本起輪次會被擋。
- **停權立即生效**：帳號被停權後，手上已發出的登入通行證下一個請求就失效，續期也被拒，網頁與即時通訊兩條路都擋（[CM-2056](https://app.notion.com/p/FR-114-5A-5-M13-17-SUMMARY-92-3e2346da4cd0810ba31ddb110fc83ebb)）。

### 2.3 代理程式與防竄改

- **代理程式每次打進來都要證明身分**：代理程式（裝在客戶機房、替我們去掃主機的那支程式）登記時，雲端多發一張長效通行證；之後心跳、確認收單、回報結果、下載證據檔四條路都要帶這張證，雲端每次驗章並查資料庫看這台有沒有被撤銷或停用（[CM-2052](https://app.notion.com/p/FR-114-5A-1-SUMMARY-1-3e2346da4cd08135ba63d89e24cfbd8b)）。**操作會變**：管理畫面按「撤銷」後，那台機器四條路當場全斷；被撤銷的機器重新登記會被拒，不會洗回正常。
- **重灌後接手原機器要證明**：帶舊編號重新登記，必須用舊私鑰簽名證明自己是原機器，否則當新機器處理；登記時送的憑證申請（CSR）上的機器識別碼必須等於本次登記的機器，堵住冒用別台指紋拿真憑證的路（CM-2052）。
- **時鐘偏差警告**：代理程式登記與心跳時比對雲端時間，差超過 60 秒就醒目警告（只警告，不中止）。偏差過大時證據檔上傳會全部失敗，以前現場只看得到一行難懂的錯誤（[CM-2085](https://app.notion.com/p/FR-114-X-3-60-M01-SUMMARY-145-3e4346da4cd081569b1cc56a46a4c7b0)）。
- **驗簽工具拔不掉**：負責核對簽章的零件（`cryptography`、`cffi`）改成強制編進產品核心，不再以可替換的原始檔形式附帶；打包版不再理會「換掉要核對哪個目錄」的環境變數（[CM-2053](https://app.notion.com/p/FR-114-5A-2-SUMMARY-17-18-90-91-133-3e2346da4cd081bca8f2d61e4ae2f250)）。
- **鎖定紀錄刪不掉**：機器因竄改被鎖後，開機時除了看檔案標記也查資料庫；刪掉標記檔重開，資料庫還有紀錄就照樣鎖，並把標記補回去。解鎖回執要附原廠簽過的解鎖碼，手寫的回執不採信。竄改回報送出的主機紀錄先遮蔽密碼再附上（CM-2053）。

### 2.4 憑證與設定

- **版控裡的密碼殘留清掉**：資料庫管理員密碼（249 檔）、外部平台通行證、AI 金鑰、Google 雲端硬碟金鑰、交接文件帳密、47 檔過期登入通行證，全部從現存檔案清除（不改寫 git 歷史）；開始掃描時不再把明文主機帳密多存一份進派工表（[CM-2048](https://app.notion.com/p/FR-114-4-1-AI-SUMMARY-10-11-72-73-122-3e2346da4cd081199bd1e3b1a0237e76)、[CM-2049](https://app.notion.com/p/FR-114-4-2-249-SUMMARY-12-3e2346da4cd081a6beced0c650716ab8)、[CM-2050](https://app.notion.com/p/FR-114-4-3-SUMMARY-70-3e2346da4cd0816ea853c742e76c27d1)、[CM-2051](https://app.notion.com/p/FR-114-4-4-Google-SUMMARY-74-3e2346da4cd081019d80d2cc68c98f73)）。
- **密鑰不再回傳給前端**：儲存設定頁的物件儲存密鑰，後端只回「有沒有設定過」，不回真值；儲存設定也加進密鑰遮蓋名單（[CM-2063](https://app.notion.com/p/FR-114-5C-2-SUMMARY-14-M02-124-3e2346da4cd08190b187d8ac26457d9c)、CM-2054）。**操作會變**：密鑰欄顯示遮罩，沒改就不送，只改其他欄位不會洗掉密碼。
- **日誌轉送可加密**：日誌轉送設定頁新增「傳輸加密」開關與「信任憑證」欄（CM-2056）。**操作會變**：設定頁多兩個欄位，既有設定一律維持不加密。
- **連線驗證與版本鎖定**：GitHub 連線恢復憑證驗證（[CM-2066](https://app.notion.com/p/FR-114-5C-5-GitHub-SUMMARY-100-135-3e2346da4cd08191802dfa4827236b0a)）；快取連線改用套件已修好的那份（[CM-2043](https://app.notion.com/p/FR-114-3-1-SUMMARY-M13-10-3e2346da4cd08163b0e2d3fd8819905e)）；套件 monorepo 17 支的版本鎖定檔（poetry.lock）納入版控（[CM-2067](https://app.notion.com/p/FR-114-5C-6-monorepo-SUMMARY-101-3e2346da4cd0819a8babdb75bedcc5f7)）。
- **交出去的東西先處理**：匯出 Excel、Word 時，`=`、`+`、`-`、`@` 開頭的內容當文字不當公式；日誌轉送的換行編碼，避免一筆被讀成兩筆（[CM-2055](https://app.notion.com/p/FR-114-5A-4-Word-5C-7-SUMMARY-20-93-102-126-3e2346da4cd0815ba5e4cbb156e795f6)）；檔案預覽先驗真實格式，不在白名單或內容與副檔名不符的一律改成下載，HTML 預覽不執行裡面的程式（[CM-2062](https://app.notion.com/p/FR-114-5C-1-SUMMARY-13-123-3e2346da4cd08135b72ada180d9cf563)）。**操作會變**：部分以前能直接預覽的檔案改成下載。
- **各種上限**：AI 儀表板與 AI 助手加逾時與次數上限，超過回 429（請求太頻繁）；AI 助手訊息上限 4000 字（[CM-2064](https://app.notion.com/p/FR-114-5C-3-AI-SUMMARY-21-94-95-3e2346da4cd081a69acfd19073ac2a08)）；清單每頁筆數上限 1000、設定值打錯字改退回正式模式、寫日誌失敗不連累使用者請求（[CM-2065](https://app.notion.com/p/FR-114-5C-4-SUMMARY-82-83-136-84-103-3e2346da4cd0815b9fb1e83c660611c2)）；弱點檢測規則包解析前擋惡意內容、壓縮檔炸彈上限、測試連線主機數上限、重新解析去重（[CM-2057](https://app.notion.com/p/FR-114-5B-1-SUMMARY-15-76-77-78-3e2346da4cd08105b73cd0130f43e9ee)、[CM-2058](https://app.notion.com/p/FR-114-5B-2-SUMMARY-79-80-81-3e2346da4cd081a0ba2ceef7ab17f30e)）。**操作會變**：AI 連按太快會看到「請稍後再試」。

### 2.5 刪掉的功能

- 三組沒人呼叫的任務管理功能（[CM-2044](https://app.notion.com/p/FR-114-3-2-SUMMARY-68-69-120-3e2346da4cd08121baf6f0ab9a2672e8)）。
- 開發用小工具 `gen_comment.py` 與從沒被讀過的死開關；`jedi-common` 的 `[ai]` 選配相依跟著拿掉（[CM-2045](https://app.notion.com/p/FR-114-3-3-SUMMARY-116-117-3e2346da4cd081b9b8eae747ae78aaf9)）。
- 全站成員名冊查詢入口 `/issue/get_members`（資料表與同步程式保留，意見回饋指派負責人還在用）（[CM-2046](https://app.notion.com/p/FR-114-3-4-SUMMARY-121-3e2346da4cd08104b886cf19145d5a14)）。
- 「快速設定」獨立畫面兩個入口（專案規劃頁裡的快速配置不受影響）（[CM-2047](https://app.notion.com/p/FR-114-3-5-SUMMARY-119-3e2346da4cd081f6b3cfef8a2aaeea55)）。**操作會變**：那兩個網址直接打不開。
- 密碼產生器兩份零引用的重複副本，只留 `jedi-iam` 那一份（[CM-2068](https://app.notion.com/p/FR-114-5C-8-SUMMARY-137-3e2346da4cd08198aaafd5db3089f4a6)）。

---

## 3. 合回的硬順序（順序錯了服務起不來）

### 3.1 為什麼有順序

三個事實決定了順序，任何一個沒照做，服務會直接起不來或代理程式全斷線：

1. **BE 用到了套件的新東西，但 BE 還指著舊版套件。** BE 工作區已 commit 的程式 import 了只有新版套件才有的東西，例如 `jedi_remote_agent.api.agent_pass_required`（代理程式通行證守門）、`jedi_integrity.ports.IBootLockStore`（開機查資料庫鎖定）、`jedi_common.utils.export_safety`（匯出防公式）、`jedi-task-platform` 的 `resolve_project_id_for_job`。而 BE 主線 `pyproject.toml` 仍 pin 舊版（例如 `jedi-remote-agent==1.1.2`、`jedi-integrity==1.1.0`、`jedi-common==1.2.0`）。**只合 BE 不發套件，一開機就 ImportError。** runner 開發時是靠 path dependency（直接指向本機套件資料夾）與 `PYTHONPATH` 繞過去的，那些設定沒有 commit。
2. **代理程式新舊版與雲端新舊版互不相容，而且是兩個方向都不相容。** 舊代理程式配新雲端：舊版只認「404 就重新登記」，收到新雲端的 401（未帶通行證）會一直失敗、一直重試，等於斷線。新代理程式配舊雲端：新版登記時要拿通行證，舊雲端不發，新版會當成雲端設定錯誤一直重試。**所以雲端與代理程式要同一次上。**
3. **套件之間也有新依賴。** `jedi-survey` 用到 `jedi-task-platform` 新加的專案成員守門；`jedi-issue` 與 `jedi-log` 用到 `jedi-common` 新加的匯出防公式工具。這幾支發版時，依賴對方的版本下限要一起抬高，否則裝到舊版的組合會壞（這是我從 import 查到的三處，發版的人要再全面 grep 一次）。

**首腦不合回、不發版、不部署，下表每一步都由決策者或你指定的人執行。**

### 3.2 步驟表

| 步 | 做什麼 | 誰做 | 做完怎麼確認 |
|---|---|---|---|
| 0 | 做完第 4 節「合回前清理」 | 你指定的 runner（開一張清理卡） | 清理卡回寫逐項打勾，工作區 commit |
| 1 | **套件 monorepo**：`fix/security-b1` 合回 `feature/review` | 決策者或指定的人 | 合回後主線 `git log` 看得到 41 筆；`CM-2032` 那筆主線已有同內容的 `eafc7ae`，工作區是 cherry-pick 版 `e98fea2`，合回時同內容不該衝突，衝突就取任一邊 |
| 2 | **套件發版**：下表 17 支 bump 版號、推 Nexus（內部套件庫）。順序：先 `jedi-common`，再 `jedi-iam`、`jedi-task-platform`，其餘不分先後 | 決策者明示後由指定的人照 `docs/claude/jedi-packages.md` 發版流程 | Nexus 上查得到 17 支新版；抬高依賴下限的三處（見 3.1 第 3 點）已改 |
| 3 | **BE**：合回；`pyproject.toml` 把 17 支的 pin 改成新版號；跑 `poetry update <逐一列出 17 支套件名>` | 同上 | **一律指定套件名，不裸跑 `poetry update`**（裸跑會把沒驗過的第三方一起升）；`python -c "import jedi_remote_agent; print(jedi_remote_agent.__file__)"` 指到 site-packages 且版本是新版；`python main.py` 起得來、`/api/1.0/version` 回 200 |
| 4 | **FE**：合回 | 同上 | `npm run build` 通過 |
| 5 | **代理程式**：合回；出一版代理程式 release（`scripts/build/build_agent_release.sh`） | 同上 | release 產物內含兩筆新 commit；版號與雲端版號對得上 |
| 6 | **部署 DEV**：本機 BE 用合回後主線起服務、FE 起前端、DEV 上的代理程式換新版 | 同上 | 代理程式心跳正常、管理畫面看得到在線；log 無 ImportError |
| 7 | **188 全量 build 唯讀驗證**（第 5 節）與**出貨基線重產**（第 6 節） | 基線重產要你一句「做」；build 驗證要你放行 | 見各節 |
| 8 | **總測**（第 7 節） | 決策者 | 各組清單全過；有問題開卡調 |
| 9 | 才談 STG／POC | 決策者明示 | 另開上版卡，走 init image migrate 模式，不手動套 |

### 3.3 要發版的 17 支套件

依「除了測試與 lock 之外有沒有改到程式」判斷。以下是工作區 `fix/security-b1` 相對 `feature/review` 的實際變動（排除測試檔與 `poetry.lock`）：

| 套件 | 現版 | 改動檔數 | 主要來自 |
|---|---|---:|---|
| jedi-issue | 1.2.0 | 28 | 意見回饋權限與作者比對、名冊入口刪除、GitHub 驗證、附件刪除 |
| jedi-survey | 1.1.2 | 21 | 問卷讀取守門、討論作者比對、即時填答身分 |
| jedi-remote-agent | 1.1.2 | 18 | 代理程式通行證、撤銷、持有證明、雲端時間 |
| jedi-detection | 1.1.0 | 18 | 嚴格任務守門、資源上限、規則包、報告檔名 |
| jedi-log | 1.2.0 | 15 | 日誌轉送加密、匯出防公式、換行編碼 |
| jedi-ai-dashboard | 1.2.0 | 15 | 查詢權限白名單、逾時與次數上限 |
| jedi-task-platform | 1.1.1 | 13 | 指派寫入反查、成員查詢守門、工作項歸屬 |
| jedi-file-upload | 1.2.0 | 13 | 檔案歸屬檢查、預覽格式驗證、衍生檔清理 |
| jedi-integrity | 1.1.0 | 12 | 開機查 DB 鎖、解鎖回執驗章、log 遮蔽 |
| jedi-bulletin | 1.1.0 | 11 | 發送範圍 |
| jedi-common | 1.2.0 | 10 | 匯出防公式、序列化排除密碼、分頁上限、密碼遮蔽；拿掉 `[ai]` 選配 |
| jedi-compliance-audit | 1.1.3 | 7 | 稽核輪次守門、起輪次要求已發布、錯誤碼鏡像 |
| jedi-asset | 1.1.0 | 7 | 設備與資訊系統讀取權限 |
| jedi-ai-bot | 1.2.0 | 7 | 訊息長度、逾時、次數上限 |
| jedi-flow-engine | 1.3.0 | 5 | 流程圖走訪上限、範本檢查 |
| jedi-iam | 1.3.0 | 3 | 停權即時生效 |
| jedi-system-core | 1.1.1 | 1 | 儲存設定密鑰遮蓋名單 |

**不用發版的 4 支**：`jedi-evidence-classification`、`jedi-license-runtime`、`jedi-notification`、`jedi-oscal-v2` 只多了 `poetry.lock` 入版控，lock 不進發布出去的套件檔，程式沒變。

> 注意：派工時給我的 18 支清單裡有 `evidence-classification`、`license-runtime`、`notification`（只有 lock），但沒有 `jedi-compliance-audit`（7 檔程式）與 `jedi-system-core`（1 檔程式）。以上表為準。

### 3.4 合回時要對得上的關鍵 commit

合回的人核對用。每個 repo 的完整清單用 `git -C <工作區> log --oneline feature/review..HEAD` 看。

| repo | commit | 內容 |
|---|---|---|
| 套件 | `c4acc687`、`9c049540` | 代理程式通行證、撤銷、持有證明、CSR 識別碼檢查（CM-2052） |
| 套件 | `953697c9`、`ba129ca1` | 開機查 DB 鎖、log 遮蔽、解鎖回執驗章（CM-2053） |
| 套件 | `bad15b7d` | 登記與心跳回傳雲端時間（CM-2085） |
| 套件 | `e98fea29` | CM-2032 的 cherry-pick 版，主線已有同內容 `eafc7ae5` |
| 套件 | `09cbb7cf` | 17 支 `poetry.lock` 入版控（CM-2067） |
| BE | `7a3bdf1ec` | 證據檔下載改驗通行證（CM-2052），import 新版 `jedi-remote-agent` |
| BE | `0dda326b8`、`b64995ac9` | 驗簽工具編進核心、開機查 DB 鎖接線、補同步重驗回執（CM-2053） |
| BE | `3a651140e`、`fc9fca7d0`、`38c5b131e`、`32ace7aa5`、`4e7754de7`、`1b52aa24b` | 六支 migration 與 manifest 登記 |
| BE | `264b42262`、`c87d2eea5` | 資安報告狀態批次更新（第 5 批以前的 Done 卡） |
| 代理程式 | `ecdc9f3` | 每請求帶通行證、401 自動重新登記（CM-2052） |
| 代理程式 | `9edda49` | 時鐘偏差警告（CM-2085） |
| FE | `756783b` | 儲存設定密鑰欄遮罩（CM-2063） |
| FE | `10dc47c`、`bbf0f68`、`4f1e501`、`45b3d7c` | 403 提示與七個錯誤碼文案（CM-2082、CM-2042） |

BE 與 FE 主線在工作區分岔後：BE 主線多了 77 筆（只和工作區在 `docs/features-site/` 重疊，見第 4 節），FE 與代理程式主線沒有新 commit，可以直接快轉合回。

### 3.5 出事怎麼退

合回之後、總測之前如果發現起不來或大面積壞掉，退法依壞在哪一層：

- **BE 起不來（ImportError）**：幾乎都是步驟 2、3 漏了某支套件。先看錯誤訊息指的套件名，確認 Nexus 上有新版、BE pin 有改、`poetry update` 有點名它。不要用 path dependency 臨時救，那會把開發設定帶進主線。
- **代理程式全斷線**：先看雲端與代理程式是不是都換成新版。只換了一邊就把另一邊也換上；兩邊都要退時一起退，不可只退一邊。
- **某個功能被新守門擋住正常使用者**：先確認那個角色有沒有對應權限（第 6 節的兩支補權限 migration 是否已套）。確定是守門寫錯才開卡改，不要為了讓總測過而拿掉守門。
- **整批要撤**：四個 repo 的合回各自是一筆合併 commit，可以各自 revert；套件已發出去的新版號不收回，BE 把 pin 改回舊版號即可。資料庫那六支 migration 都是「只加不刪」（加欄位、補權限、放寬一條新增規則），留著不影響舊版程式運作，撤程式時不必撤資料庫。

---

## 4. 合回前一定要做的清理

建議開**一張清理卡**一次做完，在四個工作區裡 commit，做完再進第 3 節步驟 1。

| 項 | 在哪 | 誰做／何時 | 做完怎麼驗 |
|---|---|---|---|
| FE 補錯誤碼文案：`GRC_400131`「留言內容不可為空」、`GRC_400132`「留言內容過長，上限 2000 字」、`GRC_400133`「本流程留言則數已達上限（500 則）」 | FE `src/config/locales/i18n/{zh-tw,en,zh-cn}/error-code.json`（我已確認三語目前都沒有） | 清理卡／合回前 | 三個語系 grep 得到三個碼；送空白流程留言畫面顯示中文句子不是原始代碼 |
| FE 補代理程式錯誤碼文案：`FILE_AGENT_401002`（通行證缺失或無效）、`FILE_AGENT_403003`（代理程式已停用）、`FILE_AGENT_400007`（憑證申請上的機器識別碼不符） | 同上 | 清理卡／合回前 | 三語 grep 得到。這三個只有代理程式端點會回，畫面碰不到，補上是為了一致 |
| 收掉施工日誌型註解（程式裡寫「第幾棒、哪張卡、重派」這類，應該進 commit message） | BE `common/code/flow_control_error_code.py:384-386`；BE 帶「FR-114.2-5」的 4 支檔 5 處（`project_summary_report_service.py` 等）；套件 `jedi-compliance-audit` 錯誤碼鏡像對應段；FE 帶「CM-2082」9 檔 13 處、帶「重派」6 處、帶「CM-2042」4 處 | 清理卡／合回前 | `git grep -n "CM-20[0-9][0-9]\|重派\|FR-114\."` 在程式碼（不含 docs）零命中，只留真正的「為什麼」 |
| 還原 BE 工作區未 commit 的開發設定：`pyproject.toml` 12 支 pin 被改成 path、`poetry.lock` 跟著變；共用 venv 裡 12 個 `.pth` 指到 jedi 工作區 | BE 工作區 `git status` 看得到的兩檔；venv site-packages | 清理卡／合回前（這些不能被合回） | 工作區 `git status` 乾淨；`python -c "import jedi_common; print(jedi_common.__file__)"` 不再指到工作區 |
| 文件站以主線為準：BE 主線在分岔後多了 77 筆，和工作區重疊的**只有** `docs/features-site/`（約 590 檔）。合回時這一區一律取主線 CM-2084 全量版（121 個 FR），工作區只保留 CM-2048 憑證清理那 22 個檔 | BE `docs/features-site/` | 合回當下 | 合回後 `docs/features-site/` 與主線相比只差憑證清理那幾檔；站內 FR 數 121 |
| 升級手冊補一段角色權限說明。**內容要改向**：原本要寫「升級前檢查各角色有沒有讀取權限」，但你 09-23 已裁補權限 migration 一律隨出貨，升級時會自動補上。所以改寫成「升級會把 `device.read`、`information-system.read`、`detection-profile.read` 補給缺的角色；刻意拿掉的，升級後要再收一次」 | `docs/spec-site/current/user-manual/onprem/05-upgrade-rollback.md` | 收尾棒（等令） | 手冊頁看得到這三顆與「升級後再收」一句 |
| 權限守門兩種流程的說明文件（能力點型與資源歸屬型），SUMMARY「怎麼修」加連結 | 新檔 `docs/security-report/GUIDE-02-authz-two-patterns.md`（目前還不存在；桌面有 HTML 草稿） | 收尾棒（等令） | 檔案存在、SUMMARY 連結點得開 |
| SaaS 版 TLS（加密連線）在哪裡終結（Cloudflare、雲端負載平衡或其他）寫一行 | `docs/features/FR-063-2608-nuitka-packaging/deployment-env.md` | 決策者或維運看一眼，合回前 | 那一行存在 |
| SUMMARY 狀態列批次更新：#1、#17、#18、#90、#133 改已修，#91 改部分修；**頂部的統計數字與「最要緊的五件」也要跟著改**（五件實際都已修，其中第 4 件待 188 build 實證，但頁面上仍寫未修或只修一半；「還沒動的 99 件」也已過期） | BE 工作區 `docs/security-report/SUMMARY.md` 與對應 M 檔 | 清理卡（首腦驗收後的報告批次更新） | 重 build 後頁面欄數正常、抽五條狀態正確 |
| FE 儲存設定頁 bucket 欄的錯誤樣式綁錯到密鑰欄的驗證（既有小錯，CM-2063 順帶發現） | FE `StorageConfigForm.vue` | 清理卡可順手，或另開 | bucket 留空時紅框出現在 bucket 欄 |
| CM-2037 套件側四支 service 各自寫一支 `_require_participant`，沒用 `guard.py` 既有守門 | `jedi-compliance-audit` 四支 app service | review 時看要不要收攏（不收也不影響功能） | 收攏的話四處改呼叫同一支 |

---

## 5. 188 全量 build 要驗的三點

**這是唯讀驗證，不部署、不動 STG。** CM-2053 把驗簽工具改成編進核心，有三件事只有跑一次完整打包才看得到，runner 只在 188 跑過單支套件的小探針（編 `cryptography` 33 秒、驗章正常），沒跑全量。

**在哪跑、開跑前做什麼**：
- 188 的 `/opt/guidant-ai-be`，先 `git pull` 到**合回後的主線 commit**（部署機一律 git pull，不 scp）。
- 開跑前 `pgrep -f 'nuitka|build_all'` 確認沒有別的 build 在跑（互撞會 linker crash）。
- 照 188 固定陷阱：指定 smoke 專用臨時庫（不要連 STG 那顆庫、不要去套 migration）、注意 FE image 標籤、先看磁碟。細節見 `scripts/build/README.md`。
- 出貨基線要先重產（第 6 節），否則 build 的 stamp gate（出包前檢查基線有沒有跟上 migration 的關卡）會紅。

**要驗的三點**：

| 點 | 怎麼看 | 過的樣子 |
|---|---|---|
| 驗簽工具真的在核心層 | 產物內 `cryptography/` 目錄存在，完整性清單（manifest）把它歸在 core 層；產物裡少了 `cryptography` 的 dist-info（安裝資訊目錄）不影響執行 | 開機正常、驗章正常、manifest 查得到 core 歸屬 |
| `typing_extensions` 是誰的 | 在產物裡執行 `typing_extensions.__file__` | 指向 Nuitka 編好的模組。若指向一支複製進去的 `.py`，代表在完整性閘門檢查之前就先執行了一段沒被驗過的原始檔，要回報 |
| 換掉驗簽工具會被擋 | 在一份 build 產物的副本上換掉驗簽零件再開機 | 開機被擋（修之前是綠燈放行，這是 #17 的核心驗收） |

---

## 6. 出貨基線重產

**背景**：新客戶安裝時不是從頭重放每一支 migration，而是載入 `scripts/init/02-schema.sql` 這份「已經長好的資料庫結構」。主線新增了結構類 migration 卻沒更新這份檔，新裝客戶就會少掉那些欄位，而且服務全綠、沒有任何錯誤訊息。

**本批六支 migration（全都已登記在 `scripts/sql/manifest.tsv`）**：

| 檔名 | 卡 | 類型 | 要不要進基線 |
|---|---|---|---|
| `2026-09-22-fr114-survey-discussions-soft-delete.sql` | CM-2025 | 結構（active／*） | **要** |
| `2026-09-22-fr114-bulletin-send-scope.sql` | CM-2027 | 結構（active／*） | **要** |
| `2026-09-22-fr114-feedback-issues-insert-relax-org.sql` | CM-2032 | 結構（active／*，改資料列存取規則） | **要** |
| `2026-09-22-fr114-log-forwarding-tls.sql` | CM-2056 | 結構（active／*） | **要** |
| `2026-09-22-fr114-asset-read-capability-backfill.sql` | CM-2032 | 資料（seed／*，補權限） | 不進基線，靠安裝與升級時的 migrate 那輪套 |
| `2026-09-23-fr114-detection-profile-read-capability-backfill.sql` | CM-2042 | 資料（seed／*，補權限） | 不進基線，同上 |

**STATE 寫「七支」是多算了**：實際是六支檔，其中**四支要進基線**，兩支補權限的是資料類，照規則本來就不進 `02-schema.sql`（資料類硬塞進基線蓋章，等於宣告那些資料永遠不插）。另外套件隨包的三支 migration（`jedi-bulletin` 001、`jedi-issue` 004、`jedi-log` 005）走攤平流程，不屬主專案基線。

**重產要寫入 188 上的基線庫，屬於環境異動。** 卡開好等你一句「做」。步驟（先備份、只套這四支、重產兩份產物、驗 diff 只有新增、本機重跑 stamp gate）見 `.claude/skills/sql-migration/SKILL.md`「首腦重產的步驟」，這裡不抄。建議時機：合回主線後、188 全量 build 之前。

---

## 7. 總測建議

你 09-23 裁「最後從安裝到功能整個重測一遍」。以下按六組列，每條都是能親手點、親眼看的。**標 ★ 的是 opus 抽查時退回過、修法有判斷，請優先測。**

測試帳號建議先備好：專案 manager、同專案一般成員（viewer）、**不在這個專案**的同公司帳號、子公司管理員、總部管理員、一個自建且拿掉讀取權限的測試角色。

### 7.1 安裝與升級

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | 用新出的安裝包全新安裝一套 | 裝完能登入；公告表單有「發送範圍」、日誌轉送設定頁有「傳輸加密」開關（證明基線有跟上） |
| 2 | 拿上一版的環境跑升級 | 升級完既有公告都是「全公司」、既有日誌轉送設定維持不加密 |
| 3 | 升級後查角色權限 | 所有角色都有 `device.read`、`information-system.read`、`detection-profile.read` |
| 4 | 升級後用一個沒有部門的一般使用者送意見回饋 | 送得出去（以前會靜默失敗） |
| 5 | 全新安裝後看產品開機 log | 完整性檢查通過，沒有「無法查詢 DB 側竄改鎖定紀錄」的警示 |
| 6 | 全新安裝一次，確認物件儲存連線可用 | 檔案上傳下載正常 |

### 7.2 登入與權限

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | 停權帳號 B，B 在已登入的畫面上再點一下 | 立刻被登出（401），自動續期也失敗；復權後重新登入正常 |
| 2 | 子公司管理員改 LDAP、SMTP、登入政策、日誌轉送 | 四處都 403 且訊息看得懂；總部管理員與平台管理員改得動 |
| 3 | ★ 用拿掉讀取權限的測試角色，打開專案規劃頁、任務設定頁、合規文件設備分頁、資訊系統分頁、匯入資產挑選器、任務規劃頁的掃描設定檔下拉 | 每個下拉都顯示「沒有權限」並鎖住，**不可以是安靜的空清單** |
| 4 | 同上的角色換回有權限 | 六處下拉都有資料 |
| 5 | AI 儀表板連續快速生成、AI 助手連續快速送訊息、送一則超過 4000 字的訊息 | 超次數顯示「請稍後再試」；超長訊息被退；正常使用不受影響 |
| 6 | 意見回饋：一般員工看清單、管理頁看清單 | 一般員工只看到自己的；管理頁看得到全部 |
| 7 | 儲存設定頁：只改 bucket 名稱存檔，再上傳一個檔 | 上傳正常（密碼沒被洗掉）；瀏覽器網路面板裡回應沒有任何密鑰真值 |

### 7.3 專案流程

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | viewer 按別人任務的「完成」與「退回」 | 兩個都 403；被指派人與 manager 做得到 |
| 2 | 跑一次弱點掃描直到結束 | 自動完成照樣生效（系統代勞的路沒被擋掉）；viewer 取消或刪除執行紀錄被拒 |
| 3 | 不在專案的帳號，拿專案裡一個證據檔的編號去下載、預覽、刪除 | 全部回「找不到」；專案成員四個動作都正常 |
| 4 | ★ 摘要報告清單頁，分別用 manager 與不在專案的帳號看；翻到第二頁 | 只列自己參與的專案；分頁後的結果也只有自己的（這是被退回補過的地方） |
| 5 | 問卷：成員看列表、答案、歷史、討論；非成員拿同編號看 | 成員全看得到；非成員全被擋 |
| 6 | 問卷討論：A 留言，B 改或刪 A 的；管理者刪 A 的 | B 被拒；管理者刪後畫面看得出「被移除」而不是整筆消失 |
| 7 | 公告：部門甲發「指定部門」公告，部門乙的人看清單與直接開網址 | 部門乙看不到、直接開回找不到；A 改自己的公告發送範圍沒被清空 |
| 8 | 用草稿狀態的流程範本起稽核輪次；送空白流程留言、送超過 2000 字留言 | 起輪次被擋；留言顯示看得懂的中文錯誤 |

### 7.4 代理程式

測這組需要工程師在旁邊，部分項目要用 runner 在 DEV 實跑過的那組腳本送請求。

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | 新版代理程式全新登記、心跳、接單、回報、下載證據檔 | 六步全通；管理畫面顯示在線 |
| 2 | ★ 管理畫面按「撤銷」那台，接著讓它心跳、確認收單、回報結果、下載證據 | 四條全部被拒（403），這是全案最嚴重那件的核心驗收 |
| 3 | ★ 被撤銷的機器重新跑登記 | 被拒，而且管理畫面上仍是撤銷狀態，不會洗回正常 |
| 4 | 解除撤銷 | 四條路恢復 |
| 5 | ★ 重灌情境：帶原本的機器編號重新登記，一次附舊私鑰簽名、一次不附 | 附了保住原編號；沒附被當新機器、拿新編號 |
| 6 | ★ 送一份憑證申請，上面的機器識別碼填別台 | 被拒（400），而且沒有建出新機器列 |
| 7 | 把代理程式主機時鐘調快 5 分鐘再心跳 | log 出現醒目的時鐘偏差警告，但代理程式照常運作；調回後出現「已恢復」 |
| 8 | 舊版代理程式配新版雲端 | 連不上（預期如此），確認升級說明有寫「要一起換」 |

### 7.5 防竄改

在 build 產物上測，不是在原始碼模式測（原始碼模式刻意不查資料庫鎖定）。

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | 換掉驗簽零件後開機 | 被擋（第 5 節第三點，可與 build 驗證一起做） |
| 2 | ★ 讓機器進入竄改鎖定，刪掉檔案標記後重開 | 仍然鎖住，檔案標記被補回來 |
| 3 | ★ 鎖定後自己手寫一份解鎖回執放進去，重開 | 不採信，仍然鎖住；資料庫紀錄也沒被改成已解鎖 |
| 4 | 用原廠簽發的解鎖碼正常解鎖 | 解鎖成功，之後同步把資料庫紀錄改成已解鎖 |
| 5 | 設 `GUIDANT_RESOURCE_ROOT` 指到別的目錄再開打包版 | 被忽略，照樣核對 binary 所在目錄 |
| 6 | 觸發一次竄改回報，看送出去的內容 | 主機 log 尾段裡的密碼、Authorization 已被遮蔽 |
| 7 | 把記錄解鎖碼用過沒的檔案改成亂碼 | 出現高等級警示（目前只警示不拒絕，見第 8 節） |

### 7.6 設定頁

| # | 做什麼 | 應該看到 |
|---|---|---|
| 1 | 日誌轉送開「傳輸加密」、貼信任憑證、按測試 | 送得出去；選 UDP 時不能開加密 |
| 2 | 操作日誌匯出、意見回饋匯出（csv 與 excel），資料裡有 `=1+1` 開頭的內容 | 用 Excel 與 LibreOffice 開都顯示文字、不執行 |
| 3 | SSP 匯出 Word、PDF、ODT，欄位裡有 `&`、`<`、`>` | 三種都內容完整、排版沒壞 |
| 4 | 上傳一個副檔名是 .png、內容其實是 HTML 的檔案，按預覽 | 改成下載，不在瀏覽器裡執行 |
| 5 | 「快速設定」兩個舊網址 | 打不開；專案規劃頁裡的快速配置正常 |
| 6 | GitHub 整合建一張單 | 正常建立（恢復憑證驗證沒擋到正常連線） |

### 7.7 測完怎麼回報

- 每組測完，把「哪一條不如預期、實際看到什麼、用哪個帳號」記下來，全部測完再一次交給首腦，不要測一條修一條。
- 首腦依回報分三類：守門寫錯（開卡改程式）、權限資料缺（補 migration）、預期行為但使用者會困惑（改文案或手冊）。
- ★ 那幾條如果有任何一條不過，先停，不要往 STG／POC 推。

---

## 8. 風險與已知未修

### 8.1 報告裡還沒修的

資安檢視總報告（`docs/security-report/SUMMARY.md`）145 件，目前狀態列還標未修或部分修的有 23 件：

| 件 | 一句話 | 歸誰 |
|---|---|---|
| #1 | 代理程式五條管道不問身分、撤銷只斷三分之一 | 本批已修，狀態列待改 |
| #17、#18、#90、#133 | 換驗簽零件、刪鎖定標記、換核對目錄、竄改回報夾帶 log | 本批已修，狀態列待改（#17 待 188 build 實證） |
| #91 | 解鎖碼帳本改亂碼就能重用解鎖碼 | 本批部分修（只記警示） |
| #9、#66、#67 | 舊版證據分類讀雲端硬碟、不查成員、搜尋條件可被改寫 | CM-1849 舊線退場 |
| #89、#129～#132 | 舊版證據分類的 AI 指令注入、殘留資料、金鑰外露、壓縮檔、套件未鎖 | CM-1849 做完後 CM-2061 確認 |
| #23、#24、#104 | 資料庫隔離牆沒生效或方向寫反 | 第 6 批資料庫牆 |
| #75 | 前端打包腳本內含套件庫帳密 | CM-1998 |
| #96、#97 | 郵件密碼進紀錄檔、郵件連線不認人 | CM-1606，未排期，要你裁 |
| #98、#99 | 同一組最高權限帳密、三環境公鑰並列 | 已裁暫緩 |
| #101 | 內部套件庫走明文 | 已裁內網不修（部分修） |

分三類說明：

**本批已做完、只是狀態列還沒改的（6 件）**：#1（代理程式身分）、#17、#18、#90、#133（防竄改），這五件改「已修」；#91（解鎖碼帳本壞掉）改「部分修」，因為目前只記警示不拒絕，原因見下。

**本 arc 範圍內、排在後面的**：
- #9、#66、#67（舊版證據分類）：由 CM-1849 舊線退場處理。
- #89、#129～#132（舊版證據分類的另外五件）：等 CM-1849 做完，CM-2061 確認。
- #23、#24、#104、#105（資料庫隔離牆）：第 6 批資料庫牆，等第 1～5 批合回測過一版才開卡。
- #41（標「部分修」）、#109（標「功能本身已斷」）：第 6 批 CM-2071、CM-2072 補資料結構後收尾。

**不在本 arc 的**：
- #75（前端打包腳本裡的套件庫帳密）：沿用 CM-1998。
- #98（每套安裝都種同一組最高權限帳密）、#99（三環境公鑰並列）：已裁暫緩。
- #101（內部套件庫走明文）：已裁內網不修，lock 已入版控，標部分修。
- **#96、#97（郵件）**：寄信失敗時把整組郵件設定含密碼寫進紀錄檔；連郵件伺服器有加密但不認對方身分，攔在中間的人能收走帳密與每封信（含一次性驗證碼、新帳號初始密碼）。這兩件模組頁寫「工單 CM-1606 已開、未排期」，**不在本 arc 任何一張卡裡**。要你裁（見第 9 節）。

### 8.2 開機查資料庫失敗時一律放行，範圍比原裁示大

**場景**：客戶的產品主機開機時，防竄改機制會去資料庫查「這台有沒有未解除的鎖定紀錄」。你原本裁的是「資料庫連不上時放行並警示」，理由是不能因為資料庫暫時掛了就讓客戶整套起不來。

**現況**：`jedi-integrity` 的 `boot_db_lock.py` 把**任何**查詢失敗都當成「連不上」處理，包括「表不存在」「權限不足」「資料庫密碼設定缺欄位」。後面這幾種其實是連得上、但查不成功。

**風險**：一個有主機權限、想繞過鎖定的人，如果能讓查詢失敗（例如改掉資料庫帳號的權限），開機就只剩檔案標記那一道。要你裁（見第 9 節）。

### 8.3 另開卡候選（不在本 arc）

- **離線機器的指紋可被接手**：受害機離線時，別人直接用受害機的機器指紋登記，會照「重灌後接手」的設計拿到同一列。這是既有設計，不是本批造成（CM-2052 抽查點出）。
- **公版只有一個管理員角色**：出貨公版只內建 Administrator，客戶自建角色從零勾。以後每加一顆讀取守門，客戶都可能踩到「選單變空」。長期正解是公版內建幾個一般角色範本，預勾讀取類權限。
- **兩支沒人呼叫的方法**：摘要報告的 `get_project_summary_reports`、`get_project_summary_report_by_id`，可刪。
- **稽核階段「目前進度」查詢**：非成員仍查得到資料（CM-2035 範圍外）。
- **`list_ap_parties` 查不到輪次就放行**：寫法偏寬，總測時留意（CM-2037 順帶）。
- **儲存設定新建時密碼留空**：只有前端擋，後端 `create_system_config` 沒檢查（CM-2063 順帶）。
- **`tamper_event_repo.exists` 沒人呼叫**：開機查鎖改由宿主的短命連線做，這支舊方法閒置。
- **管理者代刪問卷討論**：CM-2024 卡上沒要求，沒做。

### 8.4 擋得到、擋不到的分界

不要把這批修正理解成「風險歸零」。

- **代理程式（CM-2052）**：修完後，網路上的外人冒充不了代理程式、A 客戶冒充不了 B 客戶、被撤銷的機器當場斷線。**擋不到的是能實際碰到那台代理程式主機的人**，他撈得到機器上的私鑰與通行證。這是設計本身：代理程式要登入客戶主機做掃描，就一定得拿到那些鑰匙，而那台機器在客戶自己的機房。風險範圍從「網路上任何人」縮小到「能實際碰到那台機器的人」。
- **防竄改（CM-2053）**：修完後，換掉驗簽零件、刪標記檔、手寫回執、換核對目錄這幾條已知的一步得手路徑都堵住了。**擋不到的是有能力改整包執行檔（binary）的人**：驗簽工具編進核心層，是讓「換一個零件」不再可行，但能整包重編或改二進位的人仍然繞得過。這套機制的定位是擋住隨手改檔案，不是擋住有完整主機權限又懂內部運作的攻擊者。
- **#91 為什麼只記警示**：解鎖碼帳本檔毀損時如果直接拒絕開機，而原廠目前沒有「帳本壞了重新簽發」的流程，機器一壞就永遠解不開。所以降級成高等級警示，等原廠有重簽流程再收緊。

---

## 9. 給你的裁決清單

**① 什麼時候合回**
- 現在合：清理卡做完就照第 3 節步驟開始合回、發版、部署 DEV、總測。第 6 批與 5B-5 之後在主線上另外做。
- 等 5B-5 與第 6 批一起：三張剩下的卡也做完才一次合回。代價是第 6 批要等 CM-1849，時間不確定，這段期間主線繼續往前跑，合回衝突會變多。

**② 發版順序確認**
- 照第 3 節步驟表：套件先合先發（`jedi-common` 最先），BE 改 pin，FE，代理程式，然後雲端與代理程式同一次上 DEV。你只要回「照表」。
- 要調整：請指出哪一步要換。

**③ #96、#97 郵件密碼寫進紀錄檔與連線不認人**
- 另開卡納入本 arc：改動小（錯誤訊息不帶整組設定、加密時驗證對方憑證），可以跟這批一起合回、一起總測。
- 維持 CM-1606 另排：本批合回不等它，報告上這兩件繼續標未修。

**④ 開機查資料庫失敗時的放行範圍**
- 維持現狀：任何查詢失敗都放行並警示。客戶端資料庫任何小毛病都不會擋開機，但「改權限讓查詢失敗」這條繞法留著。
- 收窄：只有「真的連不上」才放行，「連得上但查詢失敗」改成拒絕開機。擋掉那條繞法，代價是資料庫權限或結構有問題時客戶會開不了機，要靠支援排除。

**⑤ 出貨基線什麼時候重產**
- 合回主線後、188 全量 build 前做（建議）：build 的出包檢查才不會紅。
- 等 STG／POC 上版前再做：現在不動 188 基線庫，但在那之前出不了包。

**⑥ 另開卡候選要開哪些**（第 8.3 節八項，每項回「開」或「不開」即可）
- 我的建議：「公版一般角色範本」與「儲存設定後端密碼空值檢查」值得開；「兩支死方法」「`tamper_event_repo.exists`」可以併進下一張清理卡；其餘總測時觀察再定。

**⑦ 小事，合回時順便裁**
- 套件 monorepo 還有 10 支沒有 `poetry.lock`（含 6 支封存的），要補：合回時一起跑。不補：維持現狀，只有那 17 支有 lock。
