---
title: FR-119 交修正線清單——十九件裁定＋掃描重啟後修正卡缺口（給 FR-114 首腦）
---

# 交修正線清單：掃描線要交給 FR-114 的全部東西

> 整理卡 CM-2150（母卡 CM-2139）｜整理日期 2026-09-25｜只整理、不執行：沒有開卡、沒有改 FR-114 任何檔、沒有改總表。
> 出處：總表 `docs/features/security-scan-consolidated/README.md`（§3.1 第 118～180 項、§4 分組、§7 第 33／35 項）、裁定表 `decision-draft.md` 末尾、FR-116／FR-118 站 README「修正線要接的」、各棒報告。「修正分支現況」欄是本棒 2026-09-25 開 `fix/security-b1` 工作區（BE HEAD `9ff0a5060`、套件 HEAD `ab68291b`）實查的結果。

## 這份給誰、怎麼用

**讀者**：FR-114 首腦。你懂程式、但沒看過掃描線 09-21 重啟後那 40 棒報告。
**用法**：四段各一張表，一列一件，讀完就能開卡。

- **第一段**：你**已經做完**的卡，被掃描線查出缺口或被決策者裁退回的。要重開或補卡。
- **第二段**：第 118～180 項裡**還沒有卡**的，已按同卡關係併好。每組一張新卡。
- **第三段**：十九件裁定照抄，每件加一句「修正線要做什麼」；最後是套件發版清單。
- **第四段**：決策者裁「拆掉」的網址，列出拆之前要確認的事與要一起拆的東西。
- 最後一節是**決策者已裁、不要重問**的清單。

幾個詞：

- **守門**：程式裡檢查「你有沒有資格做這件事」的那段。
- **修正分支**：`fix/security-b1`，FR-114 的工作區，還沒合回主線。
- **出貨基線**：新客戶安裝時拿到的資料庫結構（`scripts/init/02-schema.sql`）。動了資料庫結構或隔離規則就要重產；重產屬決策者裁示。
- **發版**：改了 jedi-* 套件就要發新版，主專案才拿得到。

🔴 **開卡前先看這四件**：

1. **第 154 項是本批唯一「不必登入」的高風險，而且你已經發出去的 jedi-common 1.3.0 讓它慢了約 8 倍**（CM-2065 改了同一段比對）。正式版 1.21.0 前必須修。見第一段 1-6。
2. **CM-2037 要退回**：稽核團隊名單的修法擋不住跨客戶（第 42 項已裁）。見第一段 1-1。
3. **第 66 項已升為高**，但稽核計畫選單與儀表板兩支不在 CM-2037 範圍、沒有卡接。見第一段 1-2。
4. **總表第 133 項登記的舊修法是錯的**，照舊修法修等於沒修。見第一段 1-7。

---

## 第一段：你已做完、但要補或退回的卡（8 列）

| # | 哪張卡 | 缺口是什麼（白話） | 在哪裡 | 怎麼修 | 決策者裁定 | 開卡建議 |
|---|---|---|---|---|---|---|
| 1-1 | **CM-2037**（2-5，Done） | 🔴 稽核團隊名單「查得到輪次才檢查你是不是專案成員」。別家客戶的輪次被資料庫隔離藏起來、查回來是空的，於是整段檢查跳過、名單（姓名／Email／電話）照樣回去。**擋住了同客戶跨專案，沒擋住跨客戶** | 修正分支 `app/flow_control/service/assessment_plan_app_service.py:714-722` `list_ap_parties`（本棒實查：`:719` `if curr_user_id is not None`、`:721` `if rnd is not None`） | 查不到輪次就回 404。照同檔另一支 `resolve_ap_and_check_participant` 的寫法。**`curr_user_id` 也要一起改必填**（見 1-4） | 第 42 項：**退回 CM-2037**（主專案改、不發版） | 退回 CM-2037；可與 1-2、1-4 同一棒 |
| 1-2 | **CM-2037** 範圍外（第 66 項，**已升高**） | 稽核計畫**選單**與**儀表板**兩支只查登入、不查專案成員。儀表板還不比對「這一輪是不是這個專案的」。CM-2037 只修了 `audit_round_route.py` 那 8 支，這兩支沒卡接 | `api/flow_control/routes/assessment_plan_route.py:38` 選單 GET、`:108` `ApDashboardResource.get`；repo `infra/flow_control/repository/flow_control_project_repo_impl.py:511` `_resolve_dashboard_round_id`（`:549`／`:632` 兩處呼叫） | ①選單與儀表板在服務層補「是專案成員」檢查（走 `common.authz` 的 `assert_project_participant`）；②`_resolve_dashboard_round_id` 解出輪次後比對 `project_id` 等於網址專案，不符就回 404 | 無（修法無爭議） | **新卡**，與 1-1 同批（同一支檔、同一種漏） |
| 1-3 | **CM-2041**（2-9，Done） | CM-2041 修了第 118／119 項的兩支刪除，但**第 129 項零修法**：把程序書掛到控制項時，文件編號換內部 ID 那一步查的是全系統，沒限定「只能是這份計畫池子裡的」。可以把別人的文件掛過來、再從回應拿到檔案代號去下載（同客戶跨專案） | 主專案 `app/oscal/service/ssp_document_pool_service.py:159` `add_control_mappings`（本棒實查：`resolve_doc_uids_to_ids(doc_uids)` 沒帶計畫範圍）；套件 `jedi-compliance-audit` `infra/repository/ssp_document_pool_query.py:205-206`。**查核項目那支掛載方法同病、要一起改** | 把計畫範圍傳進 `resolve_doc_uids_to_ids()`，查詢條件加「屬於這份計畫的池子」。樣板照抄 `app/module_frame/service/module_frame_reference_document_service.py:161,173` 的 `_get_doc_or_404`。**要改套件** | 無 | **新卡**（或補 CM-2041）；jedi-compliance-audit 要發版 |
| 1-4 | **CM-2037** 的寫法 | 套件守門寫成 `curr_user_id: int = None`、「有傳才檢查」。以後任何新呼叫端忘了傳，會安靜放行、不報錯 | 套件 `jedi-compliance-audit`：`app/service/audit_round_app_service.py:223` `list_rounds`、`:232` `get_round`、`:900`；`poam_app_service.py:257` `list_items`、`:284` `get_item`；`assessment_result_app_service.py:404` `get_findings`、`:709` `list_risks`（本棒實查共 7 支）。主專案 `list_ap_parties`（1-1）同型 | 7 支＋`list_ap_parties` 的 `curr_user_id` 拿掉預設值改必填，呼叫端全部補傳 | 第 38 項：**改必填**，修正分支上順手改、**合回前**；套件要發版 | 併 1-1 同一棒（同是 CM-2037 退回範圍） |
| 1-5 | 手測陷阱（非缺口，是驗收會驗錯） | 開發機主 checkout 的 `.venv` 有 12 個 `.pth` 指到修正分支的套件工作區，但主 checkout 的路由還是舊的、沒傳 `curr_user_id`（本棒實查：主 checkout `audit_round_route.py` 24 處 vs 修正分支 32 處）。**在主 checkout 手測「輪次讀取守門」，套件新守門一次都不會觸發**，會誤判成「沒修好」或「修好了」 | 主 checkout `.venv/lib/python*/site-packages/*.pth`；總表 §0.5「已修要打折看」 | 輪次守門手測只在修正分支工作區或 190（1.21.0b2）做。合回後照 FR-114 STATE 第 5 步清掉那 12 個 `.pth` | 無 | 不開卡；寫進 1-1 那張卡的驗收段 |
| 1-6 | **CM-2065**（5C-4，Done，**已發版**） | 🔴 **第 154 項（高，不必登入）**：每個請求在檢查身分之前，攔截器就把請求內容拿去「遮密碼」，那段比對遇到特製內容越算越久，幾個請求就卡住全部工人、所有客戶停擺。CM-2065 改了同一段比對（加上吃 JSON 跳脫序列），讓它**再慢約 8 倍**。jedi-common 1.3.0 已含 CM-2065（本棒實查 tag `jedi-common-v1.3.0` 包含 `a9562dd0`），**190 上的 1.21.0b2 已經帶著這個惡化版** | 套件 `jedi-common` `jedi_common/utils/common_utils.py:145` `mark_password`（比對規則 `:159-160`）；主專案 `common/middleware/app_mw.py:167`（寫日誌）、`:172`（寫 api_log），都在遮罩前沒限長度 | 兩層：①套件把比對規則改成線性時間的寫法（根因）；②攔截器遮罩前先截長度，超過就截斷並標「已截斷」。**修完用同一組長度重測，確認耗時與長度成正比** | 總表 §7 第 33 項：**發版前必須一起修**（決策者要求轉告 FR-114） | **新卡、排最前**；jedi-common 要發版（1.3.1），BE 改 pin。建議與 2-O 同卡（同一個攔截器，見第二段） |
| 1-7 | 總表第 133 項的修法（總表登記的舊修法錯了） | 第 133 項（刪框架／刪版本不檢查還有沒有客戶在用）總表舊修法是「照叫編輯路徑那支 `_require_no_references()`」。但那支查的是「基準線指向公版目錄」，而建資源庫一律先複製再引用副本，**正常流程永遠零筆**（DEV 591 筆基準線、指向公版 0 筆）——照原修法等於沒補。編輯公版目錄那六支、匯入覆蓋那一道呼叫的也是同一支，**從來沒擋過東西** | 刪框架 `app/oscal/service/framework_app_service.py:185`、刪版本 `framework_version_app_service.py:224`；編輯路徑 `framework_version_edit_service.py:207/218/230` 呼叫、`:258-266` 定義；匯入覆蓋 `framework_parse_job_service.py:386-403`；正確該查的欄 `compliance.module_frames.oscal_framework_version_uid`（`resource_library_app_service.py:512` 寫入） | 三處（刪版本／編輯公版目錄／匯入覆蓋）改成用**系統層跨客戶查詢**查 `module_frames.oscal_framework_version_uid` 有沒有未刪除的資源庫在用，有任何引用就拒絕。「版本必須是草稿」那道照舊留著 | 第 46 項：**三處同卡換**；第 133 項維持中 | **新卡**（主專案，不發版）。同卡可順手記「刪主版本、刪有子版本的版本會吐 500」（非資安） |

| 1-8 | **CM-2052**（代理程式通行證，Done）連帶 | 🔴 **修正線啟動會炸**：09-23 修 CM-2052 那顆 commit（套件 monorepo `c4acc687`）給 `jedi-remote-agent` 的 `AgentTaskService` 建構子加了一個**必填**零件 `remote_agent_domain_service`，但修正線 BE 的組裝表沒跟著接。修正線一啟動、走到代理程式任務那條路，會直接建構失敗（`TypeError` 缺參數）。這是修正線自己的 bug，不是掃描線發現的資安問題，FR-120 DI 比對腳本跑修正線基準時抓到的 | 修正線 BE worktree `di_containers/agent_task/agent_task_containers.py:36` `agent_task_service = providers.Factory(AgentTaskService, ...)` 只給 `agent_task_domain_service` 與 `lifecycle_listener`；套件端 `jedi_remote_agent/app/service/agent_task_service.py:47` 宣告必填 | 在那個 Factory 補 `remote_agent_domain_service=remote_agent_container.remote_agent_domain_service`（主專案 `di_containers/flow_control/flow_control_containers.py:437` 已有同樣接法可抄） | 無（不是裁定項，是合回前必修） | 併回 CM-2052 或開一張連帶卡；**合回主線前必修**。兩份比對表：主線基準 `docs/features/FR-120-2609-host-residual-security-scan/di-wiring-audit.md`、修正線基準同資料夾 `di-wiring-audit-fixline.md`（「必填缺接」那節就是這一處；「🔴」那節多出的 4 處是修正線新加零件在修正線容器沒接，一併核） |

> **跟 FR-114 STATE 的衝突**：STATE §1 寫「程式面 50／53 全 Done」「已發版 18 支套件」。1-1、1-3、1-4、1-6 會讓 CM-2037／CM-2041／CM-2065 三張 Done 卡重開或出新卡，jedi-common、jedi-compliance-audit 需要再發一版；**1-6 會影響 STATE §2 第 13 條「正式 1.21.0 前必做」**——建議把第 154 項加進那條清單。

---

## 第二段：第 118～180 項還沒有卡的，建議怎麼開（21 張）

**從新卡清單剔除的（不要派）**：

| 項次 | 為什麼剔除 |
|---|---|
| 第 118／119 項 | CM-2041 已修在修正分支（套件 `delete_by_uid` 本身仍只認編號，已登記為 CM-2041 的已知限制，不另開） |
| 第 122 項 | CM-2055 已修（修正分支 `f85a82edb`：`ssp_docx_generator.py:62` `autoescape=True`） |
| 第 126 項 | CM-2055 已修（同 commit：`ssp_control_impl_import_service.py:140/164` 改用 `set_text_cell`） |
| 第 159／160 項 | 已修：第 159 項 CM-2113（`job_force_start_service.py:139` 核對任務屬於網址專案）、第 160 項 CM-2148（`:59` 補成員檢查） |
| 第 166／167 項 | 第 36 項裁**拆網址**，見第四段 4-1 |
| 第 170 項 | 第 43 項裁**拆網址**，入口消失、問題跟著消失，見第四段 4-2 |
| 第 129 項 | 已列在第一段 1-3（CM-2041 缺口） |
| 第 133 項 | 已列在第一段 1-7 |
| 第 154 項 | 已列在第一段 1-6 |
| 第 168 項 | 修法由第 37 項裁定決定，排在 2-P（下表），不是剔除 |

**每一列＝一張建議新卡。** 「發版」欄寫要發哪支套件；「基線」欄是出貨基線要不要重產。

| 卡 | 項次 | 一句話 | 嚴重度 | 該補檢查的位置（檔:行） | 併卡理由 | 發版 | 基線 |
|---|---|---|---|---|---|---|---|
| **2-A** | 120＋123＋125＋134＋137＋138＋153 | 🔴 **合規範本寫入有七處漏守**：編控制項清單、Excel 匯入覆蓋、Word 匯入覆蓋三條路不檢查權限與歸屬；編輯範本時改「分享範圍」有檢查、改「內容」沒有；改流程圖的檢查包在 if 裡；刪改子項檢查網址編號、動手改另一個；只有「建立」權限能用 Excel 匯入覆蓋既有範本 | 高×3（120／123／125）、中×4 | 120：`app/oscal/service/resource_library_app_service.py:364/412`（＋同路徑 `_init_ao_workflows`）；123：`ssp_excel_import_app_service.py:803-817` 上傳端、`:441/:478` 確認端；125：`ssp_docx_import_app_service.py:138-166` 上傳端、`:335` 確認可換目標、`:358-387`；134：`app/module_frame/service/module_frame_service.py:117`（守的那半在 `:112`）；137：`module_frame_item_service.py:62-65` 檢查移到 if 外、`:84` 寫入；138：`module_frame_item_service.py:161`（改）、`:229`（刪）、`:247`；153：`module_frame_import_service.py:124-129`（`is_update` 為真時加查 `module-frame.update`） | 同一塊地（合規範本寫入），總表明寫「三條必須一起修，只補一條另外兩條照樣進得去」；153 底下直接呼叫 134／138 那兩支 | 無（主專案） | 否 |
| **2-B** | 128＋149 | 🔴 **SSP 匯出成 Excel 範本只查「你能不能讀資源庫」、不查「這份計畫是不是你的」，跨客戶一個 GET 就下載整份計畫**；空白範本附帶全公司 email、設備 IP、資訊系統清單 | 高（128，跨客戶）、低（149） | 128：`app/module_frame/service/ssp_import_template_app_service.py` `generate_for_ssp` 第一行補 `require_participant(ssp_uid)`（照隔壁 export 抄，DI 在 `di_containers/module_frame/module_frame_containers.py`），入口 `ssp_import_template_route.py:82-110`；同檔 `generate()`（`:223`）順手補程式層檢查；149：同檔 `:600` 方法、`:640-641` 撈下拉資料改依呼叫者權限縮減 | 同一支服務檔，總表第 128／149 項都寫「修第 128 項時順手補」 | 無 | 否 |
| **2-C** | 135＋136＋139＋140＋141＋142＋143＋144＋145 | **範本模組九項、十二個讀取入口不檢查權限**，其中人員名單、設備清單、系統元件、繼承授權、系統特性跨公司都讀得到 | 中×8、低（145） | 全部補 `@require_capability("module-frame.read")`：135 `api/module_frame/routes/module_frame_route.py:25-27`、`:45-47`；136 `module_frame_item_route.py:31-33`；139 `module_frame_reference_document_route.py:61-63`；140 `module_frame_control_objective_default_route.py:50-52`、`:109-111`；141 `module_frame_party_route.py:26-28`；142 `module_frame_inventory_route.py:19-20`；143 `module_frame_components_route.py:19-20`；144 `module_frame_leveraged_route.py:30-31`＋`module_frame_system_characteristic_route.py:29-30`；145 `module_frame_ssp_resources_route.py:32-34` | 同一批讀取入口、同一行修法，總表要求一張卡。⚠️ **先確認 `module-frame.read` 權限點存在且現有角色都拿得到**，否則症狀是永遠 403 且畫面沒錯誤訊息；135 選單那支的角色設計要先問決策者（總表第 135 項左欄）；145 runner 另建議補 `api/oscal/routes/module_frame_template_ssp_route.py`，**待決策者定** | 無 | 視權限點種子：若要補種子 migration → 要重產 |
| **2-D** | 146＋147 | **範本底下六類附屬資料寫入、Excel 批次存檔控制項清單，只問「查得到嗎」不問「是不是你家的」** | 中×7 | 146：六支服務的 `_template_ssp_id()` 解出範本後各加 `assert_scope_writable(mf.tenant_id, mf.scope)`（`module_frame_party_service.py:86`、`module_frame_components_service.py:72`、`module_frame_inventory_service.py:77`、`module_frame_leveraged_service.py:84`、系統特性、SSP 資源各一）；147：`module_frame_template_import_service.py:329` 補同一行，同批補 `validate_items`（`:265`）與兩支下載（`:173`／`:187`） | 同型（查得到就放行），總表建議合成一張橫向卡；**六支要一起補，補五支等於沒補** | 無 | 否 |
| **2-E** | 121＋175 | **建資源庫的入口只驗登入**；而且不檢查框架版本發佈了沒，草稿也能拿去複製 | 中、低 | 121：`api/oscal/routes/resource_library_route.py:50` 補能力點（範本 `ModuleFrameRoute.post`），**Excel 匯入、Word 匯入那兩條建資源庫的路也要一起補**；175：`resource_library_app_service.py:460-467` 在 `get_version` 之後補「必須已發佈」，補這一支三個入口一次蓋到 | 同一支 `create_resource_library`，FR-118 README 修正線第 1 件 | 無 | 否 |
| **2-F** | 124＋127＋130＋176＋179 | **壓縮炸彈四個入口、兩個 repo**：SSP 的 Excel／Word 上傳、稽核計畫 Word、稽核結果 Excel 都只量壓縮後大小，解開整份塞記憶體；稽核結果 Excel 還一路讀到上傳者決定的最後一列（4.9KB 檔吃 355MB） | 中×5 | 主專案：`app/oscal/service/excel_parser/parser.py:42`（`read_only=False`，124／130 同一行）、`ssp_excel_import_app_service.py:124-126`；`ssp_docx_import_app_service.py:50/131` 限制、`:759` 解析（**一份上傳會被打開三次，檢查要放第一次開檔前**）。套件 `jedi-compliance-audit`：`import_adapter/registry_base.py:24`（176）、`ar_report_parser/airasia_cmmc_l1_ar_v1.py:162-164`＋`registry.py:17`（179） | 總表明寫「病灶四個入口兩個 repo、共用同一支檢查函式、同一張卡」；FR-116 README 修正線第 4 件。上限數字參考 `config/config.py:194-200`，但那組是照檢測規則包訂的，合不合適由開卡的人判斷 | jedi-compliance-audit | 否 |
| **2-G** | 173 | **一份幾 KB 的特製 Word 讓 SSP 匯入解析卡到 120 秒逾時，四個請求讓整個 API 對所有客戶停擺**，六個位置 | 中 | `domain/oscal/parser/docx_section_extractors.py:538-541`、`:545-549`、`:74-86`；`domain/oscal/parser/control_id_matcher.py:20`、`:50-76`；轉接器「標籤：值」切法與兩處找排第幾（詳見總表第 173 項） | 🔴 **兩支檔各有一條比對規則，同一份檔會先後跑到，只修一邊等於沒修**——驗收要用同一份惡意檔從上傳整條跑一次，不能只各自單元測試。與 2-A 第 125 項、2-F 第 127 項同一條上傳路，排程一起看 | 無 | 否 |
| **2-H** | 131＋152＋177 | **比對規則沒防長輸入**：Excel 版本號那格（跑在版本檢查之前，不用合法範本）、Excel 範本匯入檢核的網頁標籤比對、稽核計畫 Word 解析器三條規則，送超長字串就卡住一條工人 | 中（131）、低×2 | 131：`app/oscal/service/excel_parser/parser.py:143/148/151-153`——**最好直接刪掉這份、改用既有的 `version_check.parse_semver`**；152：`app/module_frame/service/module_frame_import_service.py:52` 開頭擋長度、`:23` 規則改 `<[^<>]{1,200}>`；177：套件 `ap_report_parser/airasia_cmmc_l1_v1.py:57／:62／:107-113`，`:205` 起先截長度 | 同型，總表寫「一起修」 | jedi-compliance-audit（177） | 否 |
| **2-I** | 171＋172 | **特製的 CMMC PDF 把解析拖垮**：頁數沒上限、每頁不釋放；目錄行比對遇超長一行平方級變慢 | 低×2 | 套件 `jedi-oscal-v2`：`infra/adapter/base_parser_adapter.py:37-57` 開檔後先擋總頁數、每頁 `page.close()`；`infra/adapter/cmmc/cmmc2_lv2_parser_adapter.py:73` `_TOC_PATTERN`、`:81` 開頭 `len(line) > 500` 直接當內文、`:407` 改 `str.find`。主專案 `framework_parse_job_service.py:215` 前可再擋一次 | 同一支檔、同一次發版；第 45 項裁與 jedi-oscal-v2 依賴清理**同次發版** | **jedi-oscal-v2** | 否 |
| **2-J** | 132＋174 | **解析失敗把原始錯誤訊息回給前端**：PDF 那側吐套件結構與 SQL，Word 那側吐伺服器暫存檔完整路徑 | 中、低 | 132：`app/oscal/service/framework_parse_job_service.py:228` 拿掉 `: {e}`（本棒實查仍在），`:215-219` 那個 try 也包住寫資料庫、一併涵蓋；174：`domain/oscal/parser/docx_parser_core.py:23`（訊息不接 `original`）、`:171-172`／`:193-194`，服務層 `ssp_docx_import_app_service.py:220／:226` 不用 `str(e)` | 同一種修法，總表 §4 🅶 組寫「一張卡兩邊一起改」 | 無 | 否 |
| **2-K** | 148＋150 | **Excel 範本公式注入**：上傳時塞進去的公式、或任何人把自己暱稱改成公式，會在別人下載範本開啟時執行（連空白範本也算） | 中×2 | 改用 jedi-common 的 `set_text_cell`（已在 1.3.0，本棒實查 `export_safety.py:29`），五處六行逐一列成驗收項：`app/module_frame/excel_template/lookup_builder.py:47`、`:91`、`:93`；`generator.py:578`、`:319`、`:247`（本棒實查這兩支檔尚未用 `set_text_cell`）。⚠️ **`generator.py:543` `_apply_autofill_formulas` 是系統刻意寫的公式，不能一起改**；只修 578 會守一半 | 總表 §7 第 31 項已裁「合一張卡」，含帳號模組的使用者匯入範本；上傳端 `sheet_handlers.py:64` 已裁「只記警告日誌、不改內容」（§7 第 32 項） | 無（jedi-common 1.3.0 已含 `set_text_cell`） | 否 |
| **2-L** | 151 | **幾 KB 的 YAML 讓伺服器算到當掉**（引用展開無上限） | 低 | `infra/module_frame/adapter/yaml_to_module_frame_parser_adapter.py:14` `parse` 開頭改自訂 SafeLoader、遇 alias 拒絕；`:35` 加層數與節點數上限；格式錯目前炸 500 要改回「格式錯誤」 | 單獨一張（也可併 2-H 同一輪） | 無 | 否 |
| **2-M** | 155＋156＋157＋158 | **規劃頁任務的四個入口不核對「這張任務屬不屬於網址上那個專案」**：清單、批次匯入確認（可順便改別的專案的任務）、匯出、匯入驗證兩步 | 中×2、低×2 | 155：`app/flow_control/service/job_service.py:269` `list_jobs` 開頭補 `assert_project_participant`、專案編號改必填（本棒實查未修）；156：`job_import_service.py:398` `confirm_import`、白名單 `:413` 改用「這個計畫底下的任務」並核對計畫的專案＝網址專案；157：`:104` `export_excel` 核對計畫的專案＝網址專案；158：`:198` `validate_import`、`:315` `revalidate_items` 開頭補「計畫屬於網址專案＋你是成員」（本棒實查四支都未修） | 總表 §4 🅰「第八種長相」：五個入口要逐一列驗收項。決策者原裁第 156 項併 FR-114 卡 2-7（CM-2039），但 **2-7 已 Done**，所以改開新卡；第 159／160 項已修（見剔除表）。**「任務屬於哪個專案」的判斷請用 CM-2113 的唯一歸屬判斷**（`resolve_project_id_for_job`），見本段末 | 無 | 否 |
| **2-N** | 161 | **「AI 金鑰只限平台管理員」開關一放開，原廠 AI 金鑰會被送到客戶自己指定的伺服器**（今天打不到） | 中 | `app/system_config/service/ai_provider_key_resolver.py:133`／`:154`（位址跟金鑰同層取）；`app/system_config/service/tenant_storage_config_seeder.py:52-55`（新租戶不抄金鑰）；開關說明 `common/constant/ai_service_config.py:29` | 單獨 | 無 | 否 |
| **2-O** | 163＋164 | **操作日誌兩個洞**：網址加長就不留紀錄（欄位超長寫入失敗）；來源 IP 全記成前端代理 | 中、低 | 163：`common/middleware/app_mw.py:160-197` 寫入前把所有有上限的欄位截斷（網址 500、IP 255、瀏覽器資訊 5000），寫入失敗至少留一筆替代紀錄；164：`app_mw.py:189`、`common/integrity/adapters.py:268-272`、jedi-iam `jedi_iam/api/routes/login_route.py:26` 三處統一，**不能直接改讀 `X-Forwarded-For`**，要讓後端只信 nginx 那一跳 | 同一個攔截器；決策者已裁「同卡截所有欄位」「三處讀 IP 一起統一」。**建議與 1-6 第 154 項同卡**（同一個 `before_request`） | jedi-iam（164 那處） | 否 |
| **2-Q** | 162 | **證據分類跑超過 30 分鐘逾時，原廠 AI 金鑰以明文寫進後端日誌** | 中 | 套件 `jedi-evidence-classification` `infra/classifier_container_runner.py:189-190`（`raise ... from None`，更根本改 `--env-file`）；主專案 `infra/support/diag_masking.py:164` `_LOG_SECRET_LINE_RE` 補認 `KEY=值` | ⚠️ 套件那半要照外部套件異動規範先提醒、決策者點頭才動 | jedi-evidence-classification | 否 |
| **2-R** | 165 | **沒買問卷模組的客戶照樣能用問卷的「填答」那半**（14 支方法沒掛授權檢查） | 低 | 套件 `jedi-survey` `api/routes/task_survey_route.py`、`question_answer_route.py`、`question_answer_history_route.py`、`question_answer_history_detail_route.py`，每支 `@jwt_required()` 下加 `@require_license("survey")`（行號見 FR-115 W1 報告總覽表） | 決策者已裁三個入口一組一張卡 | jedi-survey | 否 |
| **2-S** | 169 | **把稽核「判定」接到「風險」時，套件不檢查兩者是不是同一份稽核結果**（唯一呼叫端有擋，是地雷） | 低 | 套件 `jedi-oscal-v2` `app/service/assessment_risk_service.py:109-125` `link_findings` 開頭逐一比對 `ar_result_id`、`:132-143` `get_risk_findings` 同樣過濾；呼叫端現有那道留著 | 守在套件；可併 2-I 同次 jedi-oscal-v2 發版 | **jedi-oscal-v2** | 否 |
| **2-T** | 178 | **兩張「解析工作單」表的資料庫隔離規則是 5 月已判定壞掉、SSP 那張早修掉的舊寫法，7 月建表照抄**——DEV 實測子公司看得到母公司的工作單 | 低 | `scripts/sql/2026-07-02-ap-docx-parse-jobs.sql:40-42`、`scripts/sql/2026-07-03-ar-xlsx-parse-jobs.sql`、`scripts/init/02-schema.sql:24943`。照 `2026-05-01-fix-ssp-docx-parse-jobs-rls.sql` 改成 `app_tenant_allowed_for_session(tenant_id)`＋超級管理員放行 | 兩張表一起修（FR-116 README 修正線第 5 件） | 無 | 🔴 **要重產**（新 migration，屬決策者裁示；只套 DEV） |
| **2-U** | 180 | **確認匯入稽核結果時，佐證資料照前端送來的存、不核是不是這一輪的** | 低 | 主專案 `api/project/serializers/ar_import.py:38`；套件 `jedi-compliance-audit` `ar_import_app_service.py:301`、`assessment_result_app_service.py:552-580`：確認時比對工作單存的候選池、連結只收 `http`／`https`；一般新增觀察網址 `audit_round_route.py:286-311` 同修 | 🔴 **檔案那半要等總表第 34 項（檔案預覽補歸屬檢查）才真正關得起來**，開卡時寫明依附 | jedi-compliance-audit | 否 |
| **2-P** | 168 | **專案經理能改寫、再刪掉稽核人員寫的「改善建議」** | 低 | 主專案 `api/project/routes/audit_round_route.py:440` `RiskRemediationsRoute.post`、serializer `api/project/serializers/audit_round.py:211-212`（`uuid`／`lifecycle` 無值域限制）；套件 `poam_app_service.py:310` `add_remediation` | 第 37 項已裁「稽核方專屬」：類型是「建議」那筆**拒絕覆蓋、拒絕刪除**，不做另存留痕 | jedi-compliance-audit | 否 |

**特別標的幾件**（卡上要寫清楚）：

- **第 154 項**（第一段 1-6）：唯一不必登入的高風險，排最前。
- **第 66 項升高**（第一段 1-2）：判定矩陣、風險清單、改善計畫四支讀取內容比輪次清單機密得多，三人面板一致評高；底下六張 oscal 表資料庫隔離全關，程式這一關漏了沒有備援。
- **第 173 項**（2-G）：六處同卡，兩支檔的比對規則只修一邊等於沒修。
- **第 176 項**（2-F）：壓縮炸彈四入口兩 repo，併第 124／127 項。
- **第 178 項**（2-T）：壞隔離規則兩張表，出貨基線要重產。
- **第 171／172／177／179 項**：資源耗盡，分在 2-I、2-H、2-F。
- **第 169 項**（2-S）：守在套件，不是呼叫端。

**「任務屬於哪個專案」要不要再裁**：總表 §7 第 35 項寫 CM-2108「方案待決策者裁」，但 FR-114 LOG 第 5 棒記載決策者已裁「掃描線 CM-2108 插單四件同批」，CM-2113（輪次鏈唯一判斷）已 Done。看起來**總表那句已過期**，2-M 直接用 CM-2113 的判斷即可——請首腦開卡前對照 CM-2113 卡確認。本棒不改總表。

**數字**：第二段 21 張新卡（2-A～2-U），加第一段新卡／重開 6 件（1-1 退回、1-2、1-3、1-6、1-7、1-8；1-4 併 1-1，1-5 不開卡），**合計建議 27 張**。若 2-L 併 2-H、2-S 併 2-I、2-O 併 1-6，可壓到 24 張。

**第 118～180 項 63 條逐條去向**（本棒腳本核過，無遺漏無重複）：新卡 2-A～2-U 涵蓋 51 條；第一段 3 條（第 129、133、154 項）；拆網址 3 條（第 166、167、170 項）；已修 6 條（第 118、119、122、126、159、160 項）。

---

## 第三段：十九件裁定結果（照抄裁定表，不改判）

| 項次 | 一句話 | 裁定 | 備註（照抄） | 修正線要做什麼 |
|---|---|---|---|---|
| 第 36 項 | 任務設定樹、目前 SSP 兩支網址：補守門／拆 | 選 B：拆網址 | 與第 44、43 項同批；套件 api/routing.py 拆兩支、要發版 | 開拆網址卡（第四段 4-1），jedi-compliance-audit 發版 |
| 第 37 項 | 改善建議：稽核方專屬／經理可改但留痕 | 選 A：稽核方專屬 | 第 168 項修法＝類型是「建議」那筆拒絕覆蓋與刪除；不做另存留痕 | 開 2-P |
| 第 38 項 | 修正分支守門參數：改必填／維持選填 | 選 A：改必填 | 修正分支 fix/security-b1 順手改，FR-114 合回前；套件要發版 | 第一段 1-4，併進 CM-2037 退回 |
| 第 39 項 | 回退標記讀取：列死碼／先問產品 | 選 B：不清、記功能待補 | 階段回退作廢表已寫、讀的那半沒接；功能上不會抓錯（輪次 ssp_id 決定現用）；記「凍結歷史標記作廢／瀏覽」功能待辦，三支讀取保留 | **不歸修正線**。三支讀取方法不要當死碼清掉 |
| 第 40 項 | 複製 SSP 參照：修程式不補快照／修程式也補快照／先不開卡 | 選 A：修程式、舊快照不動 | §3.2 第 36／37 項同卡修在套件；DEV 47 份凍結快照留著不清不補（凍結證據不事後動）；STG／POC 出貨前視情況再查 | 開一張非資安卡（總表 §3.2 第 36＋37 項，修在 jedi-oscal-v2 `ssp_clone_service.py:170-176`、`oscal_io_service.py` 再匯入那幾處）；修第 37 項前先用一份 Word 在 DEV 跑兩次確認。**不寫資料修補** |
| 第 41 項 | 凍結保護：套件加第二道／維持一層 | 選 A：套件加第二道 | 凍結是套件自己定義的（snapshot_ssp／FROZEN_STATUS「immutable marker」），套件 update_* 自己擋 frozen；與第 45 項同次發版 | jedi-oscal-v2 `ssp_service.update_*` 遇已凍結就拒絕（先確認凍結當下那一步不會被擋），與第 45 項同卡 |
| 第 42 項 | CM-2037 團隊名單：退回改 404（點頭／不點頭） | 點頭：退回 CM-2037 | list_ap_parties 改「查不到輪次就 404」；主專案改不發版 | 第一段 1-1 |
| 第 43 項 | 稽核中重排：一律走退回／允許重生 | 選 B：拆網址 | generate-draft 前端零呼叫、後端只有該路由呼叫、v1.20.0 release note 已點名退役；連同 launch-audit／start-auditing／ar/finalize 三支一起裁；e2e step 同拆；第 170 項隨之消失 | 開拆網址卡（第四段 4-2） |
| 第 44 項 | 更新稽核計畫網址：拆／補守門 | 選 B：拆網址 | 與第 36、43 項同批 | 開拆網址卡（第四段 4-3） |
| 第 45 項 | 套件依賴清理：同次發／分開發 | 選 A：同次發版 | jedi-oscal-v2：第 171／172 項修＋defusedxml 補宣告＋拿 pyyaml／ImportType.YAML,JSON＋第 41 項凍結第二道＋§3.2 第 36／37 項，一次發 | 套件 `pyproject.toml:15` 補 `defusedxml`、拿掉 `pyyaml`，順手拿掉 `ImportType.YAML`／`JSON`；與 2-I、第 41、40 項同一次 jedi-oscal-v2 發版 |
| 第 46 項 | 另兩道引用檢查：同卡換／只修刪除 | 選 A：三處同卡換 | 刪版本／編輯公版目錄／匯入覆蓋三處改查 module_frames.oscal_framework_version_uid，有任何客戶引用即拒絕；理由是保護稽核依據可追溯（業界慣例已引用版本不硬刪）；第 133 項維持中 | 第一段 1-7 |
| 第 47 項 | 捨棄解析工作單：收緊／維持 | 選 A：收緊 | 捨棄解析工作單改稽核員或管理者，與 Excel 側一致；套件一行、同次發版 | jedi-compliance-audit `ap_docx_import_app_service.py:172` 守門換成 `resolve_ap_and_check_auditor` |
| 第 48 項 | `blsadmin` 旗標：測試資料／產品允許 | 選 A：測試資料 | 首腦唯讀查三環境：DEV admin＋blsadmin(102)、STG 只 admin、POC 只 admin；STG／POC 乾淨，DEV blsadmin 拿不拿掉屬整潔不裁 | **不開卡** |
| 第 49 項 | 對照查詢輪次編號：改必填／維持 | 選 A：改必填 | get_wf_ids_by_control_ids round_id 必填；套件一行、同次發版 | jedi-compliance-audit `wf_control_mapping_lookup_query.py:30-53` 的 `round_id` 拿掉預設值 |
| 第 50 項 | 任務入口與批次（U7）：掃／不掃 | 選 A：掃 | U7 進下階段主專案掃描（決策者 2026-09-25 裁） | **不歸修正線**（掃描線下階段） |
| 第 51 項 | 權限共用層那句：照盤點更正（點頭／不點頭） | 選 A：更正 | 總表 §0.5 補句；common/authz 排 U1 第一棒 | **不歸修正線** |
| 第 52 項 | 系統設定守門補掃：併 U12／單獨一棒／不補 | 選 A：補掃 | 併 U12 | **不歸修正線** |
| 第 53 項 | 組裝檔專掃：工具掃 17 支／工具掃全部／腳本對照全部 | C 先、A 後、B 不做 | 先開實作卡寫腳本比對 75 支建構參數 vs 容器參數；U13a／U13b 兩棒帶著腳本結果掃 17 支沒碰過的 | **不歸修正線**。腳本實作卡由掃描線開 |
| 第 54 項 | 人員對帳跨公司比對：補併 U12（點頭／不點頭） | 選 A：補 | 併 U12（與第 52 項合計約 1,770 行） | **不歸修正線** |

### 套件發版清單

照目前修正分支版號（`pyproject.toml`）往上推；版號由 FR-114 首腦依實際改動定。**發版前同一支套件的改動全部做完、驗完才發**（FR-114 STATE 已知的坑：iam 補發三次）。

| 套件 | 現在版號 | 這一包含什麼 | 建議下一版 |
|---|---|---|---|
| **jedi-oscal-v2** | 2.4.0 | 第 171／172 項（2-I）＋第 45 項依賴清理（defusedxml 補、pyyaml 拿、`ImportType.YAML`／`JSON` 拿）＋第 41 項凍結第二道＋§3.2 第 36／37 項（第 40 項）＋第 169 項（2-S，可選同包） | 2.5.0（有新行為：凍結拒寫） |
| **jedi-compliance-audit** | 1.2.0 | 第 36 項拆兩支網址（4-1）＋第 38 項 7 支 `curr_user_id` 必填（1-4）＋第 47 項捨棄收緊＋第 49 項 `round_id` 必填＋第 37 項／第 168 項建議不可覆蓋刪除（2-P）＋第 129 項掛載限計畫（1-3）＋壓縮炸彈與列數上限（2-F 的 176／179）＋第 177 項（2-H）＋第 180 項（2-U） | 1.3.0（拆網址、必填參數是不相容改動，呼叫端要同步） |
| **jedi-common** | 1.3.0 | 🔴 第 154 項 `mark_password` 線性化（1-6） | 1.3.1，**正式 1.21.0 前必發** |
| jedi-iam | 1.4.3 | 第 164 項 `login_route.py:26` 讀 IP 統一（2-O） | 1.4.4 |
| jedi-survey | 1.2.0 | 第 165 項 14 支補 `require_license("survey")`（2-R） | 1.2.1 |
| jedi-evidence-classification | 1.3.0 | 第 162 項逾時不吐金鑰（2-Q；先照外部套件異動規範取得決策者點頭） | 1.3.1 |

---

## 第四段：拆網址清單（3 組、7 支網址）

⚠️ **拆的是網址（路由），不一定是背後的方法**。4-2 的 launch-audit／start-auditing／finalize 背後的套件方法**也被畫面上的「階段推進」按鈕呼叫**（修正分支 `app/flow_control/service/oscal_stage_handlers.py:46`／`:62`／`:82`），**方法不能刪，只拆直打的網址**。

| # | 網址 | 在哪裡 | 前端零呼叫的證據（本棒 2026-09-25 grep） | e2e 有沒有在打 | 拆時要一起拆什麼 |
|---|---|---|---|---|---|
| **4-1a** | 任務設定樹 `GET /project/<project_uid>/ap/<ap_uid>/task-setup/tree`（第 36 項／第 166 項） | 套件 `jedi-compliance-audit` `api/routing.py:33-36`（`ProjectTaskSetupTreeResource`）；服務 `app/service/task_setup_service.py:18` | 修正分支 FE：零命中（`TaskSetupView.vue` 已在 CM-2047 拆掉）。⚠️ **FE `feature/review` 主線還有 `src/views/project/TaskSetupView.vue:109` 在打**，合回後才消失——拆網址要排在 FE 合回之後，或同批 | 無（site-regression 零命中） | 路由那段；套件 `TaskSetupService` 與 `plugin/contract.py:55` 那一欄、主專案 `di_containers/flow_control/flow_control_containers.py:469-470`、`core/plugins/compliance_audit.py:123` 的組裝；`infra/repository/flow_control_task_setup_repo_impl.py:247` `_resolve_ssp`（若無其他呼叫者）。FE i18n `task-setup.json` 與 menu 字串可一起清。**拆前確認沒有 AI 儀表板或外部整合在用**（第 36 項原文要求，掃描那棒沒做） |
| **4-1b** | 目前 SSP `GET /project/<uid>/current-ssp-uid`（第 36 項／第 167 項） | 套件 `api/routing.py:31`（`ProjectCurrentSspRoute`）；服務 `app/service/project_current_ssp_service.py:35` | FE 只剩定義沒人叫：`src/config/api/api.js:263` `PROJECT_CURRENT_SSP`、`src/service/SspService.js:189` `getCurrentSspUid()`，全 FE 零呼叫者 | 🔴 **有**：`site-regression/support/data-factory/ssp-factory.js:13-14` `getLivingSspUid()` 直打，被 `steps/project-management/ssp-helpers.js:9` 引用。另一支同名函式 `task-survey-factory.js:15` 改走 `GET /grc/project/{uid}` 取 `ssp.id`，**可照它改寫** | 路由；`ProjectCurrentSspService` 與組裝（`flow_control_containers.py:249-250`、`core/plugins/compliance_audit.py:121`、套件 `plugin/contract.py:55`）；FE `api.js:263` 與 `SspService.getCurrentSspUid`；e2e `ssp-factory.js` 改走 `/grc/project/{uid}`（到 test repo 做） |
| **4-2a** | 重生草稿 `POST /audit-round/<round_uid>/ap/generate-draft`（第 43 項／第 170 項） | 主專案 `api/project/__init__.py:52`、`api/project/routes/audit_round_route.py:172` `AuditRoundApGenerateDraftRoute`；服務 `app/flow_control/service/assessment_plan_app_service.py:672` `generate_draft_for_round`（唯一呼叫者是該路由 `audit_round_route.py:180`） | FE 零命中（`api.js` 連定義都沒有）；v1.20.0 release note（`docs/spec-site/current/release-notes/v1.20.0.md:110`）已點名「前端零呼叫、只有 e2e step 在打，待開卡裁決後退役」 | 🔴 **有**：`site-regression/steps/journeys/j5a-negatives.steps.js:99`（「AP 重生草稿」） | 路由＋`generate_draft_for_round` 方法（後端無其他呼叫者，可一起刪）；e2e 那一條 step 與對應 scenario |
| **4-2b** | `POST /audit-round/<round_uid>/launch-audit` | `api/project/__init__.py:46`、`audit_round_route.py:92` `AuditRoundLaunchAuditRoute` | FE 只有 `api.js:213` 註解與 `:287` `GRC_LAUNCH_AUDIT`（另一條 `/grc/project/.../ap/.../launch-audit` 的舊常數）定義，全 FE 零呼叫者。畫面走 `StageService`（`/project/:p/audit-round/:r/stage/advance`） | 🔴 **有，而且是前置推進工具**：`support/data-factory/audit-round-factory.js:40-43` `launchAudit()`、`ssp-factory.js:19-21`；feature `journeys/j4-phase-guards.feature:13/20`、`j5-state-machine-negatives.feature:32/42/52`、`modules/project-management/project-ssp-edit.feature:22`；steps `lifecycle.steps.js:289`、`j5a-negatives.steps.js:95` | **只拆路由，不刪套件 `audit_round_app_service.launch_audit`**（階段推進在用）。e2e：factory 改用 `advanceStage`（`audit-round-factory.js:254` 已有、檔頭 `:18-22` 已寫「前置推進必須改用 advanceStage」），j4／j5 負向測試改打階段推進端點或刪除。**e2e 改動量大，建議先在 test repo 開一棒改完再拆網址**，否則回歸全紅 |
| **4-2c** | `POST /audit-round/<round_uid>/start-auditing` | `api/project/__init__.py:47`、`audit_round_route.py:105` `AuditRoundStartAuditingRoute` | 同 4-2b（`api.js:214` 只有註解） | 🔴 **有**：`audit-round-factory.js:51-53` `startAuditing()`；j4／j5 同上；`j5a-negatives.steps.js:96` | 同 4-2b：只拆路由，套件 `start_auditing` 保留（`oscal_stage_handlers.py:62` 在用） |
| **4-2d** | `POST /audit-round/<round_uid>/ar/finalize` | `api/project/__init__.py:73`、`audit_round_route.py:360` `AuditRoundFinalizeRoute` | FE 零命中 | 🔴 **有**：`audit-round-factory.js:118-120` `finalizeRound()`；`steps/journeys/j5-negatives.steps.js:41` | 同 4-2b：只拆路由，套件 `finalize_audit` 保留（`oscal_stage_handlers.py:82` 在用） |
| **4-3** | 更新稽核計畫 `PUT /grc/project/<pid>/ap/<ap_uid>`（第 44 項） | 主專案 `api/flow_control/__init__.py:106`、`api/flow_control/routes/assessment_plan_route.py:51-91` `AssessmentPlanDetailResource.put`；服務 `app/flow_control/service/project_service.py:536` `update_assessment_plan`（空殼，`return None, False`） | FE 只有 `api.js:204` `GRC_AP` 定義（註解寫 PUT），全 FE 零呼叫者 | 無（site-regression 零命中） | `put` 方法＋`AssessmentPlanUpdateRequestSchema`（若無他用）＋`project_service.update_assessment_plan` 空殼＋路由裡 Drive 改名同步那段呼叫；FE `api.js:204` 常數。⚠️ **同一個 Resource 還掛著 GET，只拆 PUT、不拆整個路由**。同檔 DELETE 已於 2026-07-21 退役，可照那段寫法 |

**拆的順序建議**：4-3（無 e2e、主專案）→ 4-2a（一條 e2e step）→ 4-1（等 FE 合回、改一個 e2e factory、發套件）→ 4-2b／c／d（先到 test repo 把前置推進改成 `advanceStage`，再拆）。

---

## 決策者已裁、不要重問

1. **十九件全部裁完**（2026-09-25），裁定照第三段，不重開。
2. 第 36、43、44 項：**拆網址**，不補守門。
3. 第 37 項：改善建議**稽核方專屬**，不做另存留痕。
4. 第 38、49 項：選填參數**改必填**。
5. 第 39 項：回退標記讀取**不清**，記功能待辦，不歸修正線。
6. 第 40 項：修程式、**已凍結的快照不動**（不清不補）。
7. 第 41、45 項：jedi-oscal-v2 **同一次發版**，含凍結第二道。
8. 第 42 項：**退回 CM-2037**，查不到輪次回 404。
9. 第 46 項：刪版本／編輯公版目錄／匯入覆蓋**三處同卡**改查 `module_frames.oscal_framework_version_uid`，第 133 項維持中。
10. 第 47 項：捨棄解析工作單**收緊**到稽核員或管理者。
11. 第 48 項：`blsadmin` 是**測試資料**，不開卡。
12. 第 50～54 項：主專案下階段掃描範圍，**不歸修正線**。
13. 第 153 項：只有「建立」權限**不能**用匯入覆蓋既有範本（總表 §7 第 30 項，09-24 裁）。
14. 第 148＋150 項＋帳號模組使用者匯入範本**合一張卡**（§7 第 31 項）；上傳端**只記警告日誌、不改內容**（§7 第 32 項）。
15. 第 154 項**發版前必須與 CM-2065 一起修**（§7 第 33 項）。
16. 第 156 項與任務其他入口**同卡**（原裁併 CM-2039；2-7 已 Done，改開新卡 2-M，精神不變：五個入口逐一列驗收項）。
17. 第 163 項**同卡截所有有上限的欄位**；第 164 項**三處讀 IP 一起統一**。
18. 第 165 項**三個入口一組一張卡**。
19. 掃描線的裁定與缺口**一次打包、不零碎轉**——這份就是那一次。

