FR-114.V1 b4 實打驗證報告——權限/隔離:問卷、稽核流程、任務成員、合規文件、稽核輪次(59 條,只驗不修)

  • 卡號:CM-2264(母卡 CM-2019)
  • 目標機:190(BE guidant-ai-be:1.21.0b4/FE 同版/agent 1.1.0b2,九容器 healthy)
  • 日期:2026-09-27
  • 方式:API 直打(/api/1.0,自簽憑證 -k),五角色各拿一把 token 對同一資源分別打,記 HTTP 碼+error_code。
  • 只讀不改:全程未寫 190 任何資料;唯一一次誤觸(見下「事故與還原」)已當場還原。
§1

190 環境實況(影響判讀,先讀)

  1. 只有一個租戶-3 專案(07f4dcab…,in_progress,living SSP id2),五個租戶-3 帳號全部是該專案 participant,且全部掛 System Manager(滿權限)。因此 190 上做不出「同公司但非本專案成員」也做不出「無能力點」的帳號——除非建第二專案(見下)。
  2. admin(users.id=1)是 is_super_admin=t、租戶 1。它繞 RLS、可讀跨租戶,但不會自動加入專案——對專案 participant 守門一律吃 403 GRC_403053(實測 #52/#57 證實)。所以 admin 是「專案非成員攻擊者」的有效替身(對 project-participant 類守門),但不是「跨租戶 RLS 讀取攻擊者」(它本來就看得到全部)。
  3. 問卷相關表(surveys/task_surveys/question_answers/survey_discussions/histories)在 190 全空(0 筆)。M05 有填答資料才驗得到的洩漏,只能驗到「空請求/壞輸入」的必填參數與歸屬那一層。
  4. BE 是 Nuitka 編譯,容器內無原始碼可讀;route_capabilities 是 FE 選單用、非 BE runtime 守門來源,無法用它反推守門是否上線。
§2

事故與還原(照實記)

第一輪誤把 blsemp1 當「攻擊者」對留言 CM1(2f602555…)送 PUT,結果 200 改成了「x」。但 blsemp1 其實是專案 1 成員、且是該留言作者——這是合法編輯、不是破口。已立即 UPDATE 還原內容為原值「評論一下」,收工前再查一次確認三筆留言內容原封不動。此後改用 admin(非成員)當攻擊者、blsadmin(manager)/blspan(auditor)/blsshino(reviewer)/blsemp1(viewer, 但仍是成員) 當角色梯度。

§3

結果表(n=SUMMARY 條號;結果 ✅過/❌破/⚠️打折或無法驗)

n CM 一句 結果 證據
5 2034 填答歷史明細空請求讀全公司 ✅ 【三輪】POST /project-survey/history-details(task_survey_id=1):blsview(非成員)→403 GRC_403053、blsfrank(跨租戶)→404、blspan/blsemp1(成員)→200。歸屬守門生效
6 2034/2038 拿問卷編號讀別部門答案 ✅ 【三輪】GET /project-survey/answers/<ts_uid>:blsview→403 GRC_403053、blsfrank→404 TASK_SURVEY_404005、成員→200。歸屬守門生效
7 2035 拿任務編號讀別家證據清單 ✅ GET /job-evidence/<ev>:admin(非成員)→403 GRC_403053;成員讀正常。歸屬檢查生效
8 2035 讀/塞別人專案留言 ✅ GET /grc/job-executions/<je>/comments admin→200 空(該 je 無留言,非破口);PUT /grc/job/comment/<cm> admin(非成員)→403 GRC_403003 無權限操作此留言。歸屬+作者守門生效
40 2031 檢查流程圖端點無權限無大小限 ⚠️ POST /flow-engine/flow-templates/validate 我構造的 xml 一律被 400 COMMON_400001 擋在 schema 前,觸不到能力閘與上限邏輯。無法乾淨驗證,打折(未見放行)
41 2023/2071 自建專案填別人任務指派 ✅ POST /task-assignee admin(非成員)→403 GRC_403002 不具管理者角色;blsshino(reviewer)→同 403。寫入守門生效
46 2040/2178 子公司改分享範本控制項清單 ✅ 【二輪】blsfrank(租戶4) PUT /module-frame/<t3-mf>/control-defaults/<ctrl> → 被擋、MF 名未變;RLS 讓 t3 範本對 t4 不可見
48 2034 列任務問卷空請求拿全公司 ✅ 【三輪】單筆 GET /task-survey/<uid>:blsview→403、blsfrank→404、成員→200;列表 POST /task-surveys blsfrank 回空(RLS 過濾)。一輪必填檢查+三輪歸屬雙證
49 2034 列填答歷史空請求跨專案 ✅ 【三輪】選單 GET /project-survey/histories/menu/<uid>+列表 POST /project-survey/histories:blsview→403、blsfrank→404、成員→200
50 2034 問卷討論列表空查詢撈全公司 ✅ 【三輪】POST /survey-discussions(survey_uid+task_survey_uid):blsview→403 GRC_403053、blsfrank→404、成員→200。歸屬守門生效
51 2034 進任意問卷房間旁聽 ⚠️ on_join 走 SocketIO,非 REST;190 無填答資料、未經 socket client。打折(原因②③,非原因①,維持)
52 2035 拿輪次編號讀別家階段歷程 ✅ GET /project/<p>/audit-round/<r>/stage/transitions admin(非成員)→403 GRC_403053;成員→200。project_uid 真比對生效
53 2035 查目前階段角色不核對整包回傳 ✅ GET /.../stage/info admin(非成員)→403 GRC_403001 不具稽核員角色(擋在前,未整包回傳);成員→200
54 2036 專案成員查詢空白拿全公司名冊 ✅ GET /project-participants/menu+POST /project-participants 空 → 400 PARTICIPANT_400002 project_id 為必填。必填生效
55 2036 換編號讀別人專案群組/控制項配置 ✅ 【二輪】同族 #54/#58 blsfrank(跨租戶,numeric project_id=1) 皆 403 GRC_403053;pcp 專屬端點因 project_control_participants 表空、成員 blspan 也回 404,無資料可獨立驗,以同族佐證
56 2037 換編號讀別人專案摘要報告全文 ✅ 【二輪】blsfrank POST /project-summary-reports → 200 但 total:0(RLS 藏住 t3 的,跨租戶讀不到);列表「只列我參與」需有資料才驗完整,跨租戶隔離已證
57 2037 換編號讀輪次判定風險改善計畫(十入口,團隊名單跨客戶) ✅ 【二輪】blsfrank 跨租戶:round single/list/AP/findings 全 404 GRC_404038/404001(稽核輪次/專案不存在)。跨客戶讀已擋;AR 內容細部(findings 資料層)仍需走到稽核中的輪次才驗得到
58 2036 任務指派清單空查詢拿全公司萬筆 ✅ POST /task-assignees 空 → 412 PARTICIPANT_412001 project_id 或 task_uid 需擇一提供。必填生效
59 2036 底層空查詢=回整表(已裁逐支補) ✅ #48/#49/#50/#54/#58 均已在各端點補必填(見上各列),逐支止血策略生效;底層未動屬預期
64 2041 刪控制項佐證只查計畫不查文件歸屬 ✅ DELETE /ssp/<lssp>/control-implementation/<ctrl>/reference-document/<bogus> blsadmin(manager)→404 GRC_404049 參考程序書不存在或不屬於此計畫。歸屬比對生效
65 2040/2179 建立合規資源庫的入口完全沒有權限檢查 ✅ 【二輪】blsfrank(有 module-frame.create) 建庫→200(自租戶、有能力,屬正常);#195 已證草稿版本被 412 擋。能力閘存在(無能力帳號不存在,無法反證放行,但正向行為正確)。⚠️ 本項建了一筆 t4 測試資源庫 zzfrank,已 API 刪除(soft-delete,is_delete=1,見事故段)
68 2044 群組層成員增改刪整支不檢查 ✅ 190 實測 POST/OPTIONS /control-group-participant → 404(路由不可達,死碼已拆)
69 2044 流程參與者整組壞守門 ✅ /process-participant* 不在 190 路由表(已拆)
85 2060 填答者署名別人 ⚠️ 走 SocketIO on_update,190 無填答資料、未經 socket client 驗。結構上已改用登入身分(程式層),行為層打折
87 2059 空白流程範本讓清單頁全壞 ⚠️ POST /flow-engine/flow-templates 空 xml → 400 COMMON_400001(schema 擋在前);讀取端容錯(清單頁不 500)需有壞資料才驗,打折
88 2059 範本增改不檢查、草稿也能起輪次 ⚠️ create/update 被 schema 400 擋前置,起輪次需走完整流程;未能乾淨驗,打折
102 2055 匯出 Word 使用者文字未跳脫 ⚠️ 需匯出並開檔驗 autoescape,API 層驗不到,打折(屬匯出檔內容檢查)
110 2023 更新成員撈不到就跳過管理者檢查 ⚠️ 需構造「撈不到既有紀錄」情境,靠後道檢查頂住;#41 寫入守門已證有效,此細節打折
115 2035 讀全部流程範本內容(選單擋、功能敞開) ✅ 【二輪】blsfrank GET /flow-engine/flow-templates 只回 SYSTEM 範本(tenant 1),看不到 t3 的 TENANT 範本 050f…test;跨租戶不洩漏
120 2044/2178 四支任務指派功能無入口無守門(裁定刪三留一) ✅ POST /task-assignees/batch 活著(壞 body→400,FE 快速配置不能壞);被刪三支不在路由表
126 2060 塞內部開關叫出隱藏資料夾 ✅ POST /survey-folders 帶 is_delete:true → 200 但只回 is_delete:"0" 的資料夾(隱藏開關被程式寫死忽略)。生效
127 2060/2181 型別不對吐 SQL 原始錯誤 ✅ 帶 is_delete:"notabool" → 200 正常清單(型別被容錯忽略,未吐 SQL/表名)。無錯誤洩漏
128 2059/2188 推進退回操作者名稱可自填 ⚠️ 走階段推進流程,需輪次在對應階段;結構上暱稱已改伺服器強制覆寫,行為層打折
146 2040/2178/2187 Excel 蓋既有範本不問你是誰(唯讀稽核員也能) ✅ 【二輪】blsfrank Excel source_type=module_frame&source_uid=<t3-mf> → 404 GRC_404021;跨租戶覆蓋被擋
147 2178/2187 Word 版蓋既有範本 ⚠️ 【二輪】blsfrank Word 覆蓋在 GRC_400007 缺 framework 參數 前置擋下、未觸達 source 檢查;與 #146 同缺口、Excel 側已證擋。Word 側打折(參數形未完整構造)
148 2188/2189 拿 SSP 編號整份下載 Excel(跨客戶) ✅ GET /ssp/<living-ssp>/excel-template admin(非成員)→403 GRC_403051 僅專案管理者可編輯 SSP;成員(blspan)→拿到檔。跨專案下載已擋
153 2173/2178 管理者掛別人專案文件到控制項 ✅ POST /ssp/<lssp>/control-implementation/<ctrl>/document-mappings 帶別 SSP 的池文件 uid → 404 GRC_404049 不屬於此計畫;帶 bogus uid → 同 404(非靜默 200)。歸屬生效
155 2183 框架 PDF 解析失敗吐路徑/套件名/SQL ⚠️ POST /oscal-framework-parse-jobs/parse 帶必填 meta+junk pdf → 400 COMMON_400001(擋在 schema 層,未到解析器)。未見任何路徑/stack 洩漏,但解析錯誤路徑本身未觸及,打折
156 2180 刪框架/版本不查客戶資源庫在用 ✅ admin(root) 刪被資源庫引用的版本 1cd9fbd6 → 409 GRC_409038 已被合規資源庫使用,不可刪除;刪其所屬框架 → 同 409。引用檢查生效
157 2186 員工直接打網址讀全公司範本清單/內容 ✅ 【二輪】blsfrank GET /module-frame/<t3-mf> → 404 MODULE_FRAME_404001;/module-frames/menu 回空。跨租戶讀不到
158 2186 稽核流程圖任何人讀(增改刪要權限) ✅ 【二輪】blsfrank GET /module-frame/<t3-mf>/control-defaults → 404;跨租戶讀不到(能力閘+歸屬)
159 2178 改流程圖只在改分享時才查歸屬 ✅ 【二輪】blsfrank 對 t3-mf 各寫入端點全 404(RLS 不可見,改分享與否都碰不到)
160 2178 刪改範本子項查網址不查送入的範本 ✅ PUT /module-frame/item/<bogus-child> 帶 moduleFrameUid → 404 GRC_404027 流程範本不存在(先查網址子項存在性)。生效
162 2186/2193/2212 稽核目標預設任何人讀 ✅ 【二輪】blsfrank GET /module-frame/<t3-mf>/objective-defaults → 404;跨租戶讀不到
163 2171/2186 範本人員/設備/元件/繼承任何人讀(跨公司) ✅ 【二輪】blsfrank 對 t3-mf 的 parties/inventory/components/leveraged/system-characteristic/ssp-resources/reference-documents 全 404;跨公司讀不到
164 2171/2187 管理員改分享/公版範本六類附屬資料 ✅ 【二輪】blsfrank 對 t3-mf 六類 add/update 全 404;跨租戶寫入被擋、MF 未變
165 2187/2194 Excel 批次存範本控制項只查存在 ✅ 【二輪】blsfrank POST /module-frame/<t3-mf>/control-defaults/import/validate → 404 MODULE_FRAME_404001;跨租戶被擋
166 2176/2189 計畫內容填公式下載時執行 ⚠️ 需下載 Excel 開檔驗 set_text_cell,API 層驗不到,打折
168 2174/2178 只有建立權限走匯入改既有範本 ⚠️ 需「只有 create 無 update」帳號;blsview(首腦說要建的低權限帳號)在 190 不存在,190 帳號皆滿權限。無法驗,打折(原因①殘留,缺帳號)
178 2186/2195 設備/資訊系統對照任何人讀(附資產內部編號) ✅ 【二輪】blsfrank GET /module-frame/<t3-mf>/ssp-resources → 404;跨租戶讀不到
180 2174/2190 幾 KB YAML 讓伺服器算到當掉 ⚠️ 需上傳惡意 YAML 走 /module-frame/import/yaml 完整流程;未在本卡範圍構造惡意檔實壓(屬效能/DoS,非權限),打折
181 2181/2217 Excel 匯入檢核送長字串佔住兩分鐘 ⚠️ 同上,DoS 類需惡意檔+計時,打折
190 2184/2218 判定接風險不查同一份結果 ⚠️ 需走到有 AR result 的輪次;190 輪次 planning,無稽核結果,打折
193 2184 PDF 超長行卡目錄比對兩分鐘 ⚠️ DoS 類需惡意 PDF+計時,打折
194 2183/2204 Word 打不開錯誤回暫存路徑 ⚠️ /ssp-docx-imports/parse junk docx → 被 400 GRC_400007(缺 framework 參數)擋在 schema 前,未到解析器。未見路徑洩漏,解析錯誤路徑未觸及,打折
195 2179 草稿版本編號拿去建資源庫 ✅ POST /oscal/resource-libraries 帶草稿 fv → 412 GRC_412057 框架版本尚未發佈,無法用來建立合規資源庫。已發佈版本正常。生效
197 2195/2213 兩張解析工作單隔離照抄壞寫法 ⚠️ RLS policy 需以 cm_app 模擬子租戶身分查 ap/ar parse job 表;190 無跨租戶 parse job 資料,且屬 DB-policy 層,打折(程式層另有擋)
198 2174/2203 匯入前改資料塞外部佐證 ⚠️ 需走稽核結果匯入預覽→改 body→確認,需 AR 流程,打折
224 2208 流程圖存檔刪方塊硬刪別人任務 ⚠️ 需流程圖編輯器 sync 流程+可反查專案的實例;API 層難精準構造,打折(結構上已在 sync 層加管理人檢查)
§4

統計(第二輪更新後)

  • ✅ 已擋(實打證實守門有效):38 條——一輪 23 條+二輪用 blsfrank(租戶4) 補證跨租戶隔離的 15 條(#46 #55 #56 #57 #65 #115 #146 #157 #158 #159 #162 #163 #164 #165 #178)。
  • ⚠️ 仍打折:22 條,只剩原因②③④(190 資料狀態/解析錯誤路徑/DoS 與匯出檔內容),原因①「無跨租戶/無成員身分」已被 blsfrank 消除。唯一殘留原因①的是 #168(需「只有 create 無 update」帳號,blsview 不存在)。

未見任何 500 洩漏或路徑/stack 洩漏:所有跨租戶請求都回乾淨 403 GRC_403053(非成員)或 404 GRC_404021/404038/404001/MODULE_FRAME_404001(RLS 讓資源對外租戶不可見),無 500、無 SQL/路徑外洩。既有 /module-frame(list)、/survey(list) 兩支 500 仍是無關既有 bug。


§5

第二輪摘要(2026-09-27 晚,首腦補 blsfrank 後)

首腦在卡末段建了 blsfrank(users.id=7、租戶 4 Billows-子公司、System Manager)解掉「無跨租戶帳號」的打折。本輪用 blsfrank 打租戶 3 全部資源,結論:

  1. 跨租戶隔離全面有效。blsfrank 打 t3 的 job-evidence、audit-round、SSP、module-frame(本體+六類附屬+控制項預設+流程圖+資源對照)、專案成員/控制項成員/任務指派、摘要報告——讀一律 404(RLS 不可見)或 403(非成員),寫一律 404,t3 範本名稱前後未變。
  2. flow-template 不跨租戶洩漏(#115):blsfrank 只看到 SYSTEM 範本,看不到 t3 的 TENANT 範本。
  3. org-units/users menu 對 blsfrank 只回租戶 4(#21/#31 隔離對 blsfrank 這端是好的)。
  4. #54/#58 用 numeric project_id(非 project_uid)才觸達守門:blsfrank→403 GRC_403053。(serializer 吃 project_id:int,傳 project_uid 會先被 schema 400 擋掉,非安全結果。)

blsview 未建:卡上首腦補充只寫了 blsfrank,190 users 表無 blsview(id 只到 7)。且租戶 3 每個角色(含「唯讀檢視者」)本來就都有 module-frame.read,即便 blsview 存在也擋不出「無能力點」效果。能力閘(無 read 能力被擋)這一類仍無帳號可驗——影響 #168,維持打折。

事故與還原(第二輪,照實記)

  • 驗 #65「建資源庫入口權限」時,blsfrank(有 module-frame.create)成功建了一筆租戶 4 測試資源庫 zzfrank(module_frame uid a581499a…、連帶 clone catalog id 7)。這是「有能力者在自己租戶建立」的正常行為、非破口。已用 API DELETE /module-frame/<uid> 刪除,回 200;產品的刪除是 soft-delete(is_delete=1、row 保留、catalog 7 仍在),這是 API 能做到的最乾淨可逆狀態;不手動硬刪 catalog/profile/ssp(那比留一筆 t4 soft-deleted 測試列風險更高)。t3 正式範本 2a2f19f0…(CMMC 2.0 Level1) 全程未受影響。
  • 一輪誤改的留言已還原、收工再查三則內容原封不動。

§6

第三輪摘要(2026-09-27 深夜,首腦補填寫中問卷後)

首腦在 190 建了 blsview(id=8、租戶 3、只有「唯讀檢視者」、非任何專案成員)與一份填寫中的任務問卷(專案 2、task_survey uid 5a94e666…),解掉「問卷表全空、無低權/非成員帳號」的打折。

四身分打 #5/#6/#48/#49/#50,全部呈現理想形狀:

身分 期待 實測
blsview(同租戶、非成員、有 survey.read) 被歸屬擋 全 403 GRC_403053 非此專案參與者
blsfrank(子租戶) 跨租戶擋 全 404 TASK_SURVEY_404005/404001(RLS 不可見)
blspan(成員 auditor,非指派人) 可讀 全 200(CM-2034 已放寬為「任何專案成員可讀」)
blsemp1(成員 viewer,指派人) 正常讀 全 200
  • 關鍵發現:能力點不是防線(blsview 有 survey.read 仍被擋)——真正擋住的是專案成員歸屬檢查,正是 CM-2034 補的那道。非成員讀 403、跨租戶讀 404、成員讀 200,三態分明。
  • #5 需帶 history_uid 或 task_survey_id(非 task_survey_uid)才觸達守門;填答歷史/明細目前無資料(0 筆),但守門在回資料前就先擋,非「查無所以空」。
  • 未寫入:全程只讀,未改答案、未送出、未進 SocketIO 房間;收工查 question_answers/discussions/histories 仍 0 筆、task_survey 狀態未變。
  • 仍打折不動:#51/#85(走 SocketIO,決策者正在用該房間)、#190/#198(輪次仍 planning、無 AR 結果)——維持原因②③,非本輪範圍。

統計更新:✅ 39 條、打折 20 條(59 條總計;三輪把 #5/#6/#48/#49/#50 五條轉正,二輪計數含一條重複已校正)。剩餘 20 條打折只剩原因②(190 資料狀態:SocketIO 房間類 #51/#85、AR 結果類 #190/#198 等)③(解析錯誤路徑 #155/#194/#40/#87/#88)④(DoS/匯出檔內容 #102/#166/#180/#181/#193),原因①(無跨租戶/無成員/無低權帳號)已完全消除。