資安掃描修正卡草案(117+33 條分組)

修正卡草案:這些問題該分成幾張卡、每張卡修什麼

這份文件回答一個問題:掃描累積下來的 145 條問題,要切成幾張修正卡,每張卡修什麼、怎麼驗、先做哪張。

這是草案,不是已經決定的事。決策者看完裁定之後,才照這份去開 Notion 卡。 每一條問題被分進哪一組、有沒有漏掉,看 分類對照表; 每一條的完整技術細節,看 總表 §3。


0. 三十秒版

145 條問題(資安 117 條+非資安的真程式錯誤 28 條)分成 44 組,建議開 24 張修正卡。

之所以組數比卡數多,是因為有幾組本來就該合在同一張卡裡修——例如檔案取得鏈那六條,只修其中一條等於沒修。

數量
分了幾組 44 組
建議開幾張卡 24 張
未歸組 0 條
已修、不必再排 11 條
改了一半、這一條針對的事還在 10 條
原封不動 124 條

最想請決策者先看這三組

  1. A1 檔案取得鏈(6 條)——門檻最低的一條攻擊路徑:一個有效帳號加一個檔案編號,就能取走、刪除別家客戶的稽核證據,還能換一張不用登入的下載連結傳到公司外面。五個出口必須同一張卡一起修,只修一邊等於沒修。決策者 2026-09-13 已裁「現在就開工單」,這組是那次裁定的直接產物。
  2. C1 設定 API 吐帳密(3 條)——任何登入帳號打一支查詢就拿到物件儲存的帳號密碼明文。不修這條,應用層補再多檢查都沒用:拿到那組帳密可以完全繞過系統直連儲存空間。三處小改。
  3. D2 日誌表本身(7 條)——密碼原文寫進日誌、日誌表沒有客戶隔離、保存期限從沒生效,三件事疊起來變成「密碼明文無限期累積在一張誰都看得到的表裡」。這組的 RUN_ENV=prod 那一半已經修好了(第 25 項),剩下的部分變得更划算。

三件跟分組有關、決策者可能會問的事

  • 「同一種病修一次」到底能省多少:145 條裡,光是「只驗登入、不驗這筆資料是不是你的」這一種病就佔了 42 條(A1~A9 九組)。這九組的修法形狀完全一樣——補一行「這是不是你的」檢查,抄同一個檔案裡寫入方法已經寫好的那支函式。分散開 42 張卡,每張卡的人都要重新理解一次;併成九張,第二張之後都是照抄。
  • 修正到現在極度偏食:兩次複查裡判定「已修」的 11 條,沒有一條是那九組裡的——修好的都是資料庫隔離(B 組)與套件小 bug。資料庫那道牆這段期間補了不少,應用程式那道門一步沒動。 這不是誰偷懶,是那批工作(FR-094、FR-107)本來就只做資料庫層。
  • 有 6 條建議「不開卡」:X3 那四條複查確認已修好、Z 組裡有兩條總表自己標明「只回報待裁、不直接開卡」。列在這裡是為了讓 145 條每條都有交代,不是要排修。

1. 怎麼分的、為什麼這樣分

分組的判準只有一個:這幾條能不能用同一套修法一次解掉。 不是按套件分、也不是按嚴重度分——按套件分會把同一種病拆散到十幾張卡,按嚴重度分會讓同一條攻擊路徑的頭尾被排進不同輪。

起點是總表 §4 已有的十一組索引(🅰~🅺),沒有從零發明分法。本棒做的是三件事:

  1. 把 §4 的大組拆細。§4 的 🅰 組收了 39 條,橫跨七支套件——當成一張卡開,沒有人做得完。本棒依「同一支套件、同一個檔案、同一次測試能驗完」拆成 A1~A9 九組,每組 2~11 條。
  2. 回頭核對 §3 全表,把漏分的補進來。§4 沒收過、本棒新歸入的共 48 條——多數是 §3.2 那些非資安的程式錯誤(§4 只零星收了幾條),以及 FR-095/FR-111 後期那幾棒的發現。對照表上每條都標了「本棒新歸入」。
  3. 標出狀態。§4 的索引是逐棒累加的,沒有人回頭核對過哪些已經被別的工作修掉了。本棒依總表自己的兩次複查(09-16、09-20)逐條標記,已修的 11 條不再排進修正順序。

本棒沒有做的事:沒有開檔重驗任何一條(複查沒覆蓋到的一律照總表寫「仍在」)、沒有改總表一個字、沒有開 Notion 卡。


2. 建議順序

排序的依據是三件事:門檻有多低(要先有什麼才打得到)、不修會不會讓別的修法失效、修法有多確定(已裁定的、一行就能改的排前面)。

輪 這一輪做什麼 組 卡數 為什麼排這裡
第一輪 堵住「拿到帳密就繞過整個系統」與最低門檻的取檔路徑 C1、A1 2 C1 不修,後面所有應用層修法都白做;A1 門檻最低且決策者已裁「現在開卡」
第二輪 「只驗登入不驗歸屬」剩下八組一次做完 A2~A9 8 修法形狀一樣,第二張之後照抄;同一批人連著做最省
第三輪 日誌這條線 + 憑證處置 D1~D5、C2、C4 5 密碼外洩的完整鏈路,要一起收才算修完
第四輪 輸入不驗、資源耗盡、防竄改 K1~K5、R1~R3、T1 4 彼此獨立,可以平行派
第五輪 資料庫隔離收尾 + 小修合卡 B1~B3、X1、X2 4 B 組大半已被 FR-094 修掉,剩收尾;X 組是順手做掉的小東西
待裁 要先定產品方向才能動程式 P1~P7、I1、I2 (1 張決策卡) 修法不只一種選項,程式不能先動

跨輪的一條線:C 組(憑證)裡有幾條是「去後台按撤銷/換密碼」的動作,不是寫程式。那幾條不占開發時間,可以跟第一輪平行做。


3. 各組草案

每一組的格式固定:共同病根一句 → 涵蓋哪幾條 → 修法 → 怎麼驗 → 風險級別。 「涵蓋哪幾條」欄裡,編號後的括號標的是狀態;沒標的就是「仍在」。


🔴 A 系列:只驗登入、不驗這筆資料是不是你的(九組 42 條)

整個 A 系列的共同病根:系統認得出「你是誰」,但從來不問「這筆資料是不是你的」。登入就放行。

九組共用同一套修法:在服務層補一道「這筆資料的主人是不是呼叫者(或呼叫者是不是這個專案的成員/管理者)」的檢查——每一組要抄的那支函式,都已經寫在同一個檔案的寫入方法裡。這不是設計新機制,是把已經存在的檢查補到漏掉的地方。

🔴 這一系列有一個驗收陷阱,每一組都適用:總表複查兩次都撞到「端點換上了看起來像守門的裝飾器,但那道守門從來不是歸屬檢查」(第 34、45、74 項)。驗收時不能只問「有沒有補守門」,要打開那道守門的實作看它到底檢查什麼。

A1 檔案取得鏈(6 條 · 建議開 1 張卡 · 🔴 最高優先)

病根:檔案的下載、換發免登入連結、刪除、服務層取檔,四個出口全都只驗登入。

條 是什麼 狀態
34 下載端點只驗登入 部分修(資料庫層已補、應用層沒動)
35 換發不用登入就能下載的通行證,不驗歸屬 仍在
37 刪除任何檔案不驗歸屬 仍在
40 主專案側檔案服務層三個方法都只憑編號取檔 部分修(同 34)
45 任務詳細 API 收了專案編號卻沒拿來用 部分修
47 存放檔案的資料表沒有隔離(最後一道防線) 部分修(資料庫層已補)

涵蓋範圍:2 支套件(jedi-file-upload、主專案側)+ 5 個檔案。

修法(四步,第三步要決策者放行):

  1. 服務層補歸屬檢查(managed_file_upload_service.py 的取檔/轉檔/刪檔三個方法)——這一層是唯一「已經把檔案撈出來、知道它屬於誰」的地方,真正該補的是這裡。
  2. 換發通行證的入口補檢查——簽發前先確認這個檔案是不是呼叫者的;並讓通行證綁客戶與使用者,不只綁檔案。
  3. 出貨基線同步(要決策者裁):資料庫那兩欄與隔離規則目前靠 installer 事後補,基線建表還是舊形狀。重產基線要寫 188 基線庫,屬決策者裁示。
  4. 第 45 項分開寫:它剩的三個缺口跟其餘五條不同(查詢那支完全沒補、補的兩支不核對任務歸屬、資料層更新只憑編號查),開卡時要分段寫清楚,不能寫成「同上」。

⚠️ 34/40/47 三條的原文必須改寫再開卡——原文說「資料表沒有客戶歸屬欄位、隔離是關的」已經過期(FR-107 為了別的目的補上了)。照抄會讓接手的人去做一件已經做完的事,然後以為整條修完了。

怎麼驗:用 A 客戶的帳號,拿 B 客戶的檔案編號打下載、換通行證、刪除三支,都要回 403(現在是成功)。同租戶內跨專案也要擋。DEV 唯讀可以先查資料庫現況,實際打端點要在 DEV 做。

風險級別:🔴 高(組內最高:34/35/40/47 四條高風險)


A2 稽核流程讀取(5 條 · 建議開 1 張卡)

病根:稽核流程引擎裡,同一個檔案的寫入方法都有歸屬檢查,讀取方法全部漏掉。

條 是什麼
49 讀證明清單只驗登入(拿到檔案編號後可接 A1 直接下載檔案本體)
50 讀單筆證明沒有歸屬檢查(目前被另一個程式錯誤擋住,那個錯誤一修就真外洩)
51 流程留言讀寫都只驗登入(可冒充同事名義發言釣魚)
52 讀稽核階段歷程完全沒有權限檢查
53 讀階段資訊:用專案編號查角色、用另一個輪次編號撈資料,兩者不核對

涵蓋範圍:jedi-flow-engine + 主專案側 · 4 個檔案。

修法:

  1. 49/50 在同一個檔案補兩行,抄同檔已有的 assert_project_participant。
  2. 51 要在另一個模組補讀取與寫入兩處。決策者 2026-09-13 已裁升為高風險。
  3. 53 要一併補 advance_stage/rollback_stage 兩支的輪次歸屬比對——目前跨專案寫入是靠下游套件的第二道檢查在擋,這一層自己沒檢查。不補的話,將來新增一支不走那個套件的處理函式,洞就重新打開。
  4. 50 與那個程式錯誤必須同一張卡:單筆查詢的端點誤用了「多筆」的序列化方式,先出錯所以沒真外洩。只修程式錯誤不補權限,等於親手把洞打開。

怎麼驗:A 客戶帳號拿 B 客戶的任務編號/輪次編號打這五支,全部要 403。第 50 項要在修完序列化錯誤之後再驗一次。

風險級別:🔴 高(49/51 兩條高風險)


A3 任務平台名冊與指派(11 條 · 建議開 1 張卡)

病根:成員名冊與任務指派的讀取全部沒有權限檢查,而且查詢條件全是選填——送一個空請求就回整張表(實測:成員名冊 751 筆、任務指派 12,494 筆,跨所有客戶)。

條 是什麼
60 專案層成員名冊讀取無檢查、空請求回全表(這是整組的資料源頭)
61 控制項層成員名冊兩支同樣無檢查
62 第六支成員服務整個檔案零權限檢查(目前沒有 API 入口,接上就開)
63 流程參與者的兜底檢查寫在提前返回之後,永遠執行不到
64 寫入時不驗 group_id/control_id 是不是真的屬於這個專案
68 任務指派清單只驗登入、八個條件全選填
69 新增指派用自填的專案編號判管理者,不驗任務歸屬
71 更新指派撈不到紀錄時整段跳過管理者檢查
§3.2 17 三支服務的「用編號查資料」永遠回空——補完權限拿這條路測會得到假通過
§3.2 19 流程參與者表 DEV 0 筆、改刪必出錯(就是第 63 項造成的)
§3.2 20 專案啟動時的成員同步刻意繞過套件守門(設計自陳,記錄不判)

涵蓋範圍:jedi-task-platform + 主專案側 · 約 10 個檔案。

修法:

  1. 讀取那幾支照抄同檔已有的 delete_task_assignee 寫法補檢查,專案編號改必填。
  2. 63 把兜底檢查移到提前返回之前,並補上那個根本不存在的驗證方法(或改呼叫真的存在的那支)。
  3. §3.2 第 17 項必須同卡先修——那支永遠回空的查詢會讓「補完權限後的測試」看起來通過,實際上只是查不到東西。不修它,這張卡的驗收是假的。
  4. 62 目前沒有 API 入口,優先度低於同組其他條,但要一起補,否則哪天有人接上就是一個全開的入口。

與 I2 的關係:60/61/68 的「空請求回全表」根因在共用底層(I2 第 70 項)。決策者 2026-09-15 已裁「要在底層擋,但分兩步,第一步先盤點」。這張卡走的是第二條路——逐支補「專案編號必填+成員檢查」,不等底層改;底層那件事照原裁定另案進行。

怎麼驗:送空白請求給那幾支,要回 400(欄位必填)而不是整張表;帶別人的專案編號要回 403。驗之前先確認 §3.2 第 17 項已修,否則結果不算數。

風險級別:🔴 高(60/69 兩條高風險)


A4 問卷讀取與討論(8 條 · 建議開 1 張卡)

病根:這是整個掃描計畫裡「補權限只補了一半」最乾淨的一次實證——填答功能 14 個入口,6 個寫入全部有檢查(6/6),8 個讀取一個都沒有(0/8),而且在同一個服務檔案裡。

條 是什麼
89 查填答歷史明細,空請求整包讀走全客戶所有問卷的每一版答案與審核意見
90 讀某份任務問卷的答案不檢查是不是你的
91 列出任務問卷不限範圍(同時是第 90 項的入場券)
92 列出填答歷史不限範圍
94 還原歷史版本不檢查那個版本是不是這份問卷的
95 即時同步的房間想進哪間就進哪間
96 問卷討論列表送空查詢就撈回全公司討論
97 改/刪討論不驗是不是本人寫的,改完還掛原作者名字

涵蓋範圍:jedi-survey · 約 8 個檔案。

修法:套件裡已經有現成的 assert_task_survey_writer 與對應的讀取守門,逐支掛上即可;91/92/96 三支的查詢條件改必填。97 若產品面決定「管理員可以刪別人的留言」,要走一條寫明白的管理路徑,照樣檢查他是不是這個專案的人。

這一組要帶一句寫進卡片:2026-07 那次統一補權限的工作只涵蓋了寫入。往後任何一次補權限的驗收,都要把讀取入口分開數一遍。

怎麼驗:A 部門帳號拿 B 部門的問卷編號打這八支,全部 403;空請求要 400。即時同步那兩支要另外開連線測。

風險級別:🔴 高(89/90 兩條高風險)


A5 主專案專案讀取(2 條 · 併入 A3 或獨立小卡)

條 是什麼
65 專案摘要報告清單與歷史版本四支讀取只驗登入(含稽核結論全文)
66 稽核輪次選單一支只驗登入

修法:照抄同檔已有的 assert_project_participant,一行。跨客戶已被 FR-094 的資料庫隔離接住,現在只剩同客戶內跨專案。

風險級別:中。建議併入 A3 同一棒做(同一個人手上,抄的是同一支函式)。


A6 弱點掃描端點(3 條 · 建議開 1 張卡)

條 是什麼
85 「測試連線」不驗權限——按一下就把掃描工具帳密解密送到按的人指定的主機(🔴 高)
86 同一支端點,目標主機由呼叫者指定、不比對白名單——代理程式變成任意連線跳板
88 查掃描執行紀錄少了專案參與者檢查(同服務另外八支都有做)

修法:85 掛上同檔其他三支在用的「工具設定修改」權限點;86 目標主機限制成存好的設定範圍;88 補一行參與者檢查。

⚠️ 85 與 86 必須同卡:補了權限(85)不會讓 86 消失,只是把門檻從「任何登入者」提高到「工具管理員」。只修 85 就結案,是把一個對外的跳板留給內部人。

風險級別:🔴 高。


A7 舊 Drive 分類線(3 條 · 🔴 要先裁「刪路由還是補守門」)

條 是什麼
108 預覽任意 Google 雲端硬碟檔(🔴 高,本 arc 唯一高風險)
109 查結果、查報表不驗專案成員
111 job 列表/單一狀態不驗專案,且進度登記簿是全程序共用、不分客戶

這組要先裁一件事(總表 §7 第 19 項):舊線已標 legacy、前端入口已經拿掉,後端三支路由仍掛著。刪那三支路由一次消掉這三條;留著就要三條各補守門。

建議:先刪路由。理由是前端已經沒有入口,留著的三支路由是純風險、零價值;補守門要寫的程式比刪路由多,而且補完還是沒人用。

風險級別:🔴 高(108)。但如果裁定刪路由,這張卡會縮成一個 10 分鐘的工作。


A8 公告與 AI 儀表板(2 條 · 建議開 1 張卡)

條 是什麼
1 公告四個漏洞(AI 儀表板繞權限讀全租戶公告含草稿/改刪不驗歸屬/單筆讀取零檢查/無部門帳號全放行)
8 AI 儀表板讓任何登入帳號列出全公司帳號、角色、租戶、部門清單

修法:公告那四個有現成的檔名行號可以直接抄。第 8 項的正規做法是讓 AI 儀表板申報的每支查詢各自標明需要什麼權限,最快的止血是把那四支查詢從 AI 可用清單移除。

⚠️ 這組跟 P6(第 10 項「視同管理員」後門)同在 AI 儀表板上,但那條是刻意設計、要先裁產品方向,不要一起修。

風險級別:中。


A9 能力點宣告了但沒接線(2 條 · 建議開 1 張卡)

條 是什麼
58 流程範本列表與單筆讀取沒掛「讀取流程範本」能力點(能力點存在、選單也綁了,就是 API 沒掛)
113 detection-profile.read 資料庫早宣告、前端矩陣也認,後端七支讀取一支都沒檢查

病根跟 A1~A8 不同:那些是「忘了寫檢查」,這兩條是「權限點宣告好了,後端沒接上去」——結果是租戶管理員以為拿掉權限就讀不到,實際上 API 完全敞開。

修法:各補一行掛上既有能力點。

要帶進卡片的一句:這種病跟 P3(第 80 項資產清冊)長得很像,但性質相反——P3 是程式碼契約自己寫明「讀取不守」的刻意取捨,這兩條是漏接。不要一起處理。

風險級別:中。


🅱 B 系列:資料庫隔離(三組 12 條)

B1 開關沒開/規則沒訂完(4 條 · 建議開 1 張卡,大半已修)

條 是什麼 狀態
43 13 張表隔離沒生效(實測曾看到 39 筆待辦、213 筆專案) 部分修(甲乙丙戊四組已由 FR-094 全部開完)
44 4 個畫面直接繞過隔離 已修
5 三張表訂了規則但開關沒開 已修
4 公告與部門關聯表沒訂規則 仍在

現況:這組實質上只剩兩條。第 43 項只剩一張 config.log_forwarding_settings 沒處理,而那張正是報告自己標「建議重新分類、不算隔離缺口」的、且牽涉 P2(第 75 項)。

修法:第 4 項走 sql-migration 標準流程。⚠️ 開卡時務必寫明:bulletin_org_units 只有兩個欄位、沒有客戶歸屬欄位,不能照抄母表 bulletins 的四條規則——要嘛透過母表借隔離,要嘛先加欄位。不寫這句,接手的人幾乎一定會照抄然後失敗。

風險級別:低(剩下的部分)。建議降到第五輪收尾做。


B2 連歸屬欄位都沒有(7 條 · 建議開 1 張卡 · ⚠️ 難度跟 B1 完全不同)

條 哪些表
23 system_logs(668,813 筆)與 api_logs 兩張日誌表
7 jedi-issue 五張表(問題單、成員、三張關聯表)
47 檔案表(✅ 資料庫層已修)
67 oscal.profile_imports/ssp_implemented_requirements
§3.2 22 任務指派的驗證觸發器沒真的建起來,而且它讀的三個欄位在表上不存在
§3.2 31 基準的領域層與存取層四個檔零隔離程式碼(前提記錄,不是要改的錯)
§3.2 32 detection_profile_controls 零隔離全靠約定(脆弱設計記錄)

⚠️ 這組不能跟 B1 排同一輪。B1 是「開關打開」,這組是「先改資料表結構、回填既有資料、再補規則」——而且 system_logs 那張有 66 萬筆要回填。

修法:每張表都是「加欄位 → 回填 → 開隔離 → 補規則 → 出貨基線同步」五步。建議一張表一張卡,不要併成一張大卡。

要先裁的(總表 §7 第 8 項):兩張日誌表到底要不要做客戶隔離,還是明確定位成「只給內部維運看的全域資料」、改成限縮誰能查就好?後者不用改表結構,成本差一個數量級。這件事沒裁,B2 這組動不了。

§3.2 第 31、32 項不開卡——總表自己標明是「前提記錄」與「脆弱設計記錄」,不是程式錯誤。建議寫進資料庫設計文件,讓下一個改動的人知道這裡的安全是靠什麼撐著。

風險級別:🔴 高,但被「要先裁方向」擋住。


B3 隔離規則方向寫反(1 條 · 建議開 1 張卡)

第 87 項:把組織路徑 /1/102/ 切成 [1, 102] 當白名單,結果子單位看得到母單位的資料(還能改能刪),母單位反而看不到自己底下子單位的。該擋的沒擋、不該擋的擋了。

這條最不容易被發現,因為從外面看「隔離有開」。DEV 實查確認母子結構真的存在、資料全在子單位,兩面都已經在發作。

涵蓋範圍:9 張表跨三個套件(jedi-detection 5 張+遠端代理程式 agent_tasks +三張授權表)+出貨基線。

修法:改判斷式即可,但要一次改 9 條、且動到出貨基線(屬決策者裁示)。

風險級別:中,但這是資安與功能雙錯——母單位看不到子單位的資料是個功能 bug,客戶會直接回報。


🅲 C 系列:憑證(五組 14 條)

C1 設定 API 吐帳密(3 條 · 建議開 1 張卡 · 🔴 最高優先之一)

條 是什麼
39 任何登入帳號打一支查詢就拿到自己客戶的雲端儲存位址、帳號、密碼明文(三層防護一起破)
72 讀取系統設定的三個入口只驗登入(同檔寫入四個入口每個都有檢查)
73 密碼遮罩名單漏掉物件儲存那一組

🔴 這三條必須同一張卡。只補權限(72)不補遮罩(73),有正當權限的管理員打開頁面時瀏覽器仍會收到共用密鑰明文——任何看得到他瀏覽器流量、存檔或前端錯誤日誌的人都拿得到。只修一邊等於沒修乾淨。

修法:

  1. 三個讀取入口掛上同檔寫入已經在用的權限檢查。
  2. 遮罩的兩份名單各補一處(群組名單補「儲存設定」、欄位名單補物件儲存實際用的欄位名)。
  3. ⚠️ 動的是標註「凍結」的契約,要連測試一起改。

為什麼排最前面:不修這條,應用層補再多檢查都沒用——拿到那組帳密可以完全繞過整個系統直連儲存空間。

怎麼驗:用一個零權限的登入帳號打那三支查詢,回應裡不能出現任何密碼欄位;用有權限的帳號打,密碼欄位也要是遮罩後的值。

風險級別:🔴 高。


C2 憑證已經外流,要換要清(4 條 · 建議開 1 張卡,但多數是動作不是程式)

條 是什麼 性質
2 資料庫密碼、四家 AI 服務金鑰、Nexus 與 MinIO 憑證寫進版控 換憑證
3 安裝程式每套部署種同一組原廠超管密碼 出貨預設值(見 P7)
83 jedi-issue 歷史裡第二把 GitLab 權杖,從沒被撤銷過 🔴 去後台按撤銷
84 cmmgr 密碼+兩組產品帳密寫在需求文件裡,那份文件已經發佈到公開網站 🔴 換密碼+清檔

🔴 第 83、84 兩項不是「要不要修」的問題,是一個動作。83 是去 gitlab.com 後台按撤銷(並查該帳號從 2025-04 起的稽核紀錄);84 是換密碼並把公開站上那頁清掉。這兩件不占開發時間,可以跟第一輪平行做。

⚠️ 決策者 2026-09-19 已裁「三個例外也不先做」(含第 84 項)——理由是快掃完了、不分批。掃描現在已經收口,這條裁定的前提消失了,建議請決策者重新確認 83/84 要不要立刻處理。

已裁定不必再問的:測試機專用的資料庫與管理帳號密碼不需更換;不重寫 git 歷史。

風險級別:🔴 高(84:不用登入就取得、且是繞隔離的帳號)。


C3 預設不安全(4 條 · 併入 X1 小修合卡)

條 修什麼
19 主產品連 Redis 寫死不驗憑證——另一個套件已經修好的同一個漏洞的複製品,照抄現成修法+現成測試
29 忘記密碼的信箱遮罩,套件預設改成開啟(主專案已覆寫,但套件預設不安全)
42 連物件儲存預設改成開加密
117 建掃描基準時放行明文 http://,且網址型來源不記指紋

四條都是一行改預設值。建議併進 X1 一起做,不單獨開卡。

風險級別:中(19 最高,因為是已知修法的漏網複製品)。


C4 憑證落地留存(2 條 · 建議開 1 張卡)

條 是什麼
107 派工時把解密後的客戶機房登入帳密另存一份明文進工單表——那份從頭到尾沒有任何程式讀過,而且沒有清理機制、累積無上限
103 AI 金鑰走 docker run 命令列,同機任何帳號 ps 看得到(落地版跑不到)

修法:107 刪掉寫入那一行 + 補一支清存量的 migration。103 改 --env-file 或 -e KEY 不帶值。

107 值得單獨強調:產品刻意做的加密保護被這一行繞過了——拿到資料庫備份、pg_dump 檔或唯讀帳號的人,一句 SQL 就得到客戶正式機房的可用 SSH 密碼。而且那份資料沒有任何人在用。刪掉它沒有任何副作用。

風險級別:中,但 107 的投入產出比極高(刪一行 + 一支 migration)。


C5 三環境共用公鑰(1 條 · 不開卡,等 LC)

第 6 項:開發環境的私鑰簽出來的授權檔,正式環境的驗證也會通過。三個環境的公鑰全部編譯進同一份後端程式。

決策者已裁:現階段不處理,等正式簽發站建好之後一併處理。程式檔頭註解說明這是刻意設計(為了支援日後換發新公鑰不中斷)。

這一條列在這裡只為了「每條都有交代」,不建議現在開卡。


🅳 D 系列:日誌與外送(五組 17 條)

D1 密碼原文進日誌(3 條 · 建議開 1 張卡)

條 是什麼 狀態
22 登入密碼與憑證原文直接寫進日誌檔與資料庫 ✅ 已修(09-20 複查:四個 log 檔實際 grep,明文 0 筆)
30 全站唯一那道遮罩機制,遇到含引號的密碼只遮一半 仍在
18 竄改回報把伺服器日誌最後 50 行原文送到原廠 仍在

第 22 項雖然已修,但留下兩件殘留,建議寫進這張卡:

  1. 2026-09-17 之前的舊 log 檔輪轉後可能仍有明文——屬資料清理,不是程式問題。
  2. 遮罩比對的是 JSON 的 key 名,表單編碼的請求擋不到(登入走 JSON 已實測有遮)。

第 30 項的修法(總表 §7 第 7 項已寫明方向):改成先把內容解析成結構化格式再逐層遮蔽。這會動到全站唯一的這道遮罩機制,屬核心共用邏輯——依測試政策,這一條要補寫測試。

風險級別:中(22 已修後)。


D2 日誌表本身(7 條 · 建議開 1 張卡 · ⚠️ 與 B2 相依)

條 是什麼 狀態
23 system_logs 沒隔離、也沒有可用來隔離的欄位(668,813 筆) 仍在(同 B2)
24 完整錯誤堆疊寫進那張沒隔離的表 部分修(風險反而被放大)
25 出貨設定沒標「這是正式環境」 ✅ 已修
26 監控功能載入就執行 ✅ 已修
27 兩種日誌類別在正式環境寫死最詳細等級 部分修(改掉一項,剩兩棵仍寫死 DEBUG)
79 兩張日誌表的保存期限從未生效 ✅ 已修(已接上每日 03:30 排程)
§3.2 11 寫資料庫日誌失敗會連帶弄壞使用者的請求 部分修(風險反而被放大)

🔴 這組有一個要特別講清楚的:第 24 項與 §3.2 第 11 項在這段期間風險反而變大了。同一支改動(CM-1920)把資料庫日誌處理器補掛到正式與測試環境——它修的是「寫多少」,而「寫失敗會弄壞使用者請求」「錯誤堆疊落在沒隔離的表」這兩件事沒碰,等於原本只在開發機的問題,現在正式環境也會走到。

修法:

  1. §3.2 第 11 項:那支 emit() 全函式還是沒有錯誤處理,補上 try/except,一個小改。
  2. 第 24 項:只保留錯誤的類型與訊息、不保留完整堆疊。
  3. 第 27 項剩下的:把 sqlalchemy.orm 與 pymongo.event_loggers 兩棵調回 WARNING。
  4. 第 23 項要等 B2 那個產品決策。

好消息:這組七條裡三條已修,剩下的四條都是小改(一個 try/except、一個不存堆疊、兩個調等級)。投入產出比很高。

風險級別:中。


D3 日誌轉送鏈(3 條 · 建議開 1 張卡 · ⚠️ 與 P2 同一個決策)

這三條合起來是一條完整攻擊鏈:

條 是什麼
75 客戶的管理員改得動「全公司日誌要送去哪」(🔴 高)
76 路上完全沒加密、不必解密就看得到
77 還能在裡面塞偽造紀錄混淆追查

與 D1 第 22 項直接相扣——日誌裡有密碼原文,所以這條管道外洩的就是密碼本身。

第 75 項要先裁產品方向(見 P2),76/77 不用等:76 補加密選項、77 過濾換行符號(gelf 那半已經有現成作法可抄),兩條都可以現在做。

建議:這張卡拆成兩段——76/77 現在做,75 等 P2 裁完再接上。

風險級別:🔴 高(75)。


D4 匯出公式注入(2 條 · 併入 X1 或獨立小卡)

條 是什麼
78 匯出操作記錄的 Excel 沒過濾公式字元——種下去不需要任何權限,在任何權限檢查跑之前就記下來了(🔴 高)
82 匯出意見回饋的 Excel/CSV 同一種病

修法:兩處共用一支「開頭是公式字元就前置單引號」的中和函式。同一種病兩個位置,一次修完。

風險級別:🔴 高(78:不用登入就能種,唯一門檻是等管理員自己打開檔案)。但修法只有一支小函式——建議提前到第一輪順手做掉,不要排到第三輪。


D5 錯誤原文回前端(2 條 · 併入 X1)

條 是什麼
100 問卷資料夾列表把資料庫原始錯誤訊息整句吐回前端(含表名、欄位名、SQL 片段)
§3.2 25 證據分類失敗時把上游例外原文存進批次、前端可讀

修法:100 把那段「所有例外都接住」的程式拿掉,讓框架的錯誤處理器回標準錯誤碼;§3.2 25 改存固定錯誤碼+一句白話,原文只進 log。

風險級別:低。


🅴 E1 回應夾帶內部欄位(3 條 · 建議開 1 張卡)

條 是什麼
9 AI 儀表板查詢結果夾帶密碼加密用的鹽值
12 AI 儀表板回應夾帶鹽值與「是不是超級管理員」旗標
33 套件共用的序列化工具原樣吐出整個物件、零欄位過濾(上面兩條只是它造成的個案)

🔴 修第 33 條才是治本,只修 9/12 這兩個個案,之後還會有新的個案冒出來。

修法:在套件層的序列化工具加白名單或黑名單參數(至少擋掉密碼、鹽值、憑證這類欄位名);9/12 同時加欄位白名單或重用既有查詢已經寫好的排除設定。

⚠️ 33 在 jedi-common,改了要發版才生效,節奏跟只改主專案不同。

風險級別:中。


🅺 K 系列:外部送什麼就收什麼(五組 10 條)

K1 apply=False 大量指派(2 條 · 建議開 1 張卡)

條 是什麼
98 資料夾更新塞一個「已刪除」欄位就繞過「非空資料夾不可刪」守門,造出孤兒資料
99 資料夾列表蓋掉內部的「排除系統資料夾」開關,叫得出快照與已刪除的資料夾

病根:網址入口那行負責「檢查前端送來的欄位對不對」的宣告被加了 apply=False——叫框架完全跳過驗證,那一行只剩裝飾作用。業界叫「大量指派」。

修法:拿掉 apply=False,明確宣告允許的欄位。⚠️ 同樣寫法在問卷套件的四個網址入口上都有,四個一起改,不要只修被掃到的那兩支。

🔴 這張卡要附帶一件盤點工作(總表 §5 已列):另外 20 支套件要搜一遍有沒有同樣寫法。這件事比修這兩條更有價值——apply=False 是一個可以用一行 grep 找遍全公司的形狀。

風險級別:中。


K2 檔名/副檔名不驗(2 條 · 建議開 1 張卡)

條 是什麼
36 上傳的網頁檔在我們自己的網域裡被當程式執行(儲存型 XSS),副檔名完全不限制(🔴 高)
101 代理程式自報的檔名原樣存成證據檔,稽核人員預覽時 .html 會在瀏覽器裡執行

同一種病的兩個入口:一個是使用者上傳、一個是代理程式回報,但落地點是同一支預覽端點(依副檔名決定型別、而且是直接在瀏覽器裡開不是下載)。

修法:預覽端點改成不依使用者自填的副檔名決定型別、危險型別一律強制下載;上傳與回報兩側都補副檔名白名單、去掉路徑。

風險級別:🔴 高(36)。


K3 規則包不驗內容(3 條 · 建議開 1 張卡)

條 是什麼
115 上傳的掃描規則包原封交給外部工具,那工具會把裡面的設定檔當 Ruby 樣板先執行再解析(🔴 高)
112 「最多一萬個檔」的上限對 .zip 形同虛設(打開那一刻就整份展開進記憶體)
116 網址型規則來源完全繞過壓縮檔驗證器(三道上限只接在上傳分支)

112 與 116 是同一支驗證器的兩種病:112 是「上限存在但太晚生效」,116 是「上限根本沒接進這條路」。一起修。

修法:115 改成解析前先剝掉或轉義樣板語法(或換一個不執行樣板的解析方式);112 改成逐筆讀;116 把驗證器接到網址分支的呼叫點。

風險級別:🔴 高(115)。


K4 字串拼查詢(2 條 · 建議開 1 張卡)

條 是什麼
21 資料庫隔離用的連線變數用字串拼接組進 SQL(三位檢查員一致認為目前不可被利用)
110 查 Google 雲端硬碟的搜尋條件字串拼接、沒跳脫(未實測)

這組記的是「寫法本身不安全」,不是「現在就有漏洞」。擋住第 21 項的是外部環境的巧合(值的來源剛好是整數主鍵與資料庫查出來的字串),不是寫法本身安全。

建議:趁現在改掉,不要指望以後有人會一直記得這個限制。 改成參數綁定,成本很小。

風險級別:低(但形狀不好)。


K5 換工具不重驗上限(1 條 · 併入 A6 或獨立)

第 105 項:在一張稽核任務上換掃描工具時,「要掃哪些機器」不會拿實際生效的值重新檢查台數上限——先綁一支不設上限的工具、填一個超大網段,再換工具,舊的超大網段跟著留到新工具底下,執行時逐台展開吃爆記憶體。

修法:換工具時用實際生效的值重跑一次上限檢查。

建議併入 A6(同一支套件、同一批人)。


🅵 T1 防竄改機制(6 條 · 建議開 1 張卡 · 自成一組)

這組跟其他組的修法完全不互相影響,可以任何時候獨立排。

條 是什麼
13 換掉驗證簽章的函式庫,整套機制永久失效——已實測證實(🔴 高)
14 文件宣稱兩道鎖,程式只做了一道,刪一個檔就能讓鎖定的機器重開(🔴 高)
15 環境變數可以換掉「要核對哪個目錄」(這是 13 的放大器,單獨修意義不大)
16 解鎖紀錄毀損時預設當作「一張都沒用過」
17 鎖定畫面洩漏機器指紋與事件編號
18 回報功能夾帶日誌內容(同 D1)

要先裁的兩件(總表 §7 第 9、10 項):

  • 第 13 項要不要開卡——已實測、風險維持高,三個候選修法已寫在總表,等決策者選一個。
  • 第 14 項選哪個修法方向——補開機時的資料庫查詢/補同步機制反向寫回/只改文件讓它與實際行為一致。三個選項成本差很多。

⚠️ 15 必須跟 13 同一張卡,單獨修沒有意義。

這組還有三個相鄰面向從頭到尾沒碰過(總表 §3.4):解鎖檔的簽發端(在 License Center)、驗章的加密邏輯本身、打包出貨時產生清單檔的流程——最後這件直接決定防竄改實際保護的範圍有多大。建議寫進卡片當已知缺口。

風險級別:🔴 高。


🅹 R 系列:一個帳號就能讓系統對所有人不回應(兩組 8 條)

R1 BPMN 輸入炸彈(4 條 · 建議開 1 張卡)

條 是什麼
55 惡意流程圖卡死處理執行緒(🔴 高,決策者 09-13 裁升級)
56 一筆空字串範本讓所有人的清單頁出錯、不會自己恢復
57 「檢查流程圖」API 任何登入帳號打一次卡住 120 秒、連打四次全站不回應(🔴 高,同裁定)
§3.2 15 相關的一條路徑(已隨 v1 殘留端點清理順手消失)

55/56/57 是同一個根本原因(不信任使用者送進來的 XML),總表已建議合成一張「BPMN 輸入強化檢查」的卡。

修法分三段:

  1. 先止血:57 補上功能權限檢查與長度上限兩行,把門檻從「任何登入帳號」拉高。
  2. 治本:驗證邏輯改用字典結構、加節點數量上限。
  3. 寫入端:補驗證會動到主專案側的 module_frame_item_route.py,跨程式庫的修改要注意。

風險級別:🔴 高。


R3 其他資源上限(4 條 · 建議開 1 張卡)

條 是什麼
31 分頁「一頁幾筆」沒有上限(全站共用底層,兩支路徑都撞到過)
38 上傳沒有單檔大小與檔案數量上限
104 分類容器無記憶體/CPU/處理程序上限,逾時殺不掉容器(落地版跑不到)
114 手動重新掃描無條件開背景執行緒,連打幾百次就是幾百條執行緒

第 31 項最划算:改一個共用底層的預設上限,全站所有分頁一次受保護。而且它不會被既有的「請求大小上限」擋住——攻擊形狀是「請求很小、要求回傳的資料很大」。

第 114 項修法極便宜:服務裡早就有 running 狀態且有在寫入,只是沒人拿來判斷。

風險級別:中。


🅷 P 系列:要先由產品面定方向才能改程式(七組)

🔴 這一整個系列不是「要不要修」,是「要修成什麼樣」。建議合成一張決策卡請決策者一次裁完,而不是七張卡各問一次。

組 條 要決定什麼 狀態
P1 59 viewer 能不能完成/退回稽核任務 ✅ 已裁(2026-09-13):不可以,除非他就是被指派的人。修法已定案,可以直接開卡。⚠️ 修法範圍要含弱點掃描模組的八支改狀態端點(吃同一道守門,viewer 可發動帶帳密的掃描與刪紀錄)
P2 74、75 客戶的管理員該不該有權改登入安全政策/日誌轉送目的地(現況:有權,而且改到的是全公司共用的那一份) ✅ 已有第四個方向(2026-09-16):只給客戶自己的第一層租戶(總部)改,子公司/部門不給;root 租戶維持不開放。⚠️ 該案套到日誌轉送表同樣要先改表結構(那張表也只有全域一列、tenant_id 寫死 NULL)
P3 80 設備與資訊系統清冊「只要登入就能看、不分權限」這個刻意取捨還算不算數 待裁。⚠️ 與意見回饋那五顆權限點(總表 §7 第 16 項)是同一種題,建議一次裁
P4 41 讀不到自己的儲存設定時去借別家客戶的帳密,是不是刻意的「共用儲存空間」設計 待裁
P5 11、102 AI 提示注入:使用者打字就能誘導 AI 選中 26 支查詢中的任何一支(11)/證據內文可以對 AI 下指令左右分類結果(102) 待裁。102 要先定「AI 分類結果要不要當半可信」
P6 10 AI 儀表板專案清單的「視同管理員」後門——刻意設計、不是寫錯,但在「使用者打字、AI 自己決定查什麼」這個新情境下從沒重新檢視過 待裁
P7 3 出貨時預設密碼的政策 決策者已裁「先記錄、之後再看」

P1 與 P2 已經裁完了,可以直接開卡——建議把這兩組從決策卡裡拿出來,併進第二輪跟 A 系列一起做。


🅸 I 系列:規劃上互相牽動(兩組)

I1 與 CM-1559 相依(2 條)

條 牽動關係 狀態
46 兩支背景排程靠「沒有登入身分時給最高權限」撐著,收緊 CM-1559 會讓它們悄悄停止運作、不報錯 ✅ 已修(改成具名系統身分)
53 修第 53 條時要一併補 advance_stage/rollback_stage 兩處 仍在(已含在 A2)

第 46 項已修,這組實質上只剩「修 A2 時記得第 53 項的連帶」這一句話。不用獨立開卡,寫進 A2 的卡片即可。

I2 空條件回全表(1 條 · 根因)

第 70 項:六張表的資料存取層沒有一支自己加專案範圍條件——全部走共用底層「查詢欄位有值才加條件、沒值就不加」的規則。這個底層行為已經連續三次在不同套件放大成全庫外洩(檔案清單、成員名冊、任務指派)。

決策者 2026-09-15 已裁:要在底層擋,但分兩步。 第一步先盤點全站哪些地方是刻意要查全表的(碼表、排程、管理員總覽),改成明確標記;第二步才動底層。兩步之間可以隔很久。

🔴 第一步的起點已經備妥——第十二任首腦做完的 17 支盤點清單(STATE「空條件回全表:全域盤點第一版」段),下一任可以直接拿來用,不必重掃。那份清單還附了判準:看這張表的一筆資料有沒有「主人」——有主人就該拒絕空條件,沒主人(碼表、公版、全站共用字典)回全表是正確的。

建議:這件事獨立開一張卡,不要跟 A3/A4 綁在一起——A 系列走的是「逐支補檢查」那條路,不等底層改。


🅶 X 系列:收尾(三組 21 條)

X1 一行小修合卡(11 條 · 建議開 1 張卡)

這些問題彼此不相干,但都很小,一次順手做掉。

條 修什麼
54 + §3.2 13 階段推進/回退兩處 ctx.setdefault 改成直接覆寫(操作人一律以登入身分為準)+資料格式定義加白名單
81 資訊系統修改的資料格式把「停用」欄位拿掉(目前只有修改權限就能達成刪除效果)
93 問卷即時同步寫答案時「這筆是誰填的」改用登入身分,不採用前端送的名字
§3.2 12 密碼產生器少算一個字元(-4 改 -3)——目前產出的密碼可能通不過自家的密碼政策
§3.2 21 「批次新增指派」死端點:後端只有 return []、前端仍在呼叫
§3.2 24 問卷 Excel 匯入失敗時暫存檔永遠留在磁碟(刪檔那句移進 finally)
§3.2 26 正解匯入加筆數上限與型別檢查
§3.2 27 AI 分類設定的「預設廠商」「預設型號」存得進去但沒人讀
§3.2 30 一段註解寫的跟資料庫實際規則相反
§3.2 33 共用分頁查詢把跨欄位字串條件用「或」而不是「且」連接(篩選條件被放寬不是收緊)
C3 的 19/29/42/117 四條改預設值(見 C3)

建議:這張卡不要一個人做完——15 個位置散在五、六支套件裡,按套件拆成兩三批平行做比較快。

X2 死碼與未接線清除(6 條 · 建議開 1 張卡)

條 刪什麼
20 db.py:97-99 三行死碼——留著會讓人誤以為那裡有一道組織層級的權限檢查
32 開發用的輔助工具混在出貨套件裡(會把檔案覆寫成空白、載入就讀 AI 金鑰環境變數)
§3.2 14 bpmn_generator.py 三支吃檔案路徑、零呼叫者的方法——是一個已經裝好、隨時可能被拿來任意讀寫檔案的陷阱
§3.2 16、18b、23 三組「已接線但沒人用、且零權限檢查」的服務——接上去就等於憑空多出完全沒守門的入口

這組的價值不在「現在有洞」,在「留著會害下一個人判斷錯」。

修法:直接刪,或在組裝的地方加一句明確的提醒註解。§3.2 16/18b/23 那三組如果要留,要標明「啟用前必須先修第 63 項」。

X3 已修、不必開卡(4 條)

§3.2 第 7、8、9、10 項,兩次複查都確認修好了。列在這裡只為了讓 145 條每條都有交代。


Z 單修(不成組,6 條)

這些條目找不到能一起修的同伴,硬塞進哪一組都是勉強。 標在這裡是誠實的分類結果,不是漏掉。

出處 條 是什麼 建議
§3.1 28 員工被停權後登入憑證還能用約三天半,而且自己還能續到約八天 獨立開卡。這條的修法橫跨停權動作、每個請求的身分確認、資料查詢層三處,跟誰都不同組
§3.1 48 本機硬碟刪檔漏清轉檔產生的 PDF 備份(雲端那邊有清) 併進 A1 順手做(同一支套件、同一個刪檔路徑)
§3.1 106 刪除整批之後,判定結果、容器報告、含原始檔名的清單全部永久留在主機工作目錄 獨立小卡
§3.2 18 沒部門的使用者送意見回饋會失敗、畫面看不出原因 要先裁甲/乙/丙哪個修法(總表 §7 第 17 項)
§3.2 28 SHARED 範圍值三層各認一套,服務層那層是空的 總表標明「只回報待裁、不直接開卡」
§3.2 29 套件自帶建表腳本的值域還停在舊版 同上,且屬出貨基線待重產類——重不重產由決策者裁

4. 建議總表更正

本棒只寫草案,沒有動總表一個字。 下面是核對 145 條時發現的對不上的地方,由首腦裁定後另派一棒改。

# 在哪 現況 建議
1 §3 標題 寫「還沒開卡的(141 項)」 §3.1 實有 117 列 + §3.2 實有 28 列 + §3.3 實有 2 列 = 147;扣掉 §3.3 那兩條(是掃描本身的補洞、已有卡)= 145。標題數字與小節標題都要重算
2 §3.2 標題 寫「非資安但是真 bug(26 項)」 實際列了 28 列(編號 7~33 連號,另有一列 18b)
3 §0「問題累積」表 寫「掃到了但還沒開工單的問題 143 項」 與 §3 標題的 141、實際的 145 三個數字互不相同
4 §7 第 12 項 STATE 已於 09-15 標註「總表 §6 第 12 項尚未改成已裁,下一任補」 該註記至今仍在,這件事已經掛了五天
5 §7「已裁決」末段 「等全部套件掃完再統一開卡」與「三個例外也不先做」(09-19 裁) 掃描已於 09-20 收口,這兩條的前提消失了。建議請決策者重新確認 C2 的第 83/84 項(撤銷權杖、清公開站密碼)要不要立刻處理——那兩件不占開發時間
6 §3.1 第 34/40/47 項 原文仍寫「資料表沒有客戶歸屬欄位、資料庫隔離關閉」 已過期(FR-107 為了別的目的補上了)。總表自己在複查段已經警告「開卡前原文必須改寫」,但原文本身還沒改

5. 這份草案沒有做的事

寫清楚免得下一棒誤會已經做過:

  • 沒有開檔重驗任何一條。狀態一律以總表自己的兩次複查為準;複查沒覆蓋到的照總表寫「仍在」。
  • 沒有改總表、風險總表、STATE 任何一個字。
  • 沒有開任何 Notion 修正卡——決策者要先看草案再裁。
  • 沒有推翻決策者已裁的修法方向(P1 viewer、P2 第一層租戶、CM-1559 順序、CM-1595 維持最嚴重等)。草案裡凡是已裁的,都照裁定寫。
  • 沒有估工時。每組的「涵蓋幾支套件幾個檔」可以當成規模的參考,但沒有換算成人天。
  • 沒有判斷 jedi 套件的發版節奏。草案只標出哪幾組動到套件(改了要發版才生效),實際怎麼排發版是另一件事。

6. 座標

要什麼 去哪
每一條屬於哪一組、有沒有漏 classification-matrix.md
每一條的完整技術細節 README.md §3
按風險等級排序的視角 risk-overview.md
現況與已裁決事項 FR-075/handoff/security-scan-STATE.md
本卡 CM-1983