本卡為 FR-096 母案:掃 jedi-system-core 這支套件與它在主專案的接線,三棒共 82 檔,只掃不修。子卡清單在末段。
這支套件管的是「系統設定」與「選單字典」兩張表。設定表裡存的是寄信伺服器、LDAP 目錄服務、物件儲存這些外部系統的連線資料——包含帳號與密碼。選單字典存的是前端下拉選單的選項文字(例如「啟用/停用」)。
前面幾支掃的是「誰能看到誰的資料」。這一支的風險形狀不一樣:這裡存的不是客戶資料,是系統自己的鑰匙。一把寄信伺服器的密碼外流,攻擊者就能用公司名義寄釣魚信;一把物件儲存的密碼外流,所有客戶上傳的檔案都在他手上。而且改設定是全站生效的總開關——改掉 LDAP 伺服器位址,等於把全公司的登入驗證導去攻擊者的機器。
① 這支是「起手式五支」之一(common/iam/log/system-core/notification),每個新客戶裝上就有,出問題是全產品範圍。
② 總表已經登記了好幾條「密碼被寫進日誌檔」與「儲存設定的帳密沒遮罩就回給前端」的問題(FR-085 C2、FR-086 B2-A),源頭都是這張設定表。這一棒要把源頭那一段查清楚,而不是繼續在下游一條一條撿。
③ 主專案側有一支名字就寫著「守門版」的服務(guarded_system_config_service.py)——名字說它擋了什麼,那就要查「有沒有路徑繞過它」。首腦開卡時已經查出四處繞過,見下一段。
以下都是首腦 2026-09-15 親自開檔看過的,行號與內容屬實。不是要 runner 重新確認這些,是要他從這裡往外追:
GuardedSystemConfigService,讓所有使用者拿到同一個過濾後的視圖。但 di_containers/auth/auth_containers.py:180、notification/notification_containers.py:20、user_change_password/user_change_password_containers.py:46 三處組裝的是套件原版 SystemConfigService,login_containers.py 則直接接 repo。這三支的 consumer 是登入、寄信、改密碼——都是會碰到 SMTP 密碼的路徑。這是「單一收口點」設計被繞過的教科書形狀。jedi_system_core/app/service/system_config_service.py:20 一行 logger.info(f"Get system configs: {configs}") 把 entity 整包塞進 log;設定列的 value 是 JSON,SMTP 那列裡面就是 {"secret": "真密碼"}。選單那半也有同款(system_menu_service.py:22,但選單無機密)。這是總表「密碼寫進日誌」那幾條的源頭表,要交叉對照(FR-085 C2 的 HIGH)。plugin/contract.py:61 的遮罩清單只有 SMTP/第三方登入/通知/議題整合四組,沒有 STORAGE_CONFIG;第 65 行的機密欄位名只有 secret 與 private_token,沒有 access_key/secret_key(那正是物件儲存帳密的欄位名)。FR-086 B2-A 已從上傳那一側報過這件事,這一棒要從名單這一側確認是不是同一個根因,以及「名單是凍結的對外契約」這個設定要怎麼加。core/plugins/system_core.py 的 _GROUP_CAPABILITY_RESOURCE)。表裡沒有的分組會退回一個誰都沒有的權限而永遠擋下。要看的是反面:有沒有哪個分組被映到太寬鬆的權限,以及 _ACTION_ALIAS 把通知設定的「新增/刪除」都映成「修改」(system_core.py:100 附近)會不會讓「只能改」的人其實能刪。system_menus 沒有租戶欄、沒有隔離、(分組,鍵) 全域唯一,任一客戶改一筆就是改到全站,所以管理端點要求必須是平台管理員。要查的是:這四個守門是不是真的都掛上了、有沒有哪條漏掉(套件 plugin/assembly.py:17 有一道「缺守門就拒絕掛載」的檢查,要確認它真的擋得住)。infra/system_config/system_config_root_reader.py 的 60/96/122/179/242/300 行各一句 SET LOCAL app.is_super_admin = 't')。這些是為了讀寫「總部那一列」共用設定而刻意繞過隔離的,設計上有理由;但 FR-086 B2-B 已經證明同款樣板被上傳服務拿去當「讀不到就借別人的」退路而出事。要逐支看繞過的範圍有沒有收窄到只讀該讀的那一列。system_menus 這張表沒有隔離,是正確的,不是漏洞。 首腦 2026-09-15 對 DEV 實查:這張表 rls=false、0 條規則、共 74 筆,內容全是下拉選單的碼表(USER_STATUS/ENABLE_STATUS/ANS_TYPE 這類),沒有客戶資料、沒有個資。選單字典是全站 UI 的資料來源,隔離起來會壞掉所有頁面。掃到請不要當發現報。
但有一件相關的事要記一句:查選單的請求格式(api/serializers/system_menu.py:25-31 的 SystemMenuQueryRequest)六個欄位全部非必填,送一個空的查詢條件就會回全表。以這張表的內容而言影響有限,但它屬於首腦正在盤的「共用底層送空條件就回全表」那條線(總表 §6 A 組第 15 項,已經是第三次撞到)。報告裡寫一句、標明歸那條線即可,不要當獨立發現升級。
DEV 另一半的實查數字(給對照用,未經面板驗證):system_configs 有隔離(rls=true、4 條規則),共 23 筆分佈在 6 個客戶,分組是 ISSUE_INTEGRATE_CONFIG 2/NOTIFY_CONFIG 2/RUNTIME_CONFIG 7/SMTP 2/STORAGE_CONFIG 7/THIRD_PARTY_LOGIN 2/WEB_IDEL_CONFIG 1。其中 6 筆的密碼欄位是有值的(不是空字串),也就是說這不是空表,真的有東西可偷。