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

> 第三棒過完 M08、M22、M09、M06，並一次結清了累積十四項的待決清單。
> 這份取代 `_handoff-internalize-3.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/_module-page-spec.md` — 模組頁撰寫規格（了解格式即可）


---

## 這份報告是什麼

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

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

**它會被當成稽核證據**——所以每頁都有「檢視軌跡」段（日期、範圍、每輪票數、原始報告檔名），證明確實查過。

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

---

## 🔴 第三棒挖到的、會改變全案定位的事實

### 產品從未出貨給任何真實客戶

決策者 2026-09-20 告知：**這一輪是出貨前的最後檢查，不是已上線系統的事後稽核。**

這件事翻掉多處論述，**接手後每一塊都要用它重新檢視**：

| 作廢的論述 | 為什麼 |
|---|---|
| 「密碼可能已外流，修完要換發」 | 憑證從沒離開過我們 |
| 「已有 N 家客戶暴露在風險中」 | 那些都是開發環境測試資料 |
| 「改了會讓現有客戶掉線」 | 沒有客戶在跑 |
| 「要小心施工順序不能弄壞現有客戶」 | 多數只剩「不能弄壞打包流程」這一層 |

🔴 **但有一類不能套用**：**我們自己的基礎設施**（公開網站的資料庫密碼、GitLab 權杖、`jedi-common/.env`）與產品出不出貨無關，**可能真的曾經曝光過**。判準是「這東西是隨產品出給客戶的，還是我們自己在用的」。

**已寫進首頁**（CM-1988 做完）。後續每塊都要記得用這個前提檢視。

---

## 進度

| 塊 | 狀態 | 決策落在 |
|---|---|---|
| M02 檔案上傳下載 | ✅ 過完 | CM-1981 |
| M01 遠端代理程式 | ✅ 過完（第三棒追加兩項裁定） | CM-1982 |
| M08 防竄改 | ✅ 過完 | CM-1984 |
| M22 系統設定 | ✅ 過完 | CM-1985 |
| M09 系統日誌 | ✅ 過完 | CM-1987 |
| M06 稽核流程 | ✅ 過完（B 組經實測查證，結論與原報告不同） | CM-1989 |
| **M03 弱點檢測** | ⬜ **決策者指示先跳過**（檢視還在跑，數字會變） | |
| **其餘 16 塊** | ⬜ **下一步** | |

**目標是 23 塊全部過完**，一棒接一棒做到完。

---

## 🔴 M06 留下的一個教訓：首腦自己判斷錯過一次

第三棒在 M06 第 3 條上**初判錯誤，被實測推翻**。這件事值得下一棒記住。

**經過**：決策者質疑報告寫的「惡意流程圖卡死伺服器」——「我們是整份存下來、只有啟動時才算，這會造成你說的錯誤嗎？」首腦去開檔，查到主系統 `_assert_all_reachable()` 走訪時**有 visited 集合**，於是回報「報告建議的修法是在建議一個已經存在的東西，這條可能不成立」。

**錯在哪**：那支是**存檔前的可達性檢查**，不是引擎執行期走的那條路。**真正會爆炸的是套件層另一組互相遞迴、沒有 visited 集合的函式**。首腦看錯函式就下了結論。

**派 subagent 實測後**：第 3 條**仍然成立**（10 層閘道 2.16 秒、11 層 8.76 秒、12 層算不完），而且比報告寫的更容易觸發；反倒是第 4 條的機制描述是錯的（那支端點實測 1000 節點只要 0.027 秒）。

**教訓**：
- **「我查到有防護」不等於「這條路徑有防護」**——要確認查到的那支是不是使用者實際會走到的那一支，從 API 端點一路追下來。
- 決策者的質疑方向是對的（他問的是「什麼環節會觸發」），但**首腦給的答案是錯的**。質疑對、答案錯，兩件事。
- 這種「推翻報告」的判斷**一定要派實測驗過再講**，不要憑一次 grep 就回報。

---

## M06 的結論（已開卡 CM-1989，這裡留摘要供收尾用）

**這塊最特別的發現**（收尾要用）：

> 共用套件本體 49 個檔案沒查到新問題，**問題全在主系統把它接上來的那段——而那段是整個重寫的。套件原本那份有檢查，重寫的那份漏了。**

14 條分四組：

- **A 組（第 1、2、5、6、9 條）**：只驗登入、不檢查歸屬，**統一套原則二**。三個要點：第 1 條拿到的清單裡有檔案編號（**與 M02 是同一條鏈**）；第 2 條是唯一「能寫」的，留言會推播給全體成員、**可冒充同事釣魚**；第 6 條**現在沒事是靠運氣**（推進與退回都沒檢查，只是剛好被下游套件擋住）。
- **B 組（第 3、4 條）**：見上方教訓。修法定案五件——①記住走過哪裡（治本、零風險）②加上限（**數字待定，待決 15**）③那支檢查功能補權限 ④範本新增／修改要驗證 ⑤起輪次要求範本已發布。**④⑤ 是報告完全沒寫的，不補的話前面的驗證都可繞過。**
- **C 組（第 7 條）**：決策者已裁定「只能看的角色不可以完成或退回任務，只能留言」。**影響面比表面大**——弱點掃描模組有八支會改狀態的功能吃同一道檢查，修這條會一併解決。
- **D 組（6 條）**：修法明確。兩點留意——第 12、14 條是「現在無害因為沒人用」，**建議直接刪掉不要留地雷**；第 13 條的「部分修」是順手消失的、不是刻意修的，機制原封不動。

---

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

### 回答要短
他明講過：「**之前都太長了，雖然很詳細但很容易失焦**」。先給結論再給依據、一次回答一個問題、表格優於長段落。**他要的是能上台講的一句話。**

### 不要自作主張往下跑
他會說「再來第 5 條」「回去看 M01」——照他說的走。**講完一條就停，等他發問。**

### 條數多的塊要分組講，不要逐條
第三棒在 M06（14 條）改用這招：先講這塊做什麼 → **把問題按形狀分組** → 只展開真正需要他判斷的那幾條 → 其餘列表帶過。**能套既有原則的直接套，不重新討論。** 決策者認可這個講法（「就這樣講」），14 條四輪對話就過完。

### 事實一定要回程式碼查，不要只信文件
**這是最重要的一條。** 前三棒在五塊裡抓到十幾條文件錯誤。

第三棒抓到的（M08／M22／M09）：

| 報告寫的 | 實際 |
|---|---|
| M08 第 1 條修法①「列入開機必查」 | **無效**——換掉零件後任何簽名都會過，攻擊者可以連清單一起偽造，變成自己驗自己 |
| M08 只有兩層檢查 | **有三層**，漏寫了敏感操作的即時驗證（這讓第 1 條的嚴重性被低估） |
| M08 第 5 條「部署時只讓內部網路看得到、打不到」 | **這句是錯的**——鎖定服務佔的就是後端對外的那個出入口，任何人都打得到 |
| M09 第 2、4 條修法「把換行清掉」 | **方向錯**——日誌有不可竄改的規範，應該是**編碼**不是刪除（決策者提出） |
| M22「9 個客戶的舊權限要清」 | **沒有 9 個客戶，產品沒出貨過**（決策者提出） |
| M06 第 3 條修法「讓走訪時記住走過哪裡」 | **已經有了**（決策者質疑後開檔查到） |
| 全站的程式行號 | **全面失效**——套件在掃描後經歷 CM-1731 版面歸位與 CM-1693 拆檔 |

**三個查證手段，按有效性排序**：
1. **查 commit message**——常常直接回答「當初為什麼這樣做」
2. **登入實機看**——188/189/190 三台
3. **開檔看程式**——不要憑報告或印象

**而且前三棒自己都講錯過。** 第三棒錯過三次：把「編譯了」講成「改不動」（編譯只是看不懂，檔案照樣可以整支換掉）；把容器之間的設定與對外代理混為一談，誤答「鎖定服務預設不對外開」；把總表早就裁過的「測試機密碼不用換」又列成待決。**講之前先查，講錯了立刻更正。**

### 三個 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`
- License Center：`/Users/chouraymond/Projects/Billows/Audit-Manager/license_center`（M08 用到——解鎖檔與檔案清單都是 LC 用同一把私鑰簽的）
- 實機：`ssh jedi@192.168.50.189`（POC）、`ssh jedi@192.168.50.190`（E2E）
- 資料庫：本機 `localhost:5432` / `guidant_ai_dev` / `.env` 裡的 `cmmgr`，**只能 SELECT**

🔴 **實機只能唯讀**（CLAUDE.md 環境異動鐵律）。

### 白話
讀者是 PM 與老闆。**「白話」不是把句子講得口語，是讀者完全不需要技術背景就能懂。**

**禁止寫**「已確認安全」「沒有漏洞」「全面檢視」。

### 不要自己改結論
可以說「這條我覺得評太輕／太重」並說理由，**但改不改由決策者決定**。他的判斷會受他知道而你不知道的事影響。

**第三棒有兩個實例**：①M08 第 5 條我建議「LC 簽發時比對竄改回報紀錄」，決策者指出要以落地版為準、**離線客戶根本送不出回報**，這建議會把最需要解鎖的人擋死——當場作廢。②M22 第 1 條我建議「①要求平台管理員 ＋ ④只給總部」疊加，決策者點出①會讓客戶完全不能自主、與落地版形態衝突——收回，單做④。

---

## 派工紀律

微量查證與改稿用 subagent 即可，不必開 Notion 卡（太大才開）。

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

🔴 **subagent 的回報要自己核對過再講給決策者聽。** 第一棒派去查 nginx 的那支，把「agent 那頭的 mTLS」跟「我們這頭的」當成同一道門，結論整個反了。**subagent 挖到的線索有價值，但它的結論要驗。**

---

## 與文件首腦的分工

還有一個 session 是**文件產出線的首腦**，負責格式、事實驗證、build。

**分界：它動格式與事實，你動內容與結論。** 那 23 份 md 在內化期間以你為主。

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

### 🔴 派工卡必須交代的三件事（第三棒踩過）

1. **施工清單列的是改動起點，不是全部**——改完每一處要回頭把整頁讀一遍，把跟著矛盾的地方一起修掉。**最容易留矛盾的五處**：檔頭 lede、問題一覽表的「怎麼修」欄、「這塊的結論」段、統計表、跨頁引用。
2. **自我檢查那句要寫進卡**：「如果讀者只讀這一頁、從頭讀到尾，會不會讀到兩句互相打架的話？」
3. **刪除類改動要列出 grep 關鍵詞**——刪除最容易留殘句。

第三棒因為第一版卡沒寫這三件，決策者當場提醒「**要記得請他內文也要調整**」。

---

## 已產出的卡（後面每塊照這個形狀開）

| 卡 | 內容 |
|---|---|
| **CM-1981** | M02 檔案上傳下載 |
| **CM-1982** | M01 遠端代理程式（第三棒追加 D6、D7 兩項裁定） |
| **CM-1984** | M08 防竄改 |
| **CM-1985** | M22 系統設定（第四節是「產品還沒出貨」的全站影響盤點） |
| **CM-1987** | M09 系統日誌 |
| **CM-1988** | 全站三項裁定落地（出貨前定位／拿掉行號／M01 M02 回頭修）— **已做完** |
| **CM-1989** | M06 稽核流程（第 3 條幾乎重寫、第 4 條降級） |

**卡的節次形狀**：逐條更正（E 編號）／新發現／講者決策（D 編號）／風險等級對齊／問題分組歸類／待決清單／施工紀律／**給文件首腦的施工清單**。

**把「事實更正」跟「講者決策」分開編號**（E vs D），文件首腦一眼就知道哪些是「改錯字」、哪些是「照判斷改寫」。

---

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

### 一、密碼類欄位「預設不回傳」（標記制，不是名單制）
設定類的密碼欄位一律不回傳給前端，只有程式內部存取時取得真值。

**理由**：名單制漏登記＝密碼外洩；標記制漏標記＝頂多前端少看到一個欄位。

**M22 第 3 條正是這條的活案例**（兩份名單都漏掉檔案儲存那組，而寄信與員工帳號目錄已經做對了）。

### 二、權限檢查走「一條路由 + 登記」，不為每個情境各開路由
檔案模組留一個空位說「我需要一個能回答『這人能不能拿這個檔』的判斷函式」，各業務模組把實作登記進來。

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

**「只驗登入、不檢查歸屬」那一整組都適用。M06 的 A 組五條直接套。**

### 三、所有修法都要先判斷「功能不能壞」
講修法時要一併講：這樣改會不會壞掉、哪一步有風險、安全的施工順序。

**遇到「改了會不會壞」的顧慮，先去查事實，不要停在假設。** 而且現在多了一個前提：**產品沒出貨，多數顧慮只剩「不能弄壞打包流程」。**

### 四、「帶出公司」這類論述要驗證再寫
決策者推翻過「稽核證據可以被合法帶出公司」——一個人只要下載得到，本來就能轉寄、拍照，任何系統都擋不住。**留著反而給人挑毛病的把柄。**

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

### 五、不要相信呼叫方自報的東西，自己去查
「測試連線」入口原本收網址、照著打。決策者裁定改成**收編號，後端自己去資料庫查出網址**。

**為什麼比「檢查」好**：檢查會寫漏寫錯；不收就沒得騙。**這是消除選擇，不是增加檢查。**

**M08 第 1 條的修法同一個家族**：把零件編譯進核心層，不是「多加一道檢查」——**讓「被單獨抽換」這件事物理上不可能發生**。

### 六、🆕 落地版的設定，客戶自己要管得到（M22 第 1 條定）
產品裝在客戶機房、主機權限都在他們手上，**卻要為了改密碼長度打電話給原廠，這不合理**。

**規則**：這類「一整家客戶共用的基礎設施」設定（登入安全政策、員工帳號目錄、寄信伺服器、日誌轉送目的地），**只有平台管理員與客戶組織樹最上層的管理員能改，子公司層級不能改**。

**問題的本質是「改的人層級不對」，不是「客戶不該能改」。**

### 七、🆕 交出去的資料要編碼，不是刪掉（M09 第 2、4 條定）
日誌有不可竄改的規範，**刪字元說不過去**。換行要**編碼成 `\n`**、Excel 公式字元要**標示成文字**——**資訊一個位元都不能少**。

**關鍵論述**：**被竄改的是現況，不是修法。** 攻擊者填的是一個值，現在的系統卻讓收件方讀成兩筆紀錄——那本身就是紀錄失真。編碼是修正回忠實呈現。

**一句話原則**：**進來的照實記，交出去的時候才負責讓它安全。**

---

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

| 組 | 目前成員 | 一句話 |
|---|---|---|
| **A. 只驗登入、不檢查歸屬** | M02 第 1/2/3/7 條、**M06 第 1/2/5/6/9 條**、報告說橫跨七個模組十幾條 | **最大的一組**。修法＝原則二 |
| **B. 鑰匙掛在牆上** | M02 第 5 條、M01 第 4/5/6 條、**M22 第 3 條**、M16、M09 | 拿到憑證就繞過所有檢查 |
| **C 的擴大版：「想到了，只做了一半」** | M01 第 1/2 條與撤銷不完整、M02 第 5 條、M05 問卷、**M22 第 2 條**、**M06 第 1/2/9 條** | **不是沒想到，是做了一部分就停了。成員數可能超過 A** |
| **🆕 D. 信任鏈的起點沒人守** | **M08 第 1 條** | 驗證工具自己不在被驗證範圍內 |
| **🆕 E. 客戶層級的權限，改到全系統共用那一份** | **M22 第 1 條、M09 第 1 條** | 判準：「這個權限是客戶層級的嗎？它寫入的那份設定，全系統有幾份？」修法＝原則六 |
| **🆕 F. 進來照實記，交出去沒把關** | **M09 第 2、4 條** | 與 C 組不同型：C 是「該檢查的沒檢查」，這組是「**該編碼的沒編碼**」。收尾看其他模組有沒有匯出／送外部系統的同型 |
| **🆕 G. 有正確版本但沒用** | **M06 全塊**（套件 49 檔沒問題，主系統重寫的接線漏了檢查）、**M08 的跨端觀察**（agent 那頭做滿三層、我們這頭一層都沒有） | **成因與其他組都不同**——不是沒想到、也不是做一半，是**東西寫對過，但被重做的時候沒跟上**。M06 過完後這組已有兩個實例，建議收尾時正式立組 |

**另一條值得收尾講的**：M08 挖到「同一套憑證機制，agent 那頭做滿三層、我們這頭一層都沒有」——說明問題不是「不會做」，是**兩端由不同人在不同時間做，沒有人負責檢查兩端有沒有接上**。對修法可行性是正面訊息，有現成的參考實作。

---

## 待決清單：舊的十四項已全部結清，M06 新增兩項

十四項的處置如下（**不要再把已結的重列成待決**——第三棒犯過這個錯，把總表早就裁過的「測試機密碼不用換」又列了一次）：

| # | 待決 | 結果 |
|---|---|---|
| 1 | M02 第 5 條優先順序 | 收尾一起排 |
| 2 | M02 第 2 條降級 | **不單獨排級**——與第 1/3/7 條是同一個洞的第四個出口，套原則二一起消掉 |
| 3 | 「系統共用」檔案規則 | 實作時定 |
| 4 | 數量上限 | 實作時定，給合理預設 |
| 5 | 換發儲存帳密時機 | **消掉**（產品沒出貨） |
| 6 | 裝機報到代碼三選一 | **選①**：維持現狀 + 改成看得見（CM-1982 D6） |
| 7 | 風險等級以哪邊為準 | 是文件修正不是決策 |
| 8 | Zero Trust 是否生效 | 是查證工作，未做 |
| 9 | 公開網站資料庫密碼 | **不換**（只有測試機在用，CM-1982 D7） |
| 10 | 出貨流程加密碼檢查 | 收尾一起排 |
| 11 | 報告行號失效 | **拿掉行號只留檔名**（CM-1988） |
| 12 | M08 編譯驗證誰做 | 併進 M08 修正工單 |
| 13 | 「還沒出貨」寫進首頁 | **要寫**（CM-1988，已做完） |
| 14 | M01/M02 回頭修 | **要修**（CM-1988，已做完） |

**舊清單還沒做的只有第 8 項**（Zero Trust 是否已生效、涵不涵蓋文件站——影響 M01 第 5 條的狀態改寫）。那是查證不是決策，接手後可以派 subagent。

### M06 新增兩項（決策者尚未定）

| # | 待決 | 需要誰 |
|---|---|---|
| **15** | 第 3 條的上限訂多少（分岔深度／節點總數）——**要先查現有範本（含出貨預設）最複雜幾層**，上限訂在那之上。報告不要自己寫死數字 | 講者 + PM |
| **16** | 第 4 條降到哪一級——越權與無上限問題屬實，但不會癱瘓伺服器，建議由🟠高降級 | 講者 |

---

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

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

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

---

## 開場怎麼做

1. 讀完上面三份文件（README／GUIDE-01／_module-page-spec）
2. 用三五句話說你理解的「這份報告要解決什麼問題」
3. 問決策者任何不清楚的
4. **然後開始講下一塊**

**下一塊建議**（決策者未指定，可提議讓他選）：

| 候選 | 為什麼 |
|---|---|
| **M05 問卷** | 2 輪。「**六個寫入入口全有檢查、八個讀取入口一個都沒有**」是 C 組最乾淨的例子，講起來快 |
| **M07 證據自動分類** | 5 輪。風險集中在待淘汰的舊功能（填一個雲端硬碟檔案編號就能讀客戶整個硬碟） |
| **M13 登入與權限** | 7 輪、查最多。**13 個問題全部已修**——這塊講起來是正面的，可以調節節奏 |

**M03 弱點檢測決策者已指示先跳過**（檢視還在跑，數字會變）。

**不要一次講完，一塊一塊來，他要跟得上。** 條數多的塊按形狀分組講，不要逐條。
