# Runner Session 交接文件（2026-07-25，資安掃描 arc 第三棒 runner）

> **給下一棒 runner 的冷接文件。** 本文件自包含，看完即可開工，不需回頭翻對話。
>
> 上一棒：`2026-07-25-security-arc-final-session-handoff.md`（同目錄，資安 arc 第二棒收官）

---

## 🧭 我是誰、這棒在做什麼

**角色定位：任務執行 runner。** user 指派 Notion case 編號 → 我照 `notion-case-work` skill 流程做掉 → 回填 Notion 標「修正待驗證」+ 白話補充。

**不是** arc 主導、不是分析 session。user 說「執行 CM-xxx」我才動，做完停下等下一個指令。

本棒處理 6 條 case（CM-886~889 / 893 / 新開 894）+ 1 條 user 臨時指派的 UI 一致性修正。

---

## §0 開工前必讀（硬 gate）

**接手後不要自動開始做任何事。** 等 user 指派 case 編號。

若 user 直接說「繼續」而沒給編號 → 回問要處理哪一條，不要自己從 Notion 挑。

### 冷接自檢題（答不出來先讀完本文件）

1. 這棒的 commit 已經 push 了嗎？→ **已 push**（user 於 2026-07-25 自行 push，兩 repo 皆同步）
2. BE 需要重啟嗎？→ **需要**，且**我沒有重啟**（見 §5 風險項）
3. 為什麼 BE git log 有些 commit 不是我做的？→ **有另一個 session 平行在跑同一個資安 arc**（見 §3 ⚠️）
4. CM-888 的修正驗證到什麼程度？→ **只驗語法，GitLab 實際行為未跑**（見 §5）

---

## §1 本棒完成的 case（全部已 push）

| Case | 範圍 | 狀態 | commit |
|---|---|---|---|
| CM-886 | FE | 修正待驗證 | `c8aa7bc` `480090d` `c634f32` `9c59c8b` |
| CM-887 | FE | 修正待驗證 | `7b2cd96` |
| CM-888 | BE | 修正待驗證 | `ecffff39` |
| CM-889 | BE+FE | 修正待驗證 | `f972d92d` / `a6a895b` |
| CM-893 | BE | 修正待驗證 | `456f1a1d` + 文件 `747bd35c` |
| CM-894 | — | **Not started（新開，未動工）** | — |
| （無 case）| FE | 已 commit | `40e8d4c` |

Notion 六條都已回填白話補充。CM-894 是本棒**開的新 case、沒做**，內容已寫到可直接動工的程度。

### 各 case 一句話

- **CM-886**：Sonar a11y 49 條（input label 37 + iframe title 12），29 個 vue 檔全清
- **CM-887**：BaseService.js 五方法收斂到 `settleEnvelope` helper，消 S3403/S4822
- **CM-888**：**唯一會動資料的修正**——GitLab 附件刪除從「全刪」改成「只刪指定檔案」
- **CM-889**：semgrep WARNING 33 條加 nosemgrep 註記，BE/FE 雙邊歸零
- **CM-893**：摘要報告 PDF 拔 Google Fonts 外連，字型改 `Noto Sans CJK TC`
- **`40e8d4c`**：`/project/task-manage` 狀態篩選 CSS 對齊全站慣例（user 臨時指派，無 case）

---

## §2 三個「比 case 描述更嚴重 / 不同」的發現

這三項是本棒實際查證後推翻或補強了 case 原本的假設，**下一棒若碰到相關區域要知道**：

### 2-1 CM-888 是真 bug，且原作者的「錯」有原因

`delete_gitlab_feedback_attachment` 收 `file_uids` 卻立刻用「該 issue 全部附件」覆寫 → 刪一個附件會全刪 GitLab 端。

**根因不是隨手寫錯**：GitLab 附件 uid 是 note body 裡 `/uploads/<hex>/` 的雜湊，local 附件 uid 是 DB uuid，**兩者不同 id 空間**，拿 local uid 去比對交集恆空、什麼都刪不掉。推測原作者因此改成全刪繞過。

**修法**：改用**檔名**比對定位（同名檔案依出現順序 1:1 配對），對不到就回 False 不再退化成全刪。caller 配合改為「先查檔名再刪 local」——**順序不可對調**，local mapping 刪掉後就查不到檔名了。

### 2-2 CM-893 揭露的問題比 case 假設的更實際

case 假設是「內網抓不到只剩等待成本」。實查發現：**部署環境（Debian + `fonts-noto-cjk`）根本沒有 `Noto Sans TC` 這個家族名**——那是 Google Fonts 的名稱，容器內 `fc-match 'Noto Sans TC'` 掉到 DejaVu Sans（無中文字符）。

中文不會變豆腐字（WeasyPrint 有字符級 fallback），但實測會撿到 **`Noto Sans CJK JP`**，也就是**用日文漢字字形排繁中報告**。故第一順位改指定 `Noto Sans CJK TC`。

**已確認 DEV 主機字型 OK**（user 實跑 `fc-match` 回 `NotoSansCJK-Regular.ttc: "Noto Sans CJK TC"`）。

### 2-3 nosemgrep 註記格式有坑（下次會再踩）

`nosemgrep:` **必須在 finding 行的行尾、或緊鄰的前一行**。寫成「nosemgrep 獨立一行 + 中文說明換行」會把 nosemgrep 推離錨點，**BE 剩 13 條、FE 剩 1 條沒消掉**。

正確格式：說明放更前一行，`# nosemgrep: <rule>` 放行尾。多行語句（`logger.warning(` / `make_response(`）錨在**起始行**。

---

## §3 環境與協作狀態

### Git（兩 repo 皆已 push、working tree 乾淨）

```
BE  ~/Projects/Billows/Audit-Manager/compliance-manager-be   main  已同步
FE  ~/Projects/Billows/Audit-Manager/compliance-manager-fe   main  已同步
```

BE 有若干 untracked（`docs/交付文件/`、`security-audit-report-2026-07-17.html` 等）——**本 session 開始前就存在，不是本棒產物**，不要順手清。

### ⚠️ 有另一個 session 平行在跑同一個 arc

BE git log 裡這些**不是本棒做的**，是平行 session 的產物：

| commit | 時間 | 內容 |
|---|---|---|
| `9de55a8e` | 12:23 | semgrep avoid-sqlalchemy-text ERROR 歸零 |
| `1ac436a5` | 12:41 | SonarQube hotspots 逐條審查 |
| `d245cb72` | 12:46 | SonarQube Quality Gate new-code 策略 |
| `e52ea26b` | 12:51 | weasyprint 升 69.0（CM-892 CVE） |
| `e81c8c05` | 13:09 | 摘要報告 img src 限縮 data: URI（CM-892） |

時間戳與本棒的 `ecffff39`(12:18) / `f972d92d`(12:58) / `456f1a1d`(13:10) **交錯**。

**對下一棒的影響**：
- Notion 同一個 DB 可能有另一棒正在寫 → **開新 case 前務必先搜尋**，避免撞號或重複開案
- `api/project_summary_report/` 這個目錄兩棒都動過（本棒動 template 字型、平行棒動 route 的 bleach 白名單）→ 動之前先 `git log` 看最新狀態
- 不要假設 git log 上的 commit 都是自己人做的

### BE 服務狀態

本棒**沒有啟動也沒有重啟 BE**。session 中觀察到 PID 33978 在跑（port 8000，啟動時間 13:05），**是 user 先前自己起的**，跑的是舊 code。

CLAUDE.md 記載 2026-07-22 起 BE 重啟由 Claude 負責，但因該 process 非我所起、user 可能正在使用，我問過後 user 未回覆是否重啟 → **維持未重啟**。

---

## §5 風險與未驗證項（最重要，優先讀）

### 🔴 CM-888 的 GitLab 行為未實跑驗證

**這是本棒唯一「改了會動資料、但只驗語法」的項目。**

- 已驗：Python 語法檢查通過、call site 全數更新（grep 確認無殘留舊簽名）
- **未驗**：實際刪 GitLab 附件的行為——需要有掛 GitLab 的回饋資料才測得到
- 若 GitLab 設定當前沒開（`_is_gitlab_enable()` 為 false），這條路徑根本不會執行

複測步驟已寫在 Notion CM-888 頁。**下一棒若 user 回報「刪附件有問題」，優先看這裡。**

### 🟡 BE 需重啟才生效

CM-888 改的是 service 層，CM-893 改的是 template（理論上隨請求讀取、不需重啟，但既然同輪有 service 改動，一起重啟最保險）。

**目前跑的 BE 是舊 code。**

### 🟡 STG / POC 主機字型未確認

CM-893 拔掉 Google Fonts 外連後，**「主機有中文字型」從隱藏的保險變成明確前提**。

- DEV `auditmanager-dev-01`：✅ 已確認（`Noto Sans CJK TC` 正常解析）
- STG / POC：**未確認**

上線前各跑一次：
```bash
fc-match 'Noto Sans CJK TC'   # 要回 NotoSansCJK，不是 DejaVu
```

**若某台沒裝，這個 commit 在那台會讓中文從 PDF 整段消失**（實測：完全無中文字型時中文不是豆腐字，是不見）。

### 🟡 DEV 主機有既有的 python3.11 套件衝突

`auditmanager-dev-01` 跑任何 `apt-get install` 都會報 `libpython3.11-stdlib` 版本不一致。**與本棒工作無關**（字型套件本身已是最新版）。

⚠️ **不要跑 `apt --fix-broken install`**——會動系統 python，該機正在跑 BE。要處理需另案評估。

已寫進 `docs/claude/host-dependencies.md`。

---

## §6 本棒新增的文件

### `docs/claude/host-dependencies.md`（新檔，commit `747bd35c`）

主機層依賴（部署前提）——記錄「`poetry install` 裝不到、但缺了功能會壞」的 OS 層套件：

- 中文字型 `fonts-noto-cjk`（摘要報告 PDF）
- LibreOffice（SSP 匯出 docx→pdf/odt）
- 新機一行檢查指令 + 各主機確認狀況對照表
- DEV 主機的 apt 陷阱警語

CLAUDE.md 的 Environment Variables 段已加 pointer + `LIBREOFFICE_CMD` 選用變數。

**下一棒若動到 PDF / 匯出相關，先讀這份。** CM-894 完成後需回填多語系字型段。

---

## §7 下一棒可能接到的工作

### 已備好、可直接動工

**CM-894**（本棒新開，Not started）：PDF 匯出多語系字型缺漏。

- 高棉文 408 字全豆腐、阿姆哈拉/僧伽羅內容被畫成同一個高棉字符（字碼已失真）
- 緬甸文落到非標準的 Zawgyi 字型
- 阿拉伯/希伯來 RTL 字序、婆羅米系 shaping **標為待人眼複核**（pdfplumber 取的是內部儲存順序，不必然等於視覺順序——不要當成確定的 bug）
- **動工前必須先問 user/PM「產品實際要支援哪些語系」** ——這決定工作量是「加一行註記關案」還是「裝全套 Noto + 處理 bidi/shaping」
- ⚠️ 診斷資料產自 macOS 本機，**部署主機（Debian）表現會不同且可能更差**，需在部署環境重測才能定案

case 內已含完整診斷指令（可直接重跑）與逐語系實測對照表。

### 待 user 裁決

- 是否重啟 BE（見 §3）
- CM-894 的語系範圍（見上）
- DEV 主機 python3.11 套件衝突是否另開 case 處理

---

## §8 工作慣例備忘（本棒實際遵守的）

- **commit 可自行做，push 永遠等 user 明示**（本棒全程未自行 push，user 於收尾時自行 push）
- **顯式 `git add` 逐檔列名，禁用 `-A` / `-am`**
- **不切 branch**，一律在 user 當下的 branch 工作
- **收尾動作等 user 下令**——fix 完只給一句話 status + 手測 checklist
- Notion 作業人員填 `小弟`（Claude 做的），來源 `雷門`，專案 `CM`
- 掃描輸出丟 `/private/tmp`，**不寫入 `docs/security-reports/` 日期資料夾**
- 暫存腳本用完即刪，不在版控檔留憑證

### 本棒的驗證紀律（建議延續）

- FE 每批改動都跑 `npm run build:DEV`（指令是 `build:DEV` 不是 `build`）
- 大量機械性修改後**逐檔比對來源清單**確認零遺漏（CM-886 用此法確認 29 檔全涵蓋）
- 只加註解的改動用 `git diff` 逐行比對，確認每個刪除行都以「原內容+註記」形式回來（CM-889 用此法確認零邏輯異動）
- 引用 i18n key 前先 grep 確認存在（本棒有兩個想用的 key 實際不存在，先驗才沒出包）
- 跨環境問題在**部署環境模擬容器**驗，不能只驗 macOS（CM-893 用 docker python:3.9-slim + fonts-noto-cjk）

---

## §9 給下一棒的 prompt

見同目錄 `2026-07-25-runner-session-prompt.md`，或直接用本文件開頭那段。
