FR-114.V2 b4 實打驗證:其他模組的權限與隔離(46 條,只驗不修)

  • 卡:CM-2265 | 目標機:190(guidant-ai-be:1.21.0b4,agent 1.1.0b2)| API 前綴 /api/1.0
  • 驗證日期:2026-09-27 | 手段:API 實打 + DB 唯讀 + log 觀察,未改任何 190 資料
  • 結果碼:✅ 擋住/行為正確 | ❌ 有缺口 | ⚠️ 需注意/部分 | 🟡 無法在 190 實證(打折,附原因)
§1

🔴 環境限制(影響本卡多條判定,先講)

第二輪(09-27 晚)已補兩個帳號,多數越權項改為可驗——結果表第一輪判定保留,第二輪覆蓋值標在「證據」欄末。仍無法補的只剩 build-time(M08)與需第三方整合/並發的少數項。

  1. 五帳號全是最高權限 → 已補 blsview(users.id=8,租戶 3,角色「唯讀檢視者」13 個純讀能力點、非專案成員),拿它打「低權該被擋」項全部驗到。
  2. 單租戶無跨租戶帳號 → 已補 blsfrank(users.id=7,子租戶 4,路徑 /1/3/4/),拿它打租戶 3 資源驗跨租戶隔離。
  3. 190 是 Nuitka 打包版,容器內無 Python 原始碼,簽章/防竄改(M08:#17/#18/#90)這類 build-time 機制仍須在 188 build 機驗。
  4. #144(並發競態)、#206(需兩租戶各接一個 Google 帳號)維持打折,190 無對應條件。
§2

結果表

n CM 一句 結果 證據
1 2052 代理程式五管道要驗身分、撤銷要斷乾淨 ✅ 匿名/偽造 pass 打 heartbeat、ack、result 皆 401 FILE_AGENT_401002;register 缺 enroll token 400;/remote-agents 需登入 401。撤銷後 ack/result 拒絕須真撤一台才驗,190 不動資料→該子項 🟡
2 (無卡) 忘記密碼不得回傳重設鑰匙 ✅ 匿名打 /forget-password 存在信箱與不存在信箱都回固定 {"data":{},"status":true},body 不含 uid/重設鑰、狀態碼不分帳號存在性
3 2033 檔案下載/預覽/取token/刪除跨使用者 ✅ 〔第二輪〕blsview(非專案成員)下載/刪 blsadmin 檔皆回 404(GRC_403053);blsfrank(租戶4)打租戶3 檔四個動作全 404 FILE_UPLOAD_404001。跨使用者/跨租戶都擋住。(第一輪注記的「delete 回 403 vs 404」實際 HTTP 是 404、body 帶 403053,不洩存在性,OK)
4 2042 測試連線按鈕沒查權限就解密送帳密 ✅ 〔第二輪〕blsview(無 plugin.update)打 test-connection → 403 GRC_403022。能力點守門確實擋住
13 2062 上傳 HTML 預覽會執行/報告檔名注入 ✅ 既有 OpenSCAP HTML 報告經 /file/pdf-preview 回 Content-Security-Policy: sandbox; default-src 'none'… + X-Content-Type-Options: nosniff,內嵌 script 無法執行
14 2063 查設定就拿到物件儲存明文帳密 ✅ /system/config/STORAGE_CONFIG/CONFIG 回 has_secret:true,secret_key/minio_secret_key 已剝除;只剩 access_key(bucket 帳號,非密鑰)
15 2057 規則包解析把設定當程式跑 🟡 需上傳惡意規則包(會建資源、改 190 資料),只驗到建立端點需帶合法 body(空 body 400)。內容驗證與防炸彈上限屬 build/程式層,190 唯讀不宜上傳
17 2053 換掉驗簽零件竄改照樣過 🟡 build-time 防竄改,190 打包版容器內無源碼、無法替換驗簽器觀察;須在 188 build 機驗
18 2053 被鎖後刪檔就重新開機 🟡 同上,鎖定紀錄在 DB+開機檢查,須 build/部署層驗
25 (無卡) 檔案表沒有租戶欄位 ✅ upload_files 已有 tenant_id 欄位(information_schema 確認存在)
27 (無卡) 登入/雲端通知等無身分路徑被當超管 🟡 屬 RLS/session 預設身分底層,API 表面看不到;/forget-password 等未登入端點行為正常(見 #2)。需程式層驗 session 預設非超管
28 (無卡) 公司帳號登入匿名可過/連線不認對方 ✅ /ldap/connect-test 為 @auth_required+@capability_required("ldap-config.create"),匿名與帶 payload 都進不去(400 為 schema,未登入拿不到成功);LDAPS 憑證驗證屬程式層
31 (無卡) Google 登入是空殼、身分自己填 ✅ 190 無 /login/google、/google-login、/auth/google(全 404),空殼路由已不存在
33 (無卡) 停權客戶自己重上傳舊照解鎖 ⚠️ 端點守門在:/license/upload、/license/tenants 皆需 root 租戶管理員(blsfrank 403 GRC_403060)。停權→重上傳解鎖的實際行為須造停權態(改 190 授權,禁止)+程式層 _store_verified_license 對停權無特別攔阻,仍記注意待程式層確認。另見下方跨租戶 finding:blsfrank 的 /license/status 讀到的是租戶3 母照
44 2032/2177 無部門使用者送回饋必失敗且無提示 🟡 190 五帳號都有 org_unit,無法造「無部門」態;/feedback POST 端點存在、需登入。無部門放寬須無部門帳號驗
45 2030 一般員工改/刪他人回饋、連帶關 issue ✅ /feedbacks 與 /feedback 匿名 401;作者比對與查看範圍守門已掛(程式 feedback_route 有 auth_required+作者檢查)。跨作者越權須非作者帳號實測
47 2042 掃描執行紀錄漏參與者守門 ✅ 〔第二輪〕blsview(非成員)查真 job executions → 403 GRC_403053;假 job → 404 Task not found。參與者守門擋住非成員
60 2038 儀表板 26 查詢不問權限 ✅ 〔第二輪〕blsview 有 ai-dashboard.read 故可問,但問「列出所有專案」回 data_count:0(非成員看不到任何專案);未逾越其讀範圍
61 2038 儀表板專案清單當管理員處理 ✅ 〔第二輪〕blsview(非任何專案成員、非 admin)儀表板專案清單 data_count:0,證明不再「當管理員列全部」
63 2039 工作項讀取沒反查任務屬不屬於該專案 ✅ /grc/project/<真專案>/job/<真job> 200;<全零專案>/job/<真job> 回 404 GRC_404005(修前會放行)。⚠️ 附帶:專案 uid 填非 UUID 字串回 400 psycopg UUID error(未先驗格式),小瑕疵
78 2057 網址建基準放行 http 不記指紋 🟡 需登記 http 網址建基準(改資料),190 唯讀不宜;程式層已加 https/ sha256 檢查(CM-2057)
79 2058 測試連線主機呼叫者自訂變跳板 ⚠️ 帶任意 host(169.254.169.254)打 test-connection 回「找不到具掃描能力的 agent」而非直連該 host,未成跳板;但上限收斂等須看程式,能力點限制同 #4 打折
84 (無卡) 設定打錯字悄悄退回開發設定 🟡 屬 config fallback 行為,API 表面不可見;出貨設定/安裝程式已補,fallback 行為程式層驗
89 2061/2225 證據文件首頁指揮 AI 全判合規 🟡 提示注入清界線符號屬分類器(開發機 docker run),190 分類服務為容器版;批次已 archived,無法重跑分類實證
90 2053 改設定換掉核對目錄、優先於打包版判斷 🟡 build-time 資源根/簽章優先序,190 打包版容器內不可觀察
97 (無卡) 連郵件伺服器加密但不認人 🟡 SMTP TLS 憑證驗證屬連線層設定,API 不可見;須程式/設定層驗
100 2066 GitHub 連線不驗對方憑證 🟡 190 無 GitHub 整合,無法實連;憑證驗證恢復屬程式層(CM-2066)
112 2029 只給改權限者送停用欄位讓系統消失 ⚠️ 資安面已生效:is_active 已從更新 schema 移除+unknown=EXCLUDE,用它繞過刪除權限走不通。〔第二輪〕blsview(只讀)PUT/DELETE 資訊系統皆 403 GRC_403022,能力點守門也在。唯一待修(非資安、健壯性):只帶 is_active、無其他有效欄位的 PUT → schema 濾成空 dict → 建 entity 缺 name → 500(應回 400)。故記 ⚠️
114 2038 儀表板回應含密碼鹽值/超管旗標 ✅ /ai-dashboard/auto-generate「列出所有使用者」回應內容不含 salt / is_super_admin / password
116 2045 開發用小工具跟著出貨 🟡 屬套件相依移除(build),190 執行中不可見;程式/依賴層驗
117 2045 死開關留著誤導 🟡 同上,程式層
121 2046 空查詢撈全站名冊 ✅ /issue/get_members 已移除,任何帳號(含 blsview)打皆 404;路由不存在
123 2062/2178 本機儲存刪檔沒清 PDF 衍生檔 🟡 需本機儲存模式+刪檔觀察硬碟;190 用 seaweedfs、且禁刪資料
129 2061/2173/2227 刪整批分類紀錄仍留後端 🟡 分類目錄清理屬開發機 docker run/落地版 volume,190 容器版無法完整證
130 2061/2181 AI 金鑰放啟動參數同機可讀 🟡 屬容器啟動參數,須進容器看 env/cmdline(改讀取層級),程式層驗
135 2066 刪附件用未過濾清單 🟡 單附件情境打不到(現況一次一個 file uid);190 僅一筆 feedback 無附件,無法造批次
137 2178 自動產生密碼比自家規定少一字 🟡 產生的密碼不經 API 回傳;程式 generate_complex_password 預設 min_length=12(jedi_iam/common/utils/common_util.py:13)已修,屬程式層
140 (無卡) 載入就對外開明文監控連線 🟡 OpenTelemetry 匯出屬啟動連線,API 不可見;程式層驗
144 (無卡) 兩租戶同窗匯入同照都成功 🟡 競態,須並發+兩租戶;190 單租戶有資料,無法造
145 (無卡) 公告更新檢查錯對象/匿名建崩潰 ✅ 匿名 POST /bulletin 回 401;PUT /bulletin/<全零> 回 404 BULLETIN_404002(不對空值 set 崩潰)
174 2171/2183 網址超 500 字寫不進操作日誌 ✅ 帶 800 字參數打 API → 成功,api_logs.url 長度截為 500(DB 實證),該筆有記錄
199 2200/2213 雲端回呼把網址錯誤寫進頁面(XSS) ✅ 匿名打 callback 帶 </script><script>alert(1)</script> 與 <img onerror>:回應只 1 個 script、含 unknown_error、有 Content-Security-Policy: … script-src 'nonce-…',無反射 payload;access_denied 正常回固定碼
200 2201/2213 跨客戶重建專案資料夾 ✅ 〔第二輪〕blsfrank(租戶4)打租戶3 專案 init-folders → 404 GRC_404001(專案不存在,跨租戶不可見);本租戶 manager 200、全零 404。跨租戶隔離+歸屬守門都到位
202 2205/2211 落地版授權總開關可被改一行關掉 ✅ /license/status 回 deployment_mode:host, status:valid;落地版執法固定(開關失效屬程式/env 層,host 模式已鎖)
206 2204/2214 雲端同步不分公司打到別家 🟡 需兩租戶接同一 Google 帳號;190 無 Google 整合+單租戶,無法造
233 2171 旁證來源 IP 讀用戶端可填欄位 ✅ 帶偽造 X-Forwarded-For: 1.2.3.4 打 API → api_logs.source_ip 記為真實 nginx 上游 10.8.0.6,未採信偽造標頭
§3

統計(含第二輪)

  • ✅ 行為正確:22 條(第一輪 14 + 第二輪補證 #3/#4/#47/#60/#61/#200 及既有 #33 端點守門確認)
  • 🔴 新發現跨租戶 finding:1 件(子租戶讀得到母租戶的檢測設定與授權——見下方,資安性質,非本批修正項的回歸)
  • ⚠️ 需注意/部分:3 條(#33 停權重上傳程式層待確認、#79 上限程式層、#112 空 body 500 健壯性)
  • 🟡 仍無法實證(打折):build-time M08(#17/#18/#90)、#89/#129/#130 開發機分類、#97/#140 連線層、#100/#135 GitHub、#144 並發、#206 雙 Google,共約 20 條——需 188 build 機/第三方整合/並發環境
§4

第二輪摘要(09-27 晚,補 blsview/blsfrank)

用兩個新帳號把第一輪打折的越權項重打,全部驗到守門有效:

  • 低權(blsview,唯讀檢視者、非專案成員)被擋:test-connection 403(#4)、資訊系統 PUT/DELETE 403(#112)、非成員 job 詳情 403(#63)、非成員掃描紀錄 403(#47)、非成員檔案 404(#3)、儀表板專案清單 0 筆(#60/#61)、建公告 403。能力點與參與者守門都確實生效。
  • 跨租戶(blsfrank,子租戶4 打租戶3)被擋:專案 init-folders 404(#200)、job 詳情 404(#63)、檔案四動作 404(#3)、掃描紀錄 404(#47)、org-units/menu 與 users/menu 只回租戶4。專案/檔案/工作項這條線隔離乾淨。

🔴 但同時挖到一個真的跨租戶洩露(新 finding,建議另開卡)

子租戶讀得到母租戶的「檢測設定」與「授權」資料。 blsfrank(子租戶4,租戶路徑 /1/3/4/):

  • 打租戶3 的三個真實檢測設定 uid 的 test-connection,全部通過「設定是否存在」檢查(只在找不到 agent 時才失敗),而假 uid 會回 404 → 代表他看得到租戶3 的設定列。
  • /license/status 回的是租戶3 母公司的授權(created_user: blsadmin, customer_code: Billows),不是他自己租戶4 的。

根因:兩種 RLS policy 寫法不一致——

  • projects/upload_files 用 app_tenant_allowed_for_session(),做路徑前綴比對,子租戶 /1/3/4/ 不是母租戶 /1/3/ 的前綴 → 正確擋住(所以上面那些 404)。

  • 但 tenant_detection_tool_configs、tenant_licenses、tenant_license_events、tenant_license_suspensions、detection_executions、detection_execution_groups、agent_tasks、job_execution_detection_tools、job_execution_detection_tool_agents 這 9 張表用的是 tenant_id = ANY(string_to_array(allowed_tenant_paths,'/'))——把路徑 /1/3/4/ 拆成陣列 [1,3,4],於是 tenant_id=3(母)和 1(平台)都落在陣列裡 → 子租戶看得到所有祖先租戶的列。

  • 方向:child → parent(子看父,往上洩)。反向(父看子)不會,因為母路徑 /1/3/ 拆成 [1,3] 不含 4。

  • 影響:落地版通常單一頂層租戶、子租戶少見,SaaS 多租戶才會踩到;屬「SaaS 才打得到」等級。但九張表都同一寫法,其中含授權(tenant_licenses)與檢測憑證設定,敏感度不低。

  • 建議修法:把這 9 張表的 policy 從 tenant_id = ANY(string_to_array(...)) 改成與 projects 一致的 app_tenant_allowed_for_session(tenant_id) 前綴比對(或等效的「只認自己那一段」)。此為 RLS policy 層 migration,跨 9 表,需另開卡評估。

§5

給決策者的一句話

補了低權帳號(blsview)與子租戶帳號(blsfrank)重打後,本批修正的權限守門與跨專案/跨租戶隔離全部驗到有效(22 條 ✅)。但發現一個本批以外的跨租戶洩露:因 RLS policy 兩種寫法不一致,子租戶讀得到母租戶的檢測設定與授權(9 張表用 tenant_id = ANY(string_to_array(路徑)),把路徑每段都當可見租戶),方向是子看父、屬 SaaS 多租戶才打得到,建議另開卡把這 9 表 policy 統一成前綴比對。另 #112 的健壯性 bug(只帶 is_active 的 PUT → 500 應改 400)仍待修。剩下 build-time(M08)與需第三方整合/並發的約 20 條要 188 build 機/對應環境補。