# C4a 掃描報告 — 稽核計畫 Word 匯入（CM-2103）

> 範圍：14 檔／1,271 行，跨兩個 repo 配對切。套件側 `jedi-compliance-audit` 13 檔／1,155 行（Word 匯入服務、Word 解析器與註冊表、兩支共用接縫、解析工作的實體／介面／domain service／repo／mapper／資料表定義）；主專案側 1 檔／116 行（`api/project/routes/ap_docx_import_route.py`）。
> 掃描工具：Claude Code 官方 `claude-security` plugin，effort low，**兩側各跑一次**（工具只認它所在那個 repo 的檔案）。
> 掃描基準：套件側 `eafc7ae511d5`（monorepo 主 checkout，`feature/review`）；主專案側 `6dcec98940f1`（`feature/review`）。兩邊工作區都有其他 session 還沒 commit 的改動。本機 BE 實際載入的是修正分支 worktree 的套件原始碼（`.venv` 裡的 `.pth` 指過去），本棒 13 支檔案我逐一比對過，兩份內容一樣。
> 驗證章：兩次都是 **verified**。只掃不修，兩個 repo 都沒改程式碼。

---

## 1. 一句話結論

**四個步驟的權限檢查形狀是對的，卡片擔心的 `is_admin` 放行也不成立；真正的問題出在「上傳的 Word 檔怎麼被打開」，以及資料庫那道第二防線寫錯了。**

- **工具報兩條，兩側各自掃到同一組**（面板 12 票全投，全數 3:0）：
  - **壓縮炸彈**（中）：Word 檔只量壓縮後的大小（20MB），解開後多大沒人管，一份小檔就能把伺服器記憶體吃光。這跟總表第 127 項（SSP 的 Word 匯入）是同一個病，只是換了一個入口；但病灶在**套件側**的共用讀檔器，第 127 項的修法修的是主專案那支，**這裡不會跟著好**。兩個修正分支上都還沒有任何一處檢查解開後的大小。
  - **比對規則卡死**（低）：解析器裡有三條比對規則，碰到特製的超長文字會卡住 CPU。我在本機實測，**4 萬個空白、不到 40KB 的標題就讓其中一條跑了 21 秒**，花的時間跟長度的平方成正比，放大幾倍就超過 120 秒逾時。
- **我自己查到、工具沒報的一條**（未經投票）：**這兩張「解析工作單」表的資料庫隔離規則，是 5 月就被判定「三處全壞」、SSP 那張已經修掉的舊寫法**。7 月新建這兩張表時照抄了壞掉的版本，一直沒人發現。DEV 實測：**子公司的帳號看得到母公司的匯入工作單**。現在靠程式那一關擋住，所以不是能直接打穿的洞，但第二道防線是壞的（第 6.1 節）。C4b 的 Excel 解析工作單表用的是同一條壞規則。

卡片點名的其他幾件逐一查過，都不成立：確認匯入時只檢查了甲、改的卻是乙；讀的路沒守；Word 公式注入。第 5 節逐條說明。

---

## 2. 這一棒在檢查什麼

「稽核計畫 Word 匯入」是讓稽核員上傳一份 Word 格式的稽核計畫，系統自動讀出行程、稽核方法、稽核單位，填進系統裡的稽核計畫。流程分四步：

1. **上傳＋解析**：稽核員上傳 Word 檔，系統當場解析，結果存成一張「解析工作單」。
2. **預覽**：畫面讀這張工作單，讓使用者看解析結果、刪改。
3. **捨棄**：不要了就丟掉這張工作單。
4. **確認匯入**：把預覽裡留下的行程寫進稽核計畫。

這一棒要回答三件事：

- 每一步是不是都有兩道檢查：一道是「你在這個專案裡是什麼角色」，一道是「這張解析工作單是不是你們公司的」？
- 解析工作單上的「屬於哪份計畫」是伺服器記的，還是使用者送的？確認時寫進去的東西，有沒有核對歸屬？
- 上傳的 Word 檔在解析時，有沒有被灌爆、被卡死、被夾帶公式的風險？

---

## 3. 掃到什麼：總覽

| # | 這是什麼問題 | 出事會怎樣 | 要先有什麼才打得到 | 該補檢查的位置 | 嚴重度 | 誰掃到的 | 跟總表的關係 |
|---|---|---|---|---|---|---|---|
| C4a-1 | 上傳的 Word 檔只量壓縮後大小，解開時整份塞進記憶體（壓縮炸彈） | 一份 20MB 以內的檔解開成幾十 GB，服務程序被系統砍掉；連送幾份，全部客戶一起停擺 | 某個專案的稽核員或管理者，稽核計畫還在規劃階段 | **套件側** `import_adapter/registry_base.py:23-24`（`parse_file` 打開檔案之前） | **中** | 套件側、主專案側**各自掃到** | **新入口**，與第 127 項同病同修法，但病灶在套件、第 127 項的修法修不到這裡 |
| C4a-2 | 解析器三條比對規則遇到超長文字會卡死 CPU | 一份幾十 KB 的檔卡住一個服務程序直到逾時；四份同時送，預設的四個程序全被占滿 | 同上，且文件要被判定成亞航格式（至少五個段落標題） | **套件側** `ap_report_parser/airasia_cmmc_l1_v1.py:57`、`:62`、`:107-113`（三條規則的定義處），以及 `:205` 起的 `_split_sections`（該加長度上限的地方） | **低**（面板評） | 套件側報一條、主專案側報另兩條，**我在本機實測過** | **新**，形狀與第 172 項（PDF 比對規則）相同 |
| C4a-3 | 兩張解析工作單表的資料庫隔離規則寫錯（5 月修掉的舊寫法又被抄了一次） | 子公司的帳號在資料庫層看得到母公司的工作單；使用者編號剛好等於某家公司編號時，也會看到那家的 | 現在要再配一條程式層漏掉的路才打得到，目前沒找到 | **主專案側** `scripts/sql/2026-07-02-ap-docx-parse-jobs.sql:40-42`（規則本體），以及出貨基線 `scripts/init/02-schema.sql:24943` | **低**（第二防線壞掉，第一防線還在） | **runner 自行核對，未經投票**；DEV 實測 | **新**。總表把這兩張表算成「有開隔離」，其實開了但規則是壞的 |

---

## 4. 工具報的兩條（經三人面板投票）

### 4.1 C4a-1 上傳一份「壓縮炸彈」Word 檔，就能讓整個產品停擺

**現況**：已修（M12-2，FR-114 CM-2181，commit BE `48638cbdb`／套件 `2cb9d2cd`，1.21.0 出貨）

**場景**

稽核員在稽核計畫頁按「匯入 Word」，選一份自己做的 .docx。這份檔不到 20MB，但裡面放正文的那一段（`word/document.xml`）是一大串重複內容，壓縮比可以到上千比一，解開後是幾十 GB。系統收到檔案，檢查「小於 20MB」後放行，接著打開檔案準備解析。打開時會把那一段整個解進記憶體，服務程序記憶體不夠就被作業系統砍掉。連送幾次，預設四個服務程序全倒，所有客戶都連不上。如果資料庫跟服務放在同一台機器，記憶體耗盡時資料庫也可能一起被砍。

**為什麼會這樣**

- 唯一的大小檢查在套件側 `ap_docx_import_app_service.py:40`（`_DOCX_MAX_SIZE = 20MB`）與 `:76-78`，量的是**上傳上來、壓縮著的**檔案大小。
- 真正打開檔案的是套件側 `import_adapter/registry_base.py:24` 的 `self._loader(file_path)`，Word 匯入時這個讀檔器是 `docx.Document`（`ap_report_parser/registry.py:26`）。這套程式庫會把 Word 裡每一段都完整解開放進記憶體，中間沒有任何一關問「解開後多大」。
- 全站 50MB 的上傳上限（`core/app_factory.py:104`）量的也是壓縮後的大小，擋不到。

**跟總表第 127 項的關係**

第 127 項是 SSP 的 Word 匯入（主專案 `app/oscal/service/ssp_docx_import_app_service.py`），病一模一樣。但本條的讀檔入口是**套件裡的 `RegistryBase`**，那是另一份程式碼。第 127 項的修法只要照總表說的寫在主專案那支服務裡，**這裡就不會跟著修好**。我查過兩個修正分支（主專案 `fix/security-b1` 與套件 `fix/security-b1` worktree），都還沒有任何地方用 `zipfile` 檢查解開後的大小。

**怎麼修**

在套件 `RegistryBase.parse_file` 呼叫讀檔器之前，先用壓縮檔工具讀出每一段解開後的大小、總大小、膨脹比、段數，超過上限就拒收。放在這一層，C4b 的 Excel 匯入也共用同一個 `RegistryBase`，可以一起受惠（C4b 會再確認）。建議跟第 124／127 項開成同一張卡、共用同一支檢查函式。

**嚴重度為什麼是中**：要先是某個專案的稽核員或管理者，而且稽核計畫要還在規劃階段；但一旦條件成立，打的是所有客戶共用的服務。

### 4.2 C4a-2 解析器三條比對規則，遇到特製的超長文字會卡住

**現況**：已修（M12-8，FR-114 CM-2181，同上）

**場景**

同樣是稽核員上傳 Word 檔。這份檔湊齊了亞航格式要的五個段落標題（依據、目的、稽核單位、稽核日期、稽核範圍），騙過格式判斷。接著在標題那一行塞一個「A」、後面接幾萬個空白、最後一個「B」。系統整理標題時，要檢查結尾是不是日期戳記，這條檢查會卡在那一大串空白裡來回試。不到 40KB 的文字就能卡 20 秒以上，放大幾倍就一路卡到 120 秒逾時、服務程序被砍。四份同時送，預設四個程序全占滿。

**是哪三條**

| 比對規則 | 定義位置（套件側） | 吃的是哪段文字 | 誰報的 |
|---|---|---|---|
| `_ORG_RE`（抓「由○○協助」） | `airasia_cmmc_l1_v1.py:57`，在 `:234` 使用 | 稽核單位段 | 套件側面板 3:0 |
| `_TITLE_DATE_STAMP_RE`（剝標題結尾的日期） | `:107-113`，在 `_titleize` 使用 | 標題（第一個非空段落） | 主專案側面板 3:0 |
| `_CLOCK_RE`（抓開始時間） | `:62`，在 `_parse_start_datetime` 使用 | 稽核日期段剩下的時段文字 | 同上 |

**本機實測**（只在本機跑那三條比對規則，沒有打任何網址）：

| 重複字元數 | 稽核單位規則 | 標題日期規則 | 時間規則 |
|---|---|---|---|
| 5,000 | 0.13 秒 | 1.27 秒 | 0.45 秒 |
| 10,000 | 0.51 秒 | 5.14 秒 | 1.82 秒 |
| 20,000 | 2.05 秒 | **20.69 秒** | 7.38 秒 |

長度加倍、時間變四倍，確認跟長度的平方成正比。標題那條在「20,000」這列用了 4 萬個空白，Word 壓縮後只剩幾 KB。解析器的段落文字沒有任何長度上限（`_split_sections` 從 `:205` 起直接把段落用換行接起來）。

**怎麼修**：段落文字進比對規則之前先截長度（標題、稽核單位、稽核日期這幾段，正常都不會超過幾百字）。三條規則本身也改寫成不會回頭重試的寫法，例如先把連續空白壓成一個、只看標題最後幾十個字、稽核單位規則改成逐行比對。

**嚴重度為什麼是低**（面板評）：條件跟 4.1 一樣，而且一次只卡一個程序、逾時就會被回收。但實測的量級跟總表第 172 項（PDF 那條，五頁 52 秒）差不多，首腦可以考慮比照第 172 項登記。

---

## 5. 卡片點名的問題，逐條回答（runner 自行開檔核對，未經三人面板投票）

### 5.1 `_require_job` 的 `is_admin` 放行的是平台管理員嗎？**是帳號層的「超級管理員」旗標，不是租戶管理員角色，不成立**

套件側 `ap_docx_import_app_service.py:251-258` 的 `_require_job`：工作單不存在或已停用就 404；工作單的公司跟你的公司不同，而且你不是 `is_admin`，也 404。

`is_admin` 從哪來：`jedi-iam/jedi_iam/middleware/context.py:130` 組使用者身分時寫的是 `is_admin=user.is_super_admin`，也就是帳號本身的「超級管理員」旗標。**不是**角色表上那個「管理員角色」旗標（`roles.is_admin`，每家公司的 System Manager 角色都有）。所以一般客戶的管理員拿不到這個放行。

有兩點要讓首腦知道：

- **旗標不限總部帳號**：DEV 有兩個帳號帶這個旗標，`admin`（總部，公司 1）與 `blsadmin`（**子公司 102**）。我找過，整個系統沒有任何網址或畫面能設這個旗標（`jedi-iam/api/serializers/user.py:43` 明寫不收），只能直接改資料庫。所以 `blsadmin` 應該是測試資料，不是誰都能給自己加上的。
- **就算拿到了也繞不過去**：子公司的超級管理員帶著別家公司的工作單編號來，在 `_require_job` 讀工作單那一步，資料庫隔離就先把那一列藏起來了（第 6.1 節那條壞規則，剛好在這個情況下是擋住的），拿到的是空值、回 404。後面還有一道「必須是那個專案的成員」。

### 5.2 四步是不是都有兩道檢查？**都有，形狀是對的**

守門次數 8 行對上公開方法 4 支，每一支都有：

| 步驟 | 公開方法（套件側起點） | 「是你們公司的」 | 「你在專案裡的角色」 | 階段限制 |
|---|---|---|---|---|
| 上傳＋解析 | `upload_and_parse`（`:66`） | 工作單由伺服器建立，公司編號取自登入身分 | `resolve_ap_and_check_auditor`：稽核員或管理者 | 只有規劃階段 |
| 預覽 | `get_parse_result`（`:144`） | `_require_job` | `resolve_ap_and_check_participant`：專案成員就行 | 不限 |
| 捨棄 | `discard_parse`（`:172`） | `_require_job` | 同上：專案成員就行 | 不限 |
| 確認匯入 | `confirm_import`（`:180`） | `_require_job` | `resolve_ap_and_check_auditor`（`set_tasks`、`add_ap_party` 裡面又各查一次） | 只有規劃階段 |

關鍵是**「這張工作單屬於哪份稽核計畫」是伺服器記的**：上傳時用網址上的 `ap_uid` 寫進工作單的 `source_uid`（`:80-86`），之後每一步都用 `job.source_uid` 重查角色，不收使用者另外送來的計畫編號。所以不會出現「用 A 計畫的權限去操作 B 計畫」的情況。

主專案側四條路由（`ap_docx_import_route.py:39-40`、`:63-64`、`:79-80`、`:100-101`）都掛了「要登入」＋「客戶有買稽核模組」，而且都把 `get_user_context()` 傳進服務，接縫沒有漏接。

**小觀察（不算漏洞）**：「捨棄」是寫入動作，守門卻用了讀取的標準，專案裡的「檢視者」「觀察者」也能丟掉稽核員還沒確認的工作單。影響只到同一個專案、而且只是軟刪一張暫存單，重新上傳就回來了。要不要收緊成跟上傳一樣（稽核員或管理者），交給首腦決定。

### 5.3 確認匯入時有沒有「檢查甲、動乙」？**計畫本身沒有，但寫進去的稽核人員編號沒核對歸屬（不構成資安問題）**

計畫本身不會：確認匯入動的計畫就是 `job.source_uid`，與守門查的是同一份。

但前端送上來的兩種編號，伺服器都直接照寫、沒問「是不是這份計畫的稽核人員」：`parties[].matched_party_uid`（`:206-208`）與 `tasks[].participants[].party_uuid`。它們一路寫進行程參與人員表（主專案 `app/flow_control/service/assessment_plan_app_service.py:620-625`）。

我追了寫進去之後會被怎麼用：讀回來時只回 `{party_uuid, role_id}` 兩個欄位（同檔 `:262-265`），**不會拿這個編號去撈人名、email**。複製到下一輪時也只是照抄（`:457-460`）。所以塞別家的編號只會留下一筆指向不明的連結，**讀不到別人的資料，也改不到別人的東西**。這屬於資料正確性問題，不列資安條目。一般的「設定行程」網址（不經過 Word 匯入）也是同樣的寫法，不是這條匯入路線特有。

### 5.4 讀取類「憑什麼給你看」：**預覽要求是專案成員，成立**

預覽會把解析結果、該計畫的稽核人員清單都回給前端。稽核人員清單本身在稽核計畫頁就是「專案成員都看得到」（`list_ap_parties` 的註解與行為都是這樣），沒有多給。沒有「清單所有工作單」這種方法，也就沒有「列出別人的工作單」這條路。

### 5.5 repo 方法有沒有「除了編號還限定範圍」：**只認編號，但每個呼叫點前面都已經驗過歸屬**

`ap_docx_parse_job_repo_impl.py:36` 的 `update` 只用內部 id 找、`:50` 的 `deactivate` 只用 uid 找，都沒有再加「屬於哪家公司」。但這兩支的每一個呼叫點，前面都已經過 `_require_job`，或者用的是剛剛才建立的工作單（上傳流程）。所以現在不構成「檢查甲、動乙」。將來如果有人新增呼叫點卻沒先驗歸屬，就會出事，而資料庫那一關又是壞的（第 6.1 節）。

### 5.6 Word 公式注入：**本棒範圍內不成立**

公式注入要成立，得先有「把這段文字寫進 Excel」的出口。Word 解析出來的文字寫進的是稽核計畫的行程標題與說明。我在主專案找過，稽核計畫沒有匯出 Excel 的功能（`assessment_plan_app_service.py` 全檔沒有 xlsx 相關程式）。這些文字如果之後流進別的匯出（例如稽核報告），屬於那一棒的範圍。前端的匯入元件沒有用 `v-html` 直接渲染內容。

---

## 6. 本棒另外查到的（runner 自行開檔核對，未經投票）

### 6.1 C4a-3 兩張解析工作單表的資料庫隔離規則是 5 月就被判定壞掉的寫法

**現況**：已修（M12-7，FR-114 CM-2195，BE commit `56dd4cdc8`；出貨基線另由 M11-41／CM-2215 補，1.21.0 出貨）

**場景**

一家集團客戶，總公司是 102，底下有子公司 152。總公司的稽核員在系統上傳了幾份稽核計畫 Word 檔，系統存成解析工作單。子公司 152 的任何一個帳號登入後，如果系統有任何一條路**漏掉程式層的公司檢查**，直接查這張表，就會看到總公司那幾份工作單的完整解析結果：稽核範圍、稽核依據、稽核單位名稱、行程時段。目前我沒找到這樣一條路（Word 匯入四步都有 `_require_job`），所以它不是現在就打得穿的洞。它的問題是：這張表的資料庫防線其實是壞的，總表卻把它記成「有開隔離」。

**規則長什麼樣**

```sql
-- scripts/sql/2026-07-02-ap-docx-parse-jobs.sql:40-42（出貨基線 02-schema.sql:24943 同一條）
USING (tenant_id::text = current_setting('app.user_id', true)
       OR current_setting('app.allowed_tenant_paths', true) LIKE '%' || tenant_id::text || '%')
```

這條規則錯在三處。**5 月 1 日已經有人把這三處寫成文字，並修好了 SSP 那張同名表**（`scripts/sql/2026-05-01-fix-ssp-docx-parse-jobs-rls.sql:4-8`）：

1. **拿「公司編號」去比「使用者編號」**：使用者編號剛好等於某家公司的編號時，就會看到那家公司的工作單。
2. **用「包含這串數字」比對公司路徑**：子公司的路徑 `/1/102/152/` 裡面含有 `102`，所以子公司看得到母公司的工作單；公司 2 也會被路徑 `/12/` 誤判成符合。
3. **沒有「超級管理員／系統作業」的放行條件**：平台管理員在資料庫層反而看不到子公司的工作單。這跟程式層 `_require_job` 放行 `is_admin` 的設計互相矛盾，只是不會造成外洩。

7 月 2 日建稽核計畫 Word 匯入這張表時，照抄了**修正前**的寫法。C4b 的 Excel 工作單表（`ar_xlsx_parse_jobs`）也是同一條。還有一件事：9 月 12 日的 CM-1664 migration（`scripts/sql/2026-09-12-cm1664-users-rls-self-visible-textcmp.sql:27-28`）還把這兩張表當成「既有同類規則的作法」來參考。

**DEV 實測**（2026-09-24 17:29，用受隔離的 `cm_app` 身分、唯讀交易，手動設 session 變數模擬各種登入身分，查完 ROLLBACK）：

| 模擬的身分 | 稽核計畫工作單（全部 13 筆都屬於公司 102） | Excel 工作單（4 筆） | SSP 工作單（已修好的規則，對照組） |
|---|---|---|---|
| 公司 102 的人 | 13 | 4 | 28 |
| **子公司 152 的人** | **13（應為 0）** | **4（應為 0）** | 0 |
| 兄弟公司 131 的人 | 0 | 0 | 0 |
| **公司 131 的人，但使用者編號剛好是 102** | **13（應為 0）** | **4（應為 0）** | 0 |
| 沒有公司路徑 | 0 | 0 | 0 |

我把 DEV 九家公司兩兩比對，壞規則讓每一家子公司都看得到**上面每一層母公司**的資料；正確規則只會給自己和底下的子公司。

**怎麼修**：照抄 5 月那支修正 migration 的寫法，改用 `app_tenant_allowed_for_session(tenant_id)`，並加上超級管理員的放行條件。兩張表一起改，出貨基線也要跟著重產。

**嚴重度為什麼是低**：程式層每一步都有公司檢查，目前沒有能直接利用的路。但它是「第二道防線以為有、其實沒有」：哪天程式層漏一處，資料就一路通到母公司。總表 927 行把這兩張表列為「有開隔離」，應該改掉。

---

## 7. 資料庫隔離現況（DEV 唯讀實查，2026-09-24 16:55／17:05／17:29，查完 ROLLBACK）

| 表 | Schema | DEV 隔離開了嗎 | 規則 | 規則對不對 |
|---|---|---|---|---|
| `ap_docx_parse_jobs`（稽核計畫 Word 工作單） | `oscal` | 開 | 1 條（ALL） | **錯**，見第 6.1 節 |
| `ar_xlsx_parse_jobs`（稽核結果 Excel 工作單，C4b 用） | `oscal` | 開 | 1 條（ALL） | **錯**，同一條 |
| 對照：`ssp_docx_parse_jobs`、`ssp_excel_parse_jobs`、`framework_parse_jobs` | `oscal` | 開 | 各 4 條 | 對，用標準判斷函式 |

確認匯入時實際寫入的稽核計畫本體與行程表（`oscal.assessment_plans`、`ap_tasks`、`ap_task_participants` 等）隔離全關，這點總表 927 行已經登記，本棒不重報。

---

## 8. 這份結果可信到什麼程度

**「工具報的兩條存在嗎」：可信度高。**兩次掃描共 4 個候選、12 票全數投出，4 條全部 3:0 成立，嚴重度都沒被調降。兩側的研究員彼此看不到對方，卻各自掃到壓縮炸彈，互相印證。比對規則那條我另外在本機實測過。

**「第 5、6 節」：runner 自行開檔核對，未經三人面板投票。**關鍵事實都實查過：

- `is_admin` 的來源：開 `jedi-iam` 原始碼確認。帶旗標的帳號：DEV 實查（17:28）。
- 壞掉的隔離規則：讀 migration 原文與 5 月修正檔，DEV 用 `cm_app` 模擬五種身分實測（17:29）。
- 行程參與人員編號寫進去之後怎麼用：逐一追過讀取與複製兩處。
- 兩個修正分支都沒有壓縮炸彈檢查：逐一 grep 過。

**「只有這些嗎」：不保證。**

1. 用的是最快的檔位（effort low），只跑研究員一輪加投票一輪，沒有威脅建模和廣度掃描。
2. 兩側研究員彼此看不到對方。跨側接縫（主專案四條路由 ↔ 套件四支服務方法，以及套件 ↔ 主專案 `AssessmentPlanAppService` 的四支守門與寫入方法）是我手動核對的。
3. 壓縮炸彈沒有實際做檔去打，比對規則只在本機單獨跑那三條規則，全程沒有打任何網址。

---

## 9. 執行概況（數字，給工程師看）

| 項目 | 套件側 | 主專案側 |
|---|---|---|
| 掃描範圍 | 13 檔／1,155 行 | 1 檔／116 行 |
| 基準 commit | `eafc7ae511d5`（dirty） | `6dcec98940f1`（dirty） |
| 檔位 | effort low | effort low，focus 生產程式碼 |
| 研究員 | 派 1 支，回 1 支 | 派 2 支（含密鑰專項），回 2 支 |
| 候選 | 2 條，去重後 2 條 | 2 條，去重後 2 條 |
| 投票 | 6 票全數投出 | 6 票全數投出 |
| 票型 | F1 3:0 中（壓縮炸彈）；F2 3:0 低（稽核單位規則） | F1 3:0 中（壓縮炸彈）；F2 3:0 低（標題日期規則＋時間規則） |
| 驗證章 | **verified**（`CLAUDE-SECURITY-REVISION-eafc7ae511d5-dirty.json`） | **verified**（`CLAUDE-SECURITY-REVISION-6dcec98940f1-dirty.json`） |
| 工具 run ID | `wf_158edbd4-e79` | `wf_8a9042c1-842` |
| 耗時 | 約 13 分鐘（7 個 agent，零失敗） | 約 22 分鐘（8 個 agent，零失敗；1 個回空結果，不影響候選數） |
| 工具原始報告 | 套件 repo `CLAUDE-SECURITY-20260924-085007/`（未入版控） | BE repo `CLAUDE-SECURITY-20260924-090417/`（未入版控） |

工具報告的 F 編號對到本報告：兩側 F1＝第 4.1 節；套件側 F2 與主專案側 F2 合併成第 4.2 節（三條規則）。密鑰專項沒有撿到任何東西。

---

## 10. 待首腦裁決

1. **C4a-1 壓縮炸彈要不要併進第 124／127 項那張卡**（建議要，而且要寫明：**病灶有三個入口、分在兩個 repo**。SSP Word／Excel 在主專案，稽核計畫 Word 與稽核結果 Excel 在套件的 `RegistryBase`。檢查要放在第一次打開檔案之前）。
2. **C4a-2 比對規則卡死要不要比照第 172 項登記**：面板評低，但實測量級與第 172 項相當。
3. **C4a-3 兩張解析工作單表的隔離規則要不要開修正卡**（建議要：照抄 5 月 SSP 那支修正 migration，兩張表一起修，出貨基線要重產）。總表 927 行「五張表開了隔離」那句，建議改成「三張正確、兩張規則壞掉」。
4. **（可選）「捨棄解析工作單」要不要收緊到稽核員或管理者**（第 5.2 節小觀察）：影響只到同一專案，屬產品規則。
5. **（可選）DEV 的 `blsadmin`（子公司 102）帶超級管理員旗標**：確認是不是刻意的測試資料。這個旗標在程式很多地方被當成「平台管理員」使用，只要出現在子公司帳號上，每個讀它的地方都要重新想一次（第 5.1 節）。
