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

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


§1

你的角色

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

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

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

工作目錄:/Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be 報告原始碼在 docs/security-report/(md 是源,html 是產物)。


§2

先讀這些

  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 — 模組頁撰寫規格(了解格式即可)

§3

這份報告是什麼

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

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

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

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


§4

進度

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

目標是 23 塊全部過完(決策者 2026-09-20 明示),一棒接一棒做到完。


§5

🔴 決策者的工作偏好(前兩棒踩過,務必照做)

回答要短

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

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

不要自作主張往下跑

他會說「再來第 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 也不過期,交由使用者保管就好」——他是對的,因為他知道客戶的裝機流程長什麼樣。我當場收回建議、改成列三個選項給他選。


§6

派工紀律

微量查證與改稿用 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/

§7

與另一條線的分工

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

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

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


§8

前兩棒的產出: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),文件首腦一眼就知道哪些是「改錯字」、哪些是「照決策者的判斷改寫」。建議後面沿用。


§9

🔴 決策者已拍板的原則(會跨模組適用,遇到同類問題直接援引)

一、密碼類欄位「預設不回傳」(標記制,不是名單制)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

五、🆕 不要相信呼叫方自報的東西,自己去查(M01 第 2 條定)

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

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

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

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


§10

🔴 浮現中的問題分組(收尾要用,每塊過完就歸類)

最後的「分析與結論」要把 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 那頭做滿三層(驗憑證、驗通行證簽章、驗綁定指紋),我們這頭一層都沒有。

這說明問題不是「不會做」,是兩端由不同人在不同時間做,沒有人負責檢查兩端有沒有接上。對修法可行性是正面訊息——有現成的參考實作。

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


§11

待決清單(累積中,決策者還沒定)

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

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


§12

最後的收尾(23 塊過完才做)

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

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


§13

下一塊:M08 防竄改檢查

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

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

跟前兩塊的對照(可以拿來開場):

  • M02 是「有帳號的內部人越權」
  • M01 是「完全沒帳號的外人」
  • M08 是「拿到程式碼的人動手腳」——三塊講完,決策者手上就有三種攻擊者的完整光譜

開講前建議先查證的幾點(前兩棒的教訓):

  • 「已實測證實」是什麼時候測的?現在還是這樣嗎?
  • 「永久失效」的機制是什麼?查程式碼,不要只看報告敘述
  • 有沒有對應的 Notion 工單?狀態如何?(M01 那四張查出來全是 Not started)
  • 這塊的檢視只有 2 輪(比 M01 的 5 輪少),涵蓋面的但書要講清楚

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


§14

開場怎麼做

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

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