FR-085 · 需求索引 · 本頁由 build 掃資料夾生成

FR-085 共用地基套件(jedi-common)資安檢查

三棒全部掃完(2026-09-11)。C1(授權鏈核心 29 檔)、C2(log 全鏈 32 檔)已掃完並驗收;C3(輸出面與打包 30 檔)已掃完並驗收——工具 4 條(3M 1L,18 票全一致通過)+ runner 人工 6 條,stamp verified。最高投報率為「安裝套件走不加密連線」(跨全 monorepo 25 支)

狀態:✅ 已完成 文件 3 份

🔴 一頁看完

2026-09-11 arc 收口:三棒 91 檔全部掃完並經首腦驗收。jedi-common 這支「25 支套件共用的地基」至此首次有了完整、算數的掃描(先前只有一次未經面板的半途掃描)。

三棒合計 22 條(2 HIGH/9 MEDIUM/6 LOW,另 5 條查證為無問題或死碼)。依決策者裁定修正卡一律不開,全部登記進跨 arc 總表 §3,之後從那裡挑。

投報率最高的三條:① 補一行 RUN_ENV=prod(C2-5,決定密碼進 log 那幾條的影響範圍是開發機還是客戶機)/② 分頁加上限(C3-2,一行)/③ 密碼遮罩改用 JSON 解析(C3-3,動的是全站唯一那道遮罩)。

開卡時的六個疑點,三條成立三條被推翻——can_manage_orgs 無條件為真是死碼、log 操作者可偽造是死碼、信封失敗回例外訊息進不去。懷疑清單有一半會被證偽,這是健康的。

C1 掃完了。結論:只要一個請求「還沒有登入身分」,資料庫的客戶隔離就整個關掉——而這不是罕見狀況,登入端點本身、以及一個完全沒有守門的 Google Drive 通知端點,每次都處在這個狀態。

這個洞就是既有的 CM-1559,本棒的新資訊是「它比原本記錄的更好觸及」。另找到 SQL 錯誤原文直接回前端(新),以及主產品 Redis 連線不驗憑證——那是 CM-1565 在 jedi-iam 修好、但主產品這份漏網的複製品。

C2 也掃完了。結論:使用者的登入密碼與登入憑證,目前是原文寫進伺服器 log 檔與資料庫的——而產品另一條 log 路徑明明有遮罩功能,就是這條沒用上。連帶查出出貨設定漏了一個環境變數,客戶正式機實際跑的是開發用 log 設定(一行可修,但它決定前面幾條的影響範圍是開發機還是客戶機,投報率最高)。

首腦在 DEV 實查複核:存 log 的那張表完全沒有客戶隔離(沒開、0 條規則、連可用來隔離的欄位都沒有、目前 66 萬筆),而且另一張 log 表也一樣

一個好消息:卡片原本認為「最重要」的那條(log 上的操作者可被偽造)不成立,那段程式碼從未被啟用。

C3 掃完了,三棒到此全部結束。結論:我們安裝套件走的是不加密的連線,有人插進內網那條線就能在安裝時掉包惡意程式——直接拿下開發機與打包機,而打包機產出的東西是要送到客戶手上的。這個設定25 支套件與主專案各有一份,建議開成跨 monorepo 的獨立卡。

第二件:全站唯一那道密碼遮罩,遇到含雙引號的密碼會遮一半、剩下半截原文寫進資料庫,而那張表留 90 天——越強的密碼越容易踩到。

另外查清一件卡片指定要回答的事:AI 儀表板回傳夾帶密碼鹽值(FR-083 D1 F2)是個案不是系統性問題——那支序列化工具全 monorepo 只有 jedi-ai-dashboard 2 處在用,主專案與其餘 24 支套件都是 0 處,建議修呼叫端不要動套件

C3 的好消息同樣是卡片列為「本棒最重要」的那條(信封組裝失敗把錯誤原文回前端)不成立——那段程式進不去。

📄 C1 報告C2 報告C3 報告


C2 也掃完了。結論:使用者的登入密碼、以及每個請求的登入憑證,目前原文寫進伺服器 log 檔與資料庫——而產品另一條 log 路徑(寫 api_logs 那條)明明有遮罩功能,就在同一支檔案往下 5 行,就是這一條沒用上。這是本專案第三次同型問題(前兩次 CM-1606、FR-076 L3),差別在前兩次是個別功能寫錯,這次是每一個請求都會發生

三件放大它的事:system_logs 表完全沒有客戶隔離(DEV 實查:RLS 關閉、0 條規則、66 萬筆,且表裡連 tenant 欄位都沒有,想開也開不了);② 出貨設定漏了 RUN_ENV,客戶正式機實際套用的是開發用 log 設定——而正式設定裡本來根本沒有這條寫 DB 的路徑;③ 完整錯誤堆疊也進同一張沒隔離的表。

好消息:卡片原本認定「本棒最重要」的那條(log 操作者取自 X-UserId 可偽造)不成立——那兩支程式從未被啟用,是死碼,工具三票一致擋下。這正是三人面板的價值:沒有它,會有人花時間去修一段沒在跑的程式。

📄 C2 完整報告C3 完整報告

§1

這在做什麼(白話)

jedi-common 是所有 jedi 套件與主產品共用的底層:

  • 每一次資料庫存取都經過它的 session_scope,它負責告訴資料庫「現在是誰在查、能看哪些客戶的資料」。這就是 RLS(每個客戶只能看自己資料的隔離機制)的注入點。
  • 每一個錯誤回給前端的格式由它決定。
  • 每一行 log 怎麼寫、寫到哪裡也是它。

它出問題等於 25 支全中。 這個 arc 要回答:資料庫隔離會不會被繞過或注入、錯誤訊息會不會把內部細節洩給前端、log 會不會把密碼或可偽造的身分寫進去、回給前端的資料會不會夾帶不該有的欄位。

§2

為什麼掃它

  • 至今沒有一次算數的掃描。 只有第一輪 medium 半途喊停、未經面板的紀錄。跨 arc 總表從 FR-081 起就列它為第一優先。
  • 已知洞 CM-1559 的根就在這裡(RLS fail-open,無 user context 時預設 super admin),但那張卡只盯一個點,整支套件沒人看過。
  • 宿主 662 處 import。 session.database 201、handler.exception 184、session.auth 96、utils.response_util 78,爆炸半徑是全站。
§3

怎麼切三棒(按業務功能垂直切,不按目錄大小平均切)

範圍 檔數 為什麼獨立一棒
C1 授權鏈核心 session/identity/handler/ 29 唯一能造成「跨租戶看到別人資料」的區塊。RLS 注入、身分 contextvar、例外洩漏互相咬合,拆開會漏掉合流
C2 log 全鏈 logger/ 32 唯一一條「資料離開 process」的路。自成 DDD 四層、零耦合,看的是「什麼被寫出去」而非「誰能讀」
C3 輸出面與打包 interfaces/utils/enums/constants/.envpublish.sh 30 純資料整形+打包設定,共同問題是「什麼會被序列化回前端」

接縫handler/handler.py(C1)與 utils/response_util.py(C3)是「例外 → 信封」的兩端,兩卡互相允許越界讀對方那支。mark_password(C3)是 C2 要追的遮罩層,C2 允許越界讀。

§4

首腦讀碼後的起點(未驗證,寫在各卡「重點看什麼」)

偵察由唯讀 subagent 做,關鍵行號首腦逐一開檔核過:

看到什麼 為什麼可疑
C1 RLS 的 session 變數用 f-string 拼進 SQL(db.py:95:108:113),沒有 bind parameter 值若能帶單引號就能脫逸再接 SET LOCAL app.is_super_admin = 't'。獨立於 CM-1559 的注入面
C1 每個登入者都被無條件設 app.can_manage_orgs = 't'db.py:98 靠這個變數判斷的 policy 對所有人放行
C1 SQL 例外原文第一行直接回前端(sql_exception.py:37-38 表名、欄位名、約束名外洩
C2 log 上的操作者取自 request header X-UserIdmiddleware.py:13),BE 與 FE 都沒有地方設它 任何人送 X-UserId: admin 就能讓稽核紀錄寫成別人做的
C2 完整 traceback 寫進 system_logs 表(db_handler.py:14-16),基線 schema 看不到該表的 RLS policy 誰能讀這張表要實查
C3 信封組裝失敗把例外訊息當 data 回前端(response_util.py:26 全站信封的最後一道
C3 page_size 無上限(schema/common.py:13 小輸入、大輸出,CM-1587 的 body 上限擋不住
C3 utils/gen_comment.py 帶 OpenAI key、用未定義變數開檔覆寫原始碼、吞光例外 一支開發工具混在 runtime 套件裡,零 importer
§5

進度

面板 發現 報告
C1 授權鏈核心 CM-1647 29 ✅ 24 票全投、verified 5 條:2H(同一個洞)+2M+1 無問題
(工具 3 條,2 條人工查證)
報告
C2 log 全鏈 CM-1648 32 ✅ 12 票全投、verified 7 條:1H(密碼/憑證原文進 log)+4M+2L
工具正式 0 條,4 候選全否決;7 條全為人工查證)
報告
C3 輸出面與打包 CM-1649 30 ✅ 18 票全投、verified 10 條:4M+2L+4 無問題
(工具 4 條:3M+1L,6 候選中 2 條被否決;
另 6 條人工查證,其中 4 條查完確認無問題)
報告
§6

修正卡

(掃完驗收後才會有。依決策者裁定,修正卡先不開,發現登記進跨 arc 總表 §3。)

§7

座標

  • 套件:~/Projects/Jedicogy/module/jedi-python-package/jedi-common/session/database/base_repository.py 是舊名轉發殼、真身在 session_mixin.py
  • 已知前案:CM-1559(RLS fail-open,C1 基準線)、CM-1598(.env 受版控,C3 基準線)
  • 首腦手冊:.claude/skills/security-scan-lead/SKILL.md
  • 跨 arc 總表:security-scan-consolidated/
  • 現況:FR-075/handoff/security-scan-STATE.md
§8

文件

以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。

其他文件

文件 類型 標題 最後更新
scan-C1-authz-coremd 文件 FR-085.C1 掃描報告:授權鏈核心(session/RLS 注入/身分/例外處理) 2026-09-10
scan-C2-loggermd 文件 FR-085.C2 掃描報告:log 全鏈(寫什麼、寫到哪、身分怎麼標) 2026-09-10
scan-C3-output-and-packagingmd 文件 FR-085.C3 掃描報告:輸出面與打包(jedi-common) 2026-09-11
§9

Notion 卡

卡片內容(決策紀錄、驗收條件)以 Notion 為準,本頁只記座標。

關係 卡號 標題 狀態
母案 CM-1646 FR-085 共用地基套件(jedi-common)資安掃描(三棒,只掃不修)
子卡 CM-1647 C1 授權鏈核心:session/RLS 注入/身分/例外處理(29 檔) 修正待驗證
子卡 CM-1648 C2 log 全鏈:寫什麼、寫到哪、身分怎麼標(32 檔) 修正待驗證
子卡 CM-1649 C3 輸出面與打包:回應信封/分頁/序列化/共用 util/.env(30 檔) 修正待驗證