---
title: 資安剩餘項目總分類
---

> **現況（2026-10-01）**：本文是 2026-09-26 的分類快照。文中「未修／待裁」的項目後來已全數處理——FR-114 第 8 批 12 組（CM-2211～2220 等）已修並隨 1.21.0 出貨；1.21.1 再修授權讀取（CM-2369）等。最終狀態以 `docs/security-report/SUMMARY.md` 問題總表為準（已修 206、拆除退場 15、裁定不修 12、SaaS 前必做 1）。以下內文保留當時的分類過程。

# 資安剩餘項目總分類（CM-2209）

> 給決策者一次看完、逐類拍板用。裁完後首腦照這份的編號轉成 FR-119 `handoff-to-fix-line.md` 那種交接格式，開 FR-114 第 8 批，一次修完再出一包（1.21.0b3 暫停 build 就是在等這份，見 `FR-114-STATE.md` 最末節）。
>
> 編號寫法：**#N**＝PM 總報告 `docs/security-report/SUMMARY.md` 的問題編號；**總表 N**＝資安總表 `security-scan-consolidated/README.md` §3.1 的項次。兩個都有就都寫。檔:行一律以**修正線工作區**（`fix/security-b1`，HEAD `1516d90bc`；套件 `e758cd6a`）為準——那才是第 8 批要改的底版，與主線行號可能差幾行。

## 1. 一句話結論

**還沒修的共 45 項**：要決策者裁（丙）**8 項**、已裁照修（甲）**7 項**、可直接修（乙）**19 項，分 8 組**、建議不修只記錄（丁）**2 項**、已裁暫緩（戊）**9 項**。

| 嚴重度 | 丙 | 甲 | 乙 | 丁 | 戊 | 合計 |
|---|---:|---:|---:|---:|---:|---:|
| 🟠 高 | 0 | 1 | 0 | 0 | 3 | 4 |
| 🟡 中 | 1 | 4 | 7 | 0 | 5 | 17 |
| ⚪ 低 | 7 | 2 | 12 | 2 | 1 | 24 |
| **合計** | **8** | **7** | **19** | **2** | **9** | **45** |

**三個要先知道的**：

- **原本以為還有一百多件沒修，實際是 45 件。** 主線那份 PM 總報告只回寫了第 7 批，第 1～6 批修好的 77 件它還寫「未修」；以修正線那份為準，前 145 件真正還掛著的只有 17 件（13 件未修、3 件部分修、1 件 SaaS 前必做），其中 8 件已裁暫緩（戊類）、#101 已裁不修（不列入）。
- **FR-120 這一輪掃出來的 38 項（總表 181～218）**：已修 11 項（其中 3 項是修正線順手已收、總表還沒標）、1 項非資安，**剩 26 項全在這份裡**，是第 8 批的主體。
- **乙類 19 項正常使用看不出任何差別**，建議決策者整組一次同意；決策力氣集中在丙類 8 項。**丙類只有 1 項是中風險，其餘都是低**。

---

## 2. 丙類：修了會改變產品行為，要決策者裁（8 項）

**現況（2026-10-01）**：丙類 8 項決策者 09-26 全部裁完並已修（見 `handoff-to-fix-line.md` 的 8-G／8-F／8-H／8-I／8-C／8-J），隨 1.21.0 出貨。

**每項格式**：一句白話（誰能做什麼壞事）→ 修了之後誰會碰到什麼變化 → 選項與代價 → 建議。

### 丙-1　總表 181｜低｜授權過期只靠每天凌晨一次的排程才轉唯讀

- **白話**：客戶授權到期那一刻不會立刻變唯讀，要等排程（UTC 凌晨 2 點，台灣早上 10 點）跑過才生效；排程如果沒在跑（例如只開即時通訊模式的部署），過期客戶可以一直照常新增、修改資料。
- **在哪**：`common/authz/license.py:280` `viewer_is_readonly()` 只讀資料庫存的狀態欄；排程在 `core/scheduler.py:275-323`。
- **修了會改變什麼**：過期客戶在**到期那一秒**就變唯讀，不再有「到期當天還能用到早上 10 點」的寬限。業務或客服若習慣「到期當天還能補照」，會接到客戶「怎麼突然不能改」的電話。
- **選項**：
  - **選項一：讀照時當場重算一次，取較嚴的那個**（`compute_target_status` 是純函式、不查資料庫）——即時生效，代價是上面那個寬限消失。
  - **選項二：維持排程，只補「排程多久沒跑就告警」**——行為不變，代價是縫還在，只是看得到。
- **建議**：選項一。授權到期規則本來就寫「到期即唯讀」，排程延遲是實作副作用、不是設計；要寬限應該用授權檔的寬限期欄位明確給，不是靠排程時間差。

### 丙-2　總表 184｜低｜首次安裝精靈同時按兩次可能建出兩家公司

- **白話**：安裝精靈「開通」這一步中間沒上鎖，裝機者連點兩次（或兩個人同時送），可能建出兩家公司、兩個管理員帳號。要先持有只印在裝機終端機的設定碼才打得到，實務上幾乎只有裝機者自己連點會踩到。
- **在哪**：`app/setup/service/setup_wizard_service.py:37-41`（註解宣稱有資料庫唯一約束，但實際沒有）、`:167` 查開通、`:249` 建立。
- **修了會改變什麼**：若選加鎖，第二個請求會收到「已開通」錯誤（409），精靈前端要能顯示這個訊息而不是一片空白——前端行為會變。
- **選項**：
  - **選項一：只改註解**，寫明「並發窗口存在，因需持有設定碼而接受」——零行為變化。
  - **選項二：加資料庫鎖包住「查開通＋建立」**，第二個請求排隊、輪到時回 409——要順手確認精靈前端收到 409 的畫面。
- **建議**：選項二。一次連點就留下兩家公司是很難事後清的髒資料，修法只有幾行。

### 丙-3　總表 187｜低｜授權過期唯讀有兩條縫

- **白話**：①過期後代理程式的註冊碼照樣能產生（`/agents/` 整段豁免唯讀檢查，範圍太寬）；②問卷填答走即時通訊那條路時完全不經過唯讀檢查，過期客戶照樣能填問卷。
- **在哪**：`common/middleware/license_readonly_mw.py:77`（豁免清單）；套件 `jedi-survey/jedi_survey/app/handler/fill_survey_socketio_handler.py:164`。
- **修了會改變什麼**：過期客戶**不能再新增代理程式**、**不能再填問卷**。已經裝好的代理程式照常回報（那條是機器對機器，建議保留豁免）。
- **選項**：
  - **選項一：兩縫都補**——豁免收窄成「代理程式回報結果那幾支」，問卷即時通道補唯讀檢查。
  - **選項二：只補問卷、代理程式豁免維持**——理由是代理程式屬基礎設施，過期時讓客戶還能接回機器方便續約後立刻恢復。
- **建議**：選項一。「過期＝唯讀」是對客戶說過的規則，唯讀期間新增機器與填問卷都是寫入。

### 丙-4　總表 190｜低｜雲端硬碟授權連結可以轉寄給別人代按

- **白話**：公司 A 的管理員產生一條「連接 Google 雲端硬碟」的授權連結，轉寄給公司 B 的人騙他按同意，B 的 Google 硬碟就被接進 A 公司的帳號。未實測。
- **在哪**：`app/cloud_integration/service/google_drive_integration_service.py:96-99` 產生 state、`:113-116` 回呼只看 state 本身。
- **修了會改變什麼**：授權流程必須**同一個瀏覽器**發起、同一個瀏覽器完成。「管理員產生連結、交給擁有 Google 帳號的 IT 同事去按」這種分工**會失效**——如果客戶現場真的這樣做，他們會卡住。
- **選項**：
  - **選項一：綁瀏覽器**（發起時寫一個 cookie，回呼時比對）——堵住，代價如上。
  - **選項二：不綁、改在回呼成功後寄一封確認信給發起人**——分工照舊，事後可察覺。
- **建議**：先問客服／實施有沒有看過「代按」的做法；沒有就選項一。

### 丙-5　總表 185｜中｜使用者上傳的檔案整個目錄公開在 /static/ 不用登入（**拿不準乙或丙，放丙**）

- **白話**：後端把使用者上傳檔案的資料夾設成網站的公開路徑，問卷答案 Excel、意見回饋附件等只要知道路徑就能抓。今天出貨的代理伺服器只轉 `/api/1.0` 與 `/socket.io`，所以正式站打不到；但有人為了排錯直接開 8000 埠、或客戶換自己的代理設定，就整包公開。
- **在哪**：`core/app_factory.py:95-99`（`static_folder` 指向上傳區，`static_url_path='/static'`）。
- **修了會改變什麼**：我查過修正線 BE、所有套件與前端，**沒有任何地方組 `/static/...` 網址給使用者用**（範本下載已在 FR-063.1b 移出 static；問卷答案上傳只是存檔備份，套件 `jedi-survey/.../task_survey_route.py:148`），所以正常使用看不出差別。
- **拿不準的原因**：只能證明「程式碼裡沒有」，證明不了「沒有客戶或維運手上存著 `/static/` 連結在用」（例如舊版信件裡的附件連結、維運腳本）。
- **修法**：`static_url_path=None`（或設一個不會對外的前綴），寫入路徑不動。
- **建議**：當乙類修（關掉公開路徑），release note 寫一句「`/static/` 不再對外提供」；若決策者知道有人在用這種連結，改成丙-5 另議。

### 丙-6　總表 200｜低｜批次完成任務的通知信把操作者暱稱原樣塞進信件 HTML

- **白話**：任何帳號把自己的暱稱改成一段 HTML／script，之後他批次完成任務時，收信人開信就看到他塞的內容（可偽造連結、按鈕）。
- **在哪**：`app/flow_control/service/job_batch_complete_service.py:95,105`、`app/flow_control/service/workflow_execution_service.py:1219`、`app/flow_control/service/project_service.py:776`（三處同寫法）。
- **修了會改變什麼**：暱稱改成純文字顯示。**暱稱裡本來就有 `<`、`&` 的正常使用者**（例如「R&D 王小明」），信裡會從「R&D」正確顯示——今天反而可能顯示錯。其餘人看不出差別。
- **拿不準的原因**：信件範本可能有地方刻意讓暱稱帶粗體等 HTML，跳脫後會變成字面顯示。
- **建議**：照修（跳脫），放丙只是因為動到寄出去的信件內容，請決策者知悉。

### 丙-7　#186／總表 165 剩下那一半｜低｜AI 儀表板查問卷缺模組授權

- **白話**：沒買問卷模組的客戶，透過 AI 儀表板對 AI 說一句話，照樣能查問卷資料（直接打網址那兩條已補，AI 儀表板這條沒補）。
- **在哪**：`di_containers/dashboard_apis/survey.py:20-45`（兩支申報）；要改套件 `jedi-ai-dashboard`（`jedi_ai_dashboard/app/registry.py` 加「需要的模組」申報欄）。
- **修了會改變什麼**：沒買問卷的客戶在 AI 儀表板問問卷相關問題會得到「無權限」而不是資料。**要動一支不在 b3 發版清單上的套件**（jedi-ai-dashboard），多發一支版。
- **選項**：
  - **選項一：套件加申報欄、兩支問卷查詢掛上**——根治，多發一支套件。
  - **選項二：先把這兩支從 AI 可用清單拿掉**——不改套件，代價是有買問卷的客戶也暫時不能用 AI 問問卷。
- **建議**：選項一，且與總表 218（同一支套件，AI 儀表板生成端點缺能力點）同一次發。CM-2194 首腦已把這件「轉決策者排批」。

### 丙-8　總表 218｜低｜AI 儀表板生成端點只驗商務授權、不驗能力點

- **白話**：角色設定上看不到 AI 儀表板的人（例如「稽核人員」「IT 執行人員」），直接打網址照樣能生成儀表板，而且每用一次要付兩次 AI 服務費。runner 已實測。
- **在哪**：套件 `jedi-ai-dashboard/jedi_ai_dashboard/api/routes/ai_dashboard_route.py:103`（只有 `@require_license`）；主專案 `core/plugins/ai_dashboard.py:52-61`。
- **修了會改變什麼**：這些角色直接打網址會被擋（403）。選單本來就看不到，所以**正常操作的人看不出差別**；會被擋的只有「知道網址、繞過選單」的人，或**有人手上書籤存著這個網址**。
- **拿不準的原因**：DEV 查到這兩個角色沒有該能力點，但客戶現場的角色設定可能不同——若某客戶把這功能開給了沒勾能力點的角色、靠書籤在用，修完會斷。
- **建議**：照修，與丙-7 同一次發 jedi-ai-dashboard；放丙只為了讓決策者知道「多發一支套件」。

> **總表 §7「目前真的還要決策者裁的 7 項」**（第 8／13／14／18／18b／5／21 項）是方向類待裁，本文不重寫；與本清單無重疊（第 18b 項管的是 §3.2 非資安第 28／29 項；第 21 項管「落地版證據分類停用」，只影響第 6 節 #130 的急迫度），請直接看 §7 那張短清單。

---

## 3. 甲類：已裁、照修（7 項）

**現況（2026-10-01）**：甲-1 ✅ 已修（#75，CM-2230／CM-2252）；甲-2～4 🗑️ 隨舊線退場（#9／#66／#67，CM-2222）；甲-5～7 ✅ 已修（#89 CM-2225、#132 CM-2226、#131 CM-2227），均 1.21.0 出貨。

決策者裁過要修，但還沒有修正卡或卡還沒 Done。

| # | 編號 | 嚴重度 | 一句白話 | 現況與出處 | 在哪（修正線） |
|---|---|---|---|---|---|
| 甲-1 | #75（總表 J 組） | 🟡 中 | 打包前端映像檔的腳本與前端三個 CI 設定檔裡，直接寫著一組內部套件倉庫帳密，都進了版本控制 | 已開卡 **CM-1998**（Not started）；批次規劃第 4 批「清憑證殘留」 | BE `scripts/build/build_fe_image.sh:49-51`；FE `.gitlab-ci.yml:21`、`.gitlab-ci-on-premises.yml`、`.gitlab-ci-terraform.yml`、`Dockerfile`（`NEXUS_AUTH`） |
| 甲-2 | #9 | 🟠 高 | 任何能登入的帳號，在舊版證據分類「預覽證據檔」填一個雲端硬碟檔案編號，就把那個 Google 帳號摸得到的任何檔案抓回來 | 已裁「舊線整組拿掉」，靠 **CM-1849**（Not started）拆 | 見總表 108～111；拆舊線路由 `api/routing.py` 舊 Drive 分類那段 |
| 甲-3 | #66 | 🟡 中 | 舊線的查結果、查報表、工作清單不檢查專案成員，查不到還會拿呼叫者自己公司的鑰匙去雲端硬碟讀 | 同上，CM-1849 | 同上 |
| 甲-4 | #67 | 🟡 中 | 舊線查雲端硬碟的搜尋條件用字串接起來，可被改寫搜尋範圍 | 同上，CM-1849 | 同上 |
| 甲-5 | #89 | 🟡 中 | 被稽核的一方在自己交來的證據第一頁寫一段話，就能指揮 AI 把自己歸進全部合規項目 | 已裁「只做事前預防」（M07 裁定），驗證卡 **CM-2061** 等 CM-1849 拆完才派；首腦已判「新線也叫同一個容器，退役後幾乎確定仍在」 | 套件 `jedi-evidence-classification/docker/container_entrypoint.py:374` |
| 甲-6 | #132 | ⚪ 低 | 證據分類程式用到的六個外部套件沒鎖版本、沒校驗碼，上游被掉包會直接裝進產品 | 已裁「開、校驗碼不能省」（D-b5B-5），同上等 CM-2061 | 套件 `jedi-evidence-classification/docker/requirements.txt`、`docker/Dockerfile` 安裝那一行 |
| 甲-7 | #131 | ⚪ 低 | 一份做過手腳的壓縮檔能把主機記憶體吃光；逾時後殘留程序繼續燒 AI 額度 | 已裁「現在修」（D-b5B-6），同上等 CM-2061 | 套件 `jedi_evidence_classification/infra/classifier_container_runner.py:156-186` |

> 甲-2～甲-4 三件舊線、甲-5～甲-7 三件分類容器，派工順序被 CM-1849（FR-107.6 舊線退場）卡住（乙-G 的 #129 同樣），而 CM-1849 本身是個跨兩支套件發版＋e2e 的大卡。**決策者要不要讓第 8 批直接做「刪舊線路由」這一小段、不等整張 CM-1849**，是這組唯一的排程問題。

---

## 4. 乙類：不改產品行為，可直接修（19 項，分 8 組）

使用者照正常方式用看不出任何差別。建議決策者**整組一次同意**。

### 乙-A　補歸屬／成員檢查（4 項，另 1 項修正線已收）

**現況**：✅ 已修（8-A，CM-2211，1.21.0 出貨）。

**這組是什麼病**：只驗「有登入」或「是網址上那個專案的人」，沒驗「你要動的東西屬不屬於那個專案／公司」。修法一律用 `common.authz` 既有守門，不另寫。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 202 | ⚪ 低 | 同公司任何登入帳號把網址換成別的專案編號，就看得到那個專案這一輪任務的完成數、進行中數 | 套件 `jedi-task-platform/jedi_task_platform/task/app/service/task_execution_service.py:139-141`（比照同檔 `:49` `_check_manager` 先解析專案，再呼叫 `assert_project_participant`） |
| 總表 214 | ⚪ 低 | 人員對帳四處查詢收了公司編號卻沒拿去用，平台管理員身分下會跨所有客戶比對（結果只寫附註、不影響權限） | `domain/oscal/service/reconciliation/person_reconciler.py:18,34,49,66`（比照 `organization_reconciler` 加 `tenant_id` 條件） |
| 總表 191 | ⚪ 低 | 驗證雲端硬碟應用程式憑證的端點，總部任何登入帳號都能拿任意一組憑證去試對錯 | **修正線已收**：`app/cloud_integration/service/drive_app_credential_verify_service.py` `verify()` 開頭 `require_platform_admin()`。**只剩確認**，不必開卡，併入第 7 節不一致 |
| 總表 205 | 🟡 中 | 還原摘要報告歷史版本時，不檢查那個歷史版本是不是這份報告的——專案成員可以拿別份報告（含別的專案）的內容蓋掉自己這份 | `app/project_summary_report/service/project_summary_report_history_service.py:66-85`（取出 history 後補 `history.report_id == report.id`，不符回 404；與總表 94 同寫法，總表註記併第 65 項同卡） |
| 總表 203 | ⚪ 低 | 孤兒資料清理如果某次排程沒帶到身分，會把「全部」正常資料判成孤兒整批刪掉（今天排程有帶身分，不會觸發） | `infra/flow_control/repository/job_binding_orphan_query.py:87,102,125`（三處 `NOT EXISTS`：刪之前先確認「看得到的任務數 > 0」，看不到就中止並記警告） |

> 總表 191 修正線已收、不計入，列在這裡是為了讓修正線知道不要重做。

### 乙-B　診斷包與紀錄遮罩（4 項，與總表 162「部分修」同組）

**現況**：✅ 已修（8-B，CM-2212，1.21.0 出貨）。

**這組是什麼病**：回廠診斷包與伺服器紀錄裡，密碼、登入權杖會以各種寫法原樣留下，遮罩程式只認得其中幾種。**要一次修完**——總表 162 就是只補一種寫法、其他三件未修的前車之鑑。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 186 | 🟡 中 | 即時通訊服務逐筆記錄封包，登入權杖明文進容器紀錄；診斷包收容器紀錄時沒遮，原樣帶回原廠 | `core/app_factory.py:189,191`（`logger=True`／`engineio_logger=True` 改關或降級）；`infra/support/collector/host_collector.py:136-156`、`infra/support/collector/staged_collector.py:128-153`（兩支都要過 `mask_log_lines`） |
| 總表 209 | 🟡 中 | 診斷包的設定快照遇到跨多行的值只遮第一行，第二行以後的密碼原樣留下（今天出貨的值剛好是單行） | `infra/support/collector/container_collector.py:94`（先遮值再串 `KEY=值`，或遮罩改成以整個值為單位） |
| 總表 211 | ⚪ 低 | 診斷包收的 API 連線紀錄，網址欄不在遮罩清單，網址帶權杖時原樣收進去 | `domain/support/entity/diag_bundle.py:30`（`MASKED_API_LOG_COLUMNS` 加 `url`） |
| #173／總表 162 剩下三件 | 🟡 中 | 證據分類逾時把 AI 金鑰寫進紀錄那條，CM-2193 只補了「KEY=值」一種寫法；其他寫法、資料庫紀錄改走日誌遮罩、打包前統一過一道三件未修 | `infra/support/diag_masking.py:164-169`（遮罩規則補 `password='值'`、`"key": "值"` 等寫法）；打包前統一過一道 |

> 總表 207（寄信失敗把整組寄信設定含密碼寫進紀錄）**修正線已修**（套件 `jedi-notification` commit `e12287e2`，CM-2111），總表還寫未修，見第 7 節。但它當初暴露的「遮罩不認得 `password='值'`」仍要在上面 `diag_masking.py` 那列一起補。


### 乙-C　輸出前跳脫與安全寫法（4 項）

**現況**：✅ 已修（8-C，CM-2213，1.21.0 出貨）。

**這組是什麼病**：使用者可控的字，輸出成 Excel／PDF／查詢條件時沒當成「資料」處理。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 199 | ⚪ 低 | 匯出任務 Excel 時，欄位以 `=` `+` `-` `@` 開頭會被 Excel 當公式，打開的人按了「啟用內容」就在他電腦上執行 | `app/flow_control/service/job_import_service.py:135-180` `export_excel`（沿用總表 §4 🅶 組那支「開頭是公式字元就前置單引號」的中和函式，不要寫第四份） |
| 總表 204 | 🟡 中 | 摘要報告匯出 PDF 時，圖片白名單放行了 SVG，SVG 可以指向內部網址讓伺服器去抓（繞過 CM-892 的防護） | `api/project_summary_report/routes/project_summary_report_route.py:42-49`（`data:image/svg+xml` 排除）與 `:166` `write_pdf(url_fetcher=拒絕一切外部網址)`，兩處都做 |
| 總表 197 | ⚪ 低 | 依名稱找雲端資料夾時只跳脫單引號、沒跳脫反斜線（與總表 110 同型，未實測） | `infra/cloud_integration/google_drive/google_drive_api_client.py:121`（先跳脫 `\` 再跳脫 `'`） |
| 總表 196 | ⚪ 低 | 雲端硬碟通知的 token 比對不是固定時間比對，理論上可用時間差猜 | `app/cloud_integration/service/google_drive_webhook_service.py:94`（`!=` 改 `hmac.compare_digest`） |

### 乙-D　維運腳本與診斷包路徑（3 項）

**現況**：✅ 已修（8-D，CM-2214，1.21.0 出貨）。

**這組是什麼病**：維運指令與出貨主機的預先蒐集，信任了容器內或索引檔裡的路徑。前提都是「已經能動容器內部的人」，但後果是 root 被騙寫到任意地方。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 208 | 🟡 中 | 系統診斷指令用猜得到的名字建暫存資料夾、不檢查是不是捷徑，容器內一般權限的人可預放捷徑騙 root 寫到別處 | `scripts/installer/guidant` diag 段（`:720`、`:775` 附近 `mkdir -p "$stage_host"`；報告寫的 `:700-708` 已漂移）改 `mktemp -d`，或建立前檢查不存在且非符號連結 |
| 總表 210 | ⚪ 低 | 出貨主機預先蒐集只照索引檔的路徑讀檔，服務名稱沒驗證，可塞 `../` 跳出暫存目錄 | `infra/support/collector/staged_collector.py:90,140`（服務名稱白名單，讀檔前 `realpath` 檢查在暫存目錄內） |
| 總表 206 | ⚪ 低 | 匯出診斷包時，起始時間帶時區、結束時間不帶，比較時程式當掉回 500 | `app/support/service/diag_bundle_export_service.py:76` `_resolve_window`（兩邊統一轉成帶時區） |

### 乙-E　首次安裝精靈錯誤處理（1 項）

**現況**：✅ 已修（8-E，CM-2215，1.21.0 出貨）。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 183 | ⚪ 低 | 安裝精靈設定碼欄位塞中文或特殊字元，伺服器回 500 並寫一筆錯誤紀錄（不會放行），任何人能灌假警報淹掉真的錯誤 | `common/setup/setup_token.py:92`（比對前只允許英數字，或兩邊 `.encode()` 再比）；呼叫點 `api/setup/routes/setup_route.py:70`、`:97` 不用改 |

### 乙-F　刪沒人用的死碼（1 項）

**現況**：🗑️ 已拆除（M06-16 備用服務由 8-E 拆掉，CM-2215）。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| #118（M06-14＋M06-16） | ⚪ 低 | 三支接收檔案路徑的程式直接開檔讀寫；兩組接好但沒人用的備用服務沒有權限檢查——現在無害只因沒人用 | **一半已收**：M06-14 三支（`save_bpmn_to_file`／`export_xml`／`import_xml`）已隨套件 commit `4a416ece`（CM-2059）刪除。**剩 M06-16**：`di_containers/flow_engine/workflow_excution_containers.py:4,114-117` 的 `JobExecutionService` provider 全庫零引用，拆掉 import 與 provider 即可（同檔 `ElementVariableService` 有人用，保留） |

> 總表 215（解析工作單新增規則一律允許）修正線已收（`scripts/sql/2026-09-25-fr114-ap-ar-parse-jobs-rls-fix.sql:21-26,58-63`，CM-2195），只剩出貨基線重產時帶到（CM-2197 已列），不計入。

### 乙-G　證據分類刪除殘留（1 項，與甲-5～7 同一張驗證卡 CM-2061）

**現況**：✅ 已修（#129，CM-2227，1.21.0 出貨）。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| #129（M07-6） | ⚪ 低 | 使用者按「刪除整批」後，判定結果與分類紀錄仍永久留在後端主機上——使用者以為刪乾淨了其實沒有（要主機權限才看得到，不是越權） | 套件 `jedi_evidence_classification/app/service/evidence_batch_service.py:1417-1427`（刪整批時連同 `classifier_container_runner.py:85-89` 建的工作目錄一起清） |

### 乙-H　刪檔順序（1 項）

**現況**：✅ 已修（總表 217，CM-2228，套件 `5ec9b617`，1.21.0 出貨）。

| 編號 | 嚴重度 | 一句白話 | 在哪（修正線） |
|---|---|---|---|
| 總表 217 | 🟡 中 | 刪檔時先刪硬碟上的實體檔、再刪資料庫紀錄；紀錄被資料庫擋下時檔案已經沒了，回應卻是「成功」 | **修正線已擋掉主要打法**：刪除入口 `upload_file_route.py:113` 掛了歸屬檢查、原廠共用檔（`storage_scope='system'`）刪除一律擋（`core/plugins/file_upload.py:240-253`）。**剩順序**：套件 `jedi-file-upload/.../infra/adapter/minio/minio_adapter.py:129-138`（以及 `:144-176` 批次刪）改成先刪紀錄、成功才刪實體檔。總表註記併第 37 項同卡；驗收加「一般帳號刪原廠共用檔要失敗且實體檔仍在」 |

**乙類合計**：乙-A 4＋乙-B 4＋乙-C 4＋乙-D 3＋乙-E 1＋乙-F 1＋乙-G 1＋乙-H 1＝**19 項**。中風險 7 項（總表 205／186／209／162／204／208／217），其餘低。

---

## 5. 丁類：建議不修或只記錄（2 項）

**現況（2026-10-01）**：總表 213 🗑️ 已拆除（CM-2373，1.21.1）；總表 195 🚫 裁定不修。

| 編號 | 嚴重度 | 理由 | 什麼時候要回頭修 |
|---|---|---|---|
| 總表 213 | ⚪ 低 | 即時通知有一個頻道不驗身分、任何人能加入任意房間。但伺服器端**沒有往這個頻道送資料**，前端唯一會用到的元件 `ProcessDiscussBox.vue` 在畫面上沒被引用，面板判不成立 | **`ProcessDiscussBox.vue` 被接回畫面之前**，先在 `app/notification/handler/notification_socketio_handler.py:19,34,43` 補身分驗證 |
| 總表 195 | ⚪ 低 | 雲端硬碟通知的註解說有去除重複通知，實際沒有。後果只是同一個變更可能被處理兩次；同步處理本身是冪等的（重複匯入同一檔會命中既有對應） | 若日後出現「同一份證據被匯入兩次」的客訴，或同步處理改成非冪等時 |

> 總表 201「查不到專案就放行」是決策者 09-26 裁定、CM-2208 已照做，不列在這裡；CM-2208 runner 實查 DEV 54,267 個流程實例全部查得回專案，實務上走不到那一支。

---

## 6. 戊類：已裁暫緩（9 項）

**現況（2026-10-01）**：#23 ✅ 已修（CM-2271＋CM-2369）、#104 ✅ 已修（CM-2271）、#24 ✅ 已修（12 張補齊，餘 1 張屬平台層設定）、#105 🕓 SaaS 前必做、#98／#91 🚫 裁定不修、#99 📋 已裁記錄不修、總表 193 🚫 裁定不修、#130 ✅ 已修（CM-2223）。

只列，不展開。

| 編號 | 嚴重度 | 一句話 | 裁定 |
|---|---|---|---|
| #23（M18-8） | 🟠 高 | 被停權客戶用子單位帳號，就能刪掉母公司頭上的停權紀錄自己復權 | 第 6 批「資料庫最後一道牆」：程式面先修、測過一版、決策者裁「開牆」才開卡（`FR-114.../dispatch-plan.md` §4）；是 #104 的直接後果 |
| #104（M03-7） | 🟠 高 | 資料庫隔離規則方向寫反：子單位看得到、改得到、刪得到母單位資料（出貨基線共 11 條） | 同上 |
| #24（M20-1） | 🟠 高 | 13 張標有客戶歸屬的表，資料庫隔離牆沒真正生效（部分修） | 同上 |
| #105（M20-5） | 🟡 中 | 走 SaaS 時合規文件那塊沒有最後一道防線 | 🕓 SaaS 上線前必做 |
| #98（M17-7） | 🟡 中 | 安裝程式每套都種同一組原廠超級管理員密碼、不強制首次登入改 | 已裁「維持記錄、暫不調整」 |
| #99（M18-7） | 🟡 中 | 開發環境私鑰簽的授權檔，正式環境也認 | 已裁「等正式簽發站建好一併處理」 |
| #91（M08-4） | 🟡 中 | 解鎖碼帳本被改成亂碼時只記高等級警示、不拒絕（部分修） | 已裁「原廠有重簽流程後再收緊」 |
| 總表 193 | 🟡 中 | 雲端硬碟資料夾（含根資料夾）分享設定是「知道連結的任何人都能編輯」，根資料夾連結任何登入帳號拿得到 | 決策者 09-26：POC 階段暫緩（要綁客戶 Workspace 網域，短期不綁） |
| #130（M07-7） | ⚪ 低 | AI 服務金鑰放在分類程式的啟動參數上，同主機任何帳號讀得到 | 已裁「可以緩」（D-b5B-6）；前提是**落地版刻意沒裝分類執行環境，只影響開發機**——哪天落地版要開自動分類（§7 第 21 項），要先修 |

---

## 7. 三份紀錄不一致清單（給修正線合回報告時用）

**7-1　主線 PM 報告 vs 修正線 PM 報告（#1～145）**：兩邊狀態不同的共 **94 件**。

- **77 件**主線寫「未修」、修正線寫「已修」——以修正線為準，合回時整批覆蓋即可。
- **6 件**主線寫「裁定刪除」、修正線寫「已修」；**5 件**主線寫「部分修」、修正線寫「已修」；**1 件**主線寫「裁定做法」、修正線寫「已修」；**1 件**主線「裁定拆除」、修正線「已修」——同上，以修正線為準。
- **要人看一眼的 4 件**：
  - **#91**：主線「未修」、修正線「⚠️ 部分修（毀損只記警示不拒絕）」。
  - **#101**：主線「未修」、修正線「⚠️ 部分修（lock 已入版控；https 裁不修）」。
  - **#124**：主線「未修」、修正線「🚫 裁定不修（比照 #101）」。
  - **#109**：兩邊都是「裁定退役」，只是格式不同，不用處理。

**7-2　主線 PM 報告 #146～198**（修正線那份沒有這段）：主線寫未修／部分修共 11 件，實際：

| # | 主線寫 | 實際 | 依據 |
|---|---|---|---|
| #169 #170 #182 | ⬜ 未修 | ✅ 已修 | CM-2191 Done（總表 155～157 已標） |
| #171 | ⬜ 未修 | ✅ 已修 | 修正線 `job_force_start_service.py:136-138`（CM-2113 補歸屬、`a3fe63b03`） |
| #183 | ⬜ 未修 | ✅ 已修 | CM-2191 Done（總表 158） |
| #184 | ⬜ 未修 | ✅ 已修 | CM-2148 修正待驗證（`a3fe63b03`），總表 160 還寫未修 |
| #172 | ⬜ 未修 | ✅ 已修 | CM-2192 Done（總表 161 已標） |
| #186 | ⬜ 未修 | 🟡 部分修 | CM-2194 Done 入口一、三；入口二＝本文丙-8 |
| #173 | ⬜ 未修 | 🟡 部分修 | CM-2193 只補一種寫法；剩下＝本文乙-B |
| #161 | 🟡 部分修 | 🟡 部分修（檔案下載擁有者另評估） | 修正線 `core/plugins/file_upload.py:191-212` 程序書檔案已有讀寫歸屬檢查；**建議首腦實測一次再改「已修」** |
| #179 | 🟡 部分修 | 🟡 部分修（決策者選 A） | 決策者已裁只拿掉 email 與設備 IP，**這就是終局**，建議改「✅ 已修（依裁定範圍）」 |

**7-3　主線模組頁**：`M06-flow-engine.md` 第 17～22 條（＝#169～#171、#182～#184）全標未修，實際全修（見上表）；第 14 條（#118 前半）標未修，實際已隨 `4a416ece` 刪除。

**7-4　資安總表 §3.1**（修正線是照 PM 報告編號派工，不回寫總表）：

| 項次 | 總表寫 | 實際 | 依據 |
|---|---|---|---|
| 118、119 | 未標狀態 | ✅ 已修 | CM-2041 Done；修正線 PM 報告 #64 已修 |
| 122 | 未標狀態 | ✅ 已修 | 修正線 `ssp_docx_generator.py:62` `render(context, autoescape=True)`；PM 報告 #102 已修 |
| 126 | 未標狀態 | ✅ 已修 | 修正線 PM 報告 #151 已修（CM-2055） |
| 159 | 未標狀態 | ✅ 已修 | 同 #171 |
| 160 | 未標狀態 | ✅ 已修 | 同 #184（CM-2148 修正待驗證） |
| 191 | 未標狀態 | ✅ 已修 | 修正線 `drive_app_credential_verify_service.py` `verify()` 開頭 `require_platform_admin()` |
| 207 | 未標狀態 | ✅ 已修 | 套件 `jedi-notification` `e12287e2`（CM-2111）；主線套件源碼 `smtp_mail_adapter.py:52,57` 仍是舊寫法，發版後才進出貨 |
| 215 | 未標狀態 | ✅ 已修 | CM-2195 migration 已改 INSERT 規則 |
| 1～117 大部分 | 多半未維護 | 以修正線 PM 報告為準 | 修正線照 PM 報告編號派工、不回寫總表（CM-2199 已回寫第 7 批，第 1～6 批未回寫） |

**7-5　總表 §7 待裁清單**：短清單下方註記「第 51 項已裁、FR-114 開卡中，卡號待回填」→ 卡號是 **CM-2208**（Done，`1516d90bc`）。

---

## 8. 附：怎麼核對的

| 段 | 範圍 | 用的來源 | 怎麼判 |
|---|---|---|---|
| 段一 | PM 報告 #1～145 | 修正線 `.claude/worktrees/wt-fix-security/docs/security-report/SUMMARY.md` 問題總表＋`M07`／`M06`／`M10` 模組頁 | 狀態欄非「已修」的全部列出（13 件未修、3 件部分修、1 件 SaaS 前必做、9 件裁定不修／退役／刪除——其中 #118 裁定刪除但只刪了一半，列乙-F），逐件對照裁定；#118 另開套件 git log 確認前半已刪；#101、#124 已裁不修、不列入 |
| 段二 | PM 報告 #146～198（總表 118～180） | 主線 SUMMARY 為起點；第 7 批卡清單 `FR-114-2609-security-fix-dispatch/cards/made-b7*.json`（35 張）逐張 `notion_case.py get` 查狀態 | 33 張 Done、發版卡 CM-2197 與母卡 CM-2170 Not started；主線寫未修但有 Done 卡涵蓋的以卡為準；拿不準的開修正線程式碼核對（`job_force_start_service.py`、`ssp_docx_generator.py`） |
| 段三 | 總表 181～218 | 總表 §3.1、`scan-U*.md`、`handoff-acceptance-brief.md`、§7 已裁記帳 | 已修 11 項：182／188／192／194／198／201／212／216（Notion 卡 Done）＋191／207／215（修正線程式碼已收、總表未標）；189 為非資安（§3.2）；其餘 26 項逐項開修正線檔核對行號是否漂移 |
| 裁定 | — | 修正線 SUMMARY「已裁定」各列＋主線 SUMMARY「這一輪新裁定的十九件」＋總表 §7（刪除線與已裁記帳）＋`FR-114-STATE.md` 最末三節＋卡片 CM-2061／CM-2194 回寫 | 裁過的照登，沒重判 |

**判「乙還是丙」用的準則**：使用者照正常方式用（走選單、不改網址、不繞路）是否看得到差別。會被擋的只有「繞過選單直打網址」的，算乙；正常流程會變（寬限期消失、分工斷掉、錯誤訊息出現、多發一支套件）的算丙。拿不準一律丙並寫理由（丙-5、丙-6、丙-8）。

**打折處**：
- 行號以修正線 HEAD `1516d90bc`／套件 `e758cd6a` 為準，第 8 批開工前若修正線又前進，runner 要照卡上「先 grep 驗行號」的紀律重新對。
- 「`/static/` 沒有任何地方在用」只查了程式碼（BE、全部套件、前端 `src/`），沒查客戶端或維運手上存的舊連結——所以丙-5 放丙。
- 丁-3（總表 201 查不到專案）的「全部查得回」是 DEV 的數字，STG／POC 沒查（唯讀可查，但本卡不必要）。
