# 資安檢視總報告 — 內化第三棒交接

> 第二棒做完 M01（遠端代理程式），決策已落成 Notion 卡 CM-1982。
> 這份取代 `_handoff-internalize-2.md`（那份留著當歷史，不要再照它開場）。

---

## 你的角色

陪決策者把這份資安檢視報告**讀懂、想清楚、做出判斷**。

他是這份報告的講者——要拿它去對客戶、對老闆、對稽核方解釋。所以他需要的不是你幫他寫完，是**你講給他聽，他提問，你回答，然後把他的判斷記下來**。

**你是輔佐他理解的技術人員**：幫他查問題、解惑、給建議。**不是替他做決定的人。**

工作目錄：`/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be`
報告原始碼在 `docs/security-report/`（md 是源，html 是產物）。

---

## 先讀這些

1. `docs/security-report/README.md` — 總報告首頁，一張表列完 23 個檢視項目
2. `docs/security-report/GUIDE-01-method-and-tools.md` — 用什麼方法查、結果有多可信
3. `docs/security-report/M08-integrity.md` — **下一塊要講的就是這塊**
4. `docs/security-report/_module-page-spec.md` — 模組頁撰寫規格（了解格式即可）

---

## 這份報告是什麼

PM 的要求原話：既有的技術報告「都放小弟原生產出的文件，所有過程皆需保存」，另外開一個連結放「**你內化過後的版本**」。

| | 需求中心的 21 個站 | 這份 |
|---|---|---|
| 誰的產出 | 檢視工具直接吐出的技術報告 | **決策者消化過、認可的版本** |
| 誰負責 | 過程紀錄 | **決策者**——他要能站在前面講 |
| 讀者 | 要查細節的工程師 | 客戶、老闆、稽核方 |

**它會被當成稽核證據**——客戶要看的是「你有做事，不能唬弄」，所以每頁都有「檢視軌跡」段（日期、範圍、每輪票數、原始報告檔名），證明確實查過。

23 塊報告都寫好了。**還沒做的是「內化」——也就是決策者的判斷。**

---

## 進度

| 塊 | 狀態 |
|---|---|
| M02 檔案上傳下載 | ✅ 過完，決策落在 **CM-1981** |
| M01 遠端代理程式 | ✅ **過完，決策落在 CM-1982** |
| M08 防竄改 | ⬜ **下一塊** |
| M03 弱點檢測 | ⬜（檢視進行中，數字會變） |
| 其餘 19 塊 | ⬜ |

**目標是 23 塊全部過完**（決策者 2026-09-20 明示），一棒接一棒做到完。

---

## 🔴 決策者的工作偏好（前兩棒踩過，務必照做）

### 回答要短

他明講過：「**之前都太長了，雖然很詳細但很容易失焦**」。

- 先給結論，再給依據
- 一次回答一個問題，不要把相關的全倒出來
- 表格優於長段落
- **他要的是能上台講的一句話，不是完整的技術分析**

### 不要自作主張往下跑

他會說「再來第 5 條」「回去看 M01」——**照他說的走**。
不要在他問 A 的時候順便把 B、C 也講完。**講完一條就停，等他發問。**

### 事實一定要回程式碼查，不要只信文件

**這是最重要的一條。** 前兩棒在兩塊裡就抓到近十條文件錯誤。

第一棒（M02）三條：

| 報告寫的 | 實際 |
|---|---|
| 上傳沒有大小上限 | 有，50MB，而且進來就擋 |
| 儲存連線「沒填就走明文」 | 七筆設定全填了、全填成不加密 |
| 「客戶共用儲存是不是刻意設計」是決策題 | 不是設計，是修 bug 時的過渡措施，commit 寫得清清楚楚 |

第二棒（M01）更多，而且**其中一條是查實機才發現的**：

| 報告寫的 | 實際 |
|---|---|
| nginx 設定檔少了驗憑證那一行 | **雙向憑證驗證從未實作**，不是漏填 |
| （沒寫） | agent 端憑證**有效到 2036 年**（登入 190 實機確認）——這直接鬆動了「改了會讓 agent 全掉線」的顧慮 |
| 「測試連線」讓管理員填網址 | **沒有那個表單**，是後端入口收任意網址 |
| 模組的設定檔進版控 | 是 `jedi-common/.env`，**與遠端代理程式無關**，而且主機是 `localhost`、沒有程式在讀 |
| 通行碼永不過期（暗示是 API token） | 它是**裝機啟用碼**，用一次換憑證，之後不再出現 |

**三個查證手段，按有效性排序**：

1. **查 commit message**——常常直接回答「當初為什麼這樣做」。M01 第 3 條靠這招查出 2026-06 的設計取捨（`git log` + `docs/features/FR-039-.../design-agent-auth.md`）
2. **登入實機看**——188/189/190 三台。M01 最有價值的那條證據就是在 190 上 `docker run --rm -v <volume>:/c alpine` 把憑證撈出來看到期日
3. **開檔看程式**——不要憑報告或印象

**而且前兩棒自己都講錯過**：第一棒說「歸屬欄位已經在表裡」（實際沒有）；第二棒一度說「agent 有遞憑證但我們沒看」（實際是我們沒開口要，所以根本沒送）。**講之前先查，講錯了立刻更正。**

### 三個 repo + 實機 + 資料庫

- 主專案：工作目錄
- 套件：`/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/`
- 前端：`/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-fe`
- **agent**：`/Users/chouraymond/Projects/Billows/Audit-Manager/evidence-agent`（第二棒才用到，M01 之後仍可能用）
- 實機：`ssh jedi@192.168.50.189`（POC）、`ssh jedi@192.168.50.190`（E2E，**有真 agent 在跑**）
- 資料庫：本機 `localhost:5432` / `guidant_ai_dev` / `.env` 裡的 `cmmgr`，**只能 SELECT**

🔴 **實機只能唯讀**（CLAUDE.md 環境異動鐵律）：`docker ps`、`docker logs`、看設定檔、`SELECT` 都可以；**改任何東西都不行**。

### 白話

讀者是 PM 與老闆。「白話」不是把句子講得口語，是**讀者完全不需要技術背景就能懂**。
規格第四節有禁用詞表，改完 grep 一次。

**禁止寫**「已確認安全」「沒有漏洞」「全面檢視」——我們的方法不能宣稱這個，只能說「這一輪在這個範圍內沒有找到問題」。

### 不要自己改結論

可以說「這條我覺得評太輕／太重」並說理由，**但改不改由決策者決定**。
他的判斷會受他知道而你不知道的事影響（哪些客戶在用、何時出貨、哪個功能還沒上線）。

**M01 有個實例**：第 3 條我建議「加期限」，決策者反駁「GitHub 的 API token 也不過期，交由使用者保管就好」——他是對的，因為他知道客戶的裝機流程長什麼樣。**我當場收回建議、改成列三個選項給他選。**

---

## 派工紀律

微量查證與改稿用 subagent 即可，不必開 Notion 卡（決策者視情況決定，太大才開）。

- **一定帶 `model` 參數**，不要繼承。機械性查證用 `sonnet`，要組織與取捨用 `opus`
- prompt 第一行寫「model：X／effort：Y／理由一句」
- **只讀不改**的查證要在 prompt 裡寫死，並註明「DB 只能 SELECT、實機只能唯讀」
- 回報只要結論，不要貼文件內容回來
- 顯式 `git add <檔名>`，禁用 `-am`
- commit 可自己做，**push 一律等決策者明確指示**。不要切 branch

🔴 **subagent 的回報要自己核對過再講給決策者聽。** M01 派出去查 nginx 的那支，回報時把「agent 那頭的 mTLS」跟「我們這頭的 mTLS」當成同一道門，結論整個反了。是自己開檔追了 agent 的出站呼叫才發現方向相反。**subagent 挖到的線索有價值，但它的結論要驗。**

改完 md 要重 build：
```
python scripts/deliverables/render_index.py docs/security-report/ --site-root docs/security-report/
```

---

## 與另一條線的分工

還有一個 session 是**文件產出線的首腦**，負責格式、事實驗證、build、以及弱點檢測那塊檢視收完後的更新。

**分界：它動格式與事實，你動內容與結論。** 那 23 份 md 在內化期間以你為主，它不會主動去動；它要進去改會先跟決策者說。

**所以你不要自己改那些 md**——把要改的整理成 Notion 卡，決策者會轉給文件首腦派工。M02 是 CM-1981，M01 是 CM-1982。

---

## 前兩棒的產出：CM-1981 / CM-1982

**CM-1981** [資安總報告] M02 檔案上傳下載 — 內化修訂
https://app.notion.com/p/M02-3-7-3e1346da4cd081ad9e86d96316c0882a

**CM-1982** [資安總報告] M01 遠端代理程式 — 內化修訂
https://app.notion.com/p/M01-5-1-6-3e1346da4cd081dfa32efae0ddd20d69

**這兩張是後面每一塊的格式範本**——每塊過完就照這個形狀開一張。CM-1982 的節次可以直接抄：逐條更正（E 編號）／新發現／講者決策（D 編號）／風險等級對齊／施工紀律／待決清單／跨模組觀察。

**CM-1982 的十個節有個好處**：把「事實更正」跟「講者決策」分開編號（E1-E10 vs D1-D5），文件首腦一眼就知道哪些是「改錯字」、哪些是「照決策者的判斷改寫」。建議後面沿用。

---

## 🔴 決策者已拍板的原則（會跨模組適用，遇到同類問題直接援引）

### 一、密碼類欄位「預設不回傳」（標記制，不是名單制）

設定類的密碼欄位一律不回傳給前端，只有程式內部存取時取得真值。

**理由**：差別在漏掉的時候往哪邊倒。名單制漏登記＝密碼外洩；標記制漏標記＝頂多前端少看到一個欄位，前端會來反應。

**遇到其他模組的密碼外洩問題（M22 系統設定、M16 寄信、M09 日誌），直接援引這條。**

### 二、權限檢查走「一條路由 + 登記」，不為每個情境各開路由

- 檔案模組留一個空位，說「我需要一個能回答『這人能不能拿這個檔』的判斷函式」
- 各業務模組把自己的實作登記進來
- 檔案模組查出這個檔屬於誰之後，呼叫對應的那一個
- **規則各自寫 ≠ 路由各自開**

三條要定死的規則：查不到登記的類型→拒絕；沒有模組認領的→拒絕；拒絕時回「找不到這個檔案」而不是「你沒有權限」。

**「只驗登入、不檢查歸屬」那一整組（橫跨七個模組十幾條）都適用這個形狀。**

### 三、所有修法都要先判斷「功能不能壞」

他明講：「**一切的修復方式都要先判斷過功能不能壞**」。

講修法時要一併講：這樣改會不會壞掉、哪一步有風險、安全的施工順序是什麼。

**M01 的實例**：「補上驗證會讓所有 agent 掉線」是這條最大的絆腳石，而查實機（憑證有效到 2036）直接化解了它。**遇到「改了會不會壞」的顧慮，先去查事實，不要停在假設。**

### 四、「帶出公司」這類論述要驗證再寫

他推翻了報告的一條加重理由：「稽核證據可以被合法帶出公司」。

**理由**：一個人只要下載得到檔案，本來就能轉寄、存檔、拍照，任何系統都擋不住。留著這句反而給人挑毛病的把柄。

**後面看到類似的加重論述，先檢查它是不是「所有系統都有的事」。**

### 五、🆕 不要相信呼叫方自報的東西，自己去查（M01 第 2 條定）

那個「測試連線」入口原本收網址、照著打。決策者裁定改成**收編號，後端自己去資料庫查出網址**。

**他的原話意思**：用編號去查再打，前端就不能介入了。

**為什麼比「檢查」好**：檢查會寫漏寫錯（例如只比對開頭，`https://好的網址@壞的網址` 就騙過去了）；不收就沒得騙。**這是消除選擇，不是增加檢查。**

**這條與原則二是同一個家族**，收尾時可以合併講：不要相信自報的身分、不要相信自報的網址。

---

## 🔴 浮現中的問題分組（收尾要用，每塊過完就歸類）

最後的「分析與結論」要把 141 條收成幾個病。目前累積到三組 + 一條新觀察：

| 組 | 目前成員 | 一句話 |
|---|---|---|
| **A. 只驗登入、不檢查歸屬** | M02 第 1、2、3、7 條；報告說橫跨七個模組十幾條 | **最大的一組**。修法＝原則二 |
| **B. 鑰匙掛在牆上** | M02 第 5 條（儲存帳密）、**M01 第 4 條（`jedi-common/.env`）、第 5 條（公開網站的資料庫密碼）、第 6 條（GitLab 權杖）**、M22、M16、M09 | 拿到憑證就繞過所有檢查。**M01 三條的共同根因是「出貨流程沒有自動檢查密碼外洩」**，收尾可併成一條建議 |
| **C. 寫入有把關、讀取沒有** | M02 第 5 條、M05 問卷（6 寫入全有、8 讀取全無） | A 是沒想到要檢查，**C 是想到了、只做了一半** |
| **🆕 C 的擴大版：「想到了，只做了一半」** | **M01 第 1 條**（想到要驗身分，只寫進註解沒實作）、**M01 撤銷不完整**（三條管道只補了一條）、**M01 第 2 條**（同檔其他功能都有守門，只有這支沒有）、M02 第 5 條、M05 問卷 | **建議把 C 擴寫成這個更大的樣態**——不是沒想到，是做了一部分就停了。這組的成員數可能超過 A |

**🆕 另一條值得收尾時講的跨模組事實**（M01 挖到）：

> 同一套憑證機制，**agent 那頭做滿三層**（驗憑證、驗通行證簽章、驗綁定指紋），**我們這頭一層都沒有**。
>
> 這說明問題不是「不會做」，是**兩端由不同人在不同時間做，沒有人負責檢查兩端有沒有接上**。對修法可行性是正面訊息——有現成的參考實作。

**每塊講完，把該塊的問題歸進這些組，或開新組。**

---

## 待決清單（累積中，決策者還沒定）

### M02 留下的

| # | 待決 | 需要誰 |
|---|---|---|
| 1 | 第 5 條在全案的優先順序 | 講者 |
| 2 | 第 2 條要不要降級（加重理由已判定不成立） | 講者 |
| 3 | 「系統共用」檔案的定義與授權規則（目前 14 筆，跨客戶全開） | 講者 + PM |
| 4 | 第 10 條數量上限訂多少 | 講者 + PM |
| 5 | 換發儲存帳密的時機 | 講者 + 運維 |

### M01 留下的

| # | 待決 | 需要誰 |
|---|---|---|
| 6 | 報到通行碼三個選項選哪個（可觀測／後台審核／一機一碼） | 講者 + PM |
| 7 | 第 2、4 條的風險等級以報告頁還是 Notion 卡為準 | 講者 |
| 8 | Zero Trust 是否已生效、是否涵蓋文件站（影響第 5 條狀態改寫） | 講者 |
| 9 | 公開網站那組資料庫密碼要不要換、何時換 | 講者 + 運維 |
| 10 | 出貨流程加自動密碼檢查排在什麼時候（第 4、5 條同根因） | 講者 + PM |

**這些不要替他決定，也不要寫進文件當結論。**

---

## 最後的收尾（23 塊過完才做）

- **一頁摘要**（首頁最上面那段，給只看一頁的人）
- **問題總表**（141 條按嚴重度重排，現版在 `docs/features/security-scan-consolidated/risk-overview.md`）
- **分析與結論**（上面那幾組病因，把模式講清楚）
- **修正建議**（只列優先順序，**不寫日期**——時程是決策者跟 PM 排的）

另有兩項業界標準補強沒做（不急）：報告封面基本資料、風險評級標準說明。

---

## 下一塊：M08 防竄改檢查

**為什麼排這塊**：交接檔原本的順序是 M01 → M08 → M03，索引表最上面四塊（M01/M02/M08/M03）是最需要決策者講得出來的。M01、M02 已過完。

**這塊的一句話**（索引表寫的）：🔴 **已實測證實**：換掉其中一個零件，整套防竄改永久失效且不發警示。

**跟前兩塊的對照**（可以拿來開場）：
- M02 是「有帳號的內部人越權」
- M01 是「完全沒帳號的外人」
- **M08 是「拿到程式碼的人動手腳」**——三塊講完，決策者手上就有三種攻擊者的完整光譜

**開講前建議先查證的幾點**（前兩棒的教訓）：
- 「已實測證實」是什麼時候測的？現在還是這樣嗎？
- 「永久失效」的機制是什麼？查程式碼，不要只看報告敘述
- 有沒有對應的 Notion 工單？狀態如何？（M01 那四張查出來全是 Not started）
- 這塊的檢視只有 2 輪（比 M01 的 5 輪少），涵蓋面的但書要講清楚

**M03（弱點檢測）的提醒**：那塊的檢視還在進行中，頁面已標「截至 2026-09-20」，數字之後會變。講的時候要註明這點，決策者上台時也要知道。

---

## 開場怎麼做

1. 讀完上面四份文件
2. 用三五句話說你理解的「這份報告要解決什麼問題」
3. 問決策者任何不清楚的
4. **然後開始講 M08**

**不要一次講完，一塊一塊來，他要跟得上。**
