FR-048 Phase 2a — 全站端點授權矩陣

性質:純盤點(零 code 變更)。全站 api/ 寫入端點(POST/PUT/DELETE/PATCH)+ 敏感 GET(匯出/下載/報表/qrcode/token)逐支判軸,供 user 過目定調。 上游設計../../analysis/2026-07-07-unified-auth-guard-design.md(§2 軸模型、§3.2 決策表、§5.0 scope、§7 phase)。 交叉驗證../../analysis/2026-07-07-known-pits-remediation-tracker.md §SEC 六族 75 條。 建立:2026-07-07,branch fix/v1.8.0-bugs逐模組事實底稿在 _partials/(route/method/呼叫 service/守門 file:line 皆已實查引用)。 盤點法:8 個 subagent 按模組分工,追 route → app service body 找守門呼叫;主 session 判軸 + 交叉驗證。


圖例

軸別(設計 §2):

  • ①platform — root tenant 平台管理員(純 context,require_platform_admin)。全域共用資源(框架、平台設定、LOG)。
  • ②super-admin — 帳號層 super_admin(純 context,require_super_admin)。租戶級系統設定 / 範本 / 管理頁;Phase 3 過渡、Phase 4 換 capability。
  • ③project-role — 專案/SSP/workflow 成員資格(resolve 資源,assert_project_manager / SspPermissionChecker / workflow guard)。
  • ④capability — RBAC 能力點(Phase 4,尚未實作)。
  • ⑤signed-token — 簽章短期憑證(Phase 4,瀏覽器原生 GET)。
  • owner — 資源擁有者個案(D 族,逐案)。
  • F — 刻意不擋(health / OAuth callback / 公開登入流程 / FR-039 控制面)。

狀態

  • 已守 — 已有正確守門,本案不動。
  • 待補 — 軸別明確、目前裸奔,待掛守門(Phase 2b/3/4)。
  • 待定調 — 軸別或角色層級未定,集中在 §1 問 user
  • no-action — 刻意公開/自我 scope/低風險,確認即可。
  • dead — route 未註冊(註解停用)或 service 為 stub,無 live 風險。

Phase2b=軸③專案域補守門|3=軸①②主體域 decorator|4=軸④capability+軸⑤signed-token|=已守/no-action/dead。


0. 進度總覽

分類 端點數(約) 說明
已守(no action) ~110 participant 12、SSP-scoped 34、audit-round 家族 30、framework 家族 18、cloud_integration 5、其他個別
待補 軸③(Phase 2b) ~35 grc job/review/comment/export、flow_engine 證據&任務(含停用 guard 復活)、project_summary_report、subtask_status_history、task_survey 填答、evidence_classification state
待補 軸①②(Phase 3) ~55 auth role/user/tenant/org CRUD、system_config、system_menu、device、bulletin、information_system、notification test、remote_agent 管理面、log export(軸①)
待補 軸⑤ signed-token(Phase 4) 2(+新發現若干) otp-qrcode、framework OSCAL download(+ uploadfile download/pdf-preview、report preview、task-survey import-template 待定調)
待定調(§1) ~15 群 見下
no-action(刻意) ~20 公開登入流程、OAuth/webhook callback、health、FR-039 控制面、自我 scope(ai/chatbot)
dead(未註冊/stub) ~30 FR-038 DEAD-V1 註解 route、2A dark stub
母數 373 寫入 method + 敏感 GET 8 partial 逐支列出

「已守」大宗=Phase 1 之前已鋪守門的三塊(participant / SSP / audit-round)+framework。裸奔集中在系統管理頁(軸②)與部分專案域寫入(軸③),與設計 §5.0 判斷一致。


1. 待定調清單 → 已全數定調(2026-07-07 user 拍板)

判定規則:spec 權限矩陣有寫→照 spec;spec 沒寫 / FE-BE 粒度不一致 / 權限主體不明→集中在此,不自己猜。

定調結果(全數同意建議案)

# 定調
D1 軸②(Phase 3 掛 super-admin,Phase 4 換 module_frame.manage)。範本無 participant,tracker 歸 C 族係誤判
D2 軸①platform(clone 全域框架三件組,同 framework 家族)
D3 軸②;直接採用既有錯誤碼 GRC_FLOW_TEMPLATE_NO_PERMISSION(403041)
D4 軸①platform(建租戶是平台操作)
D5 維持開放(by design,建立者自動 manager);收緊等 Phase 4 project.create
D6 軸③ manager,粒度照 spec 權限矩陣對齊
D7 manager-only(job 建改刪=規劃行為;執行/完成走 flow_engine 給 assignee)
D8 manager(若 reviewer 角色實際在用再放寬為 manager+reviewer)
D9 軸②過渡 → Phase 4 survey.manage
D10 填答=被指派者本人(assignee);configure/import=manager
D11 enroll-token/revoke/rebind → 軸② admin;register/heartbeat 維持 F
D12 改刪=owner 本人;export → 軸② admin
D13 軸②(租戶資產,不引入 owner)
D14 file download/pdf-preview → 軸⑤ signed token(動之前必查 FE 呼叫方式,有前科);schedule-report/preview → 補 JWT+admin+path 消毒(最危險,插隊優先);task-survey import-template → 補 JWT(查 FE);靜態範本、issue/get_members → 補 JWT
D15 直接移除 route(debug 殘留,hardcoded email 已實查確認 user_route.py:164
# 端點群 問題 傾向(供參考) 對應 pit
D1 module_frame 全模組(範本 CRUD + defaults + 元件/資產/party,~43 支,全裸奔) 範本(module_frame)管理權限主體是誰?範本無 project participant,SspPermissionChecker 對 template 分支是用帳號層 is_admin(軸②)。但 tracker 把 module-frame-defaults#5/#6 歸 **C 族(軸③) — 兩者矛盾。 軸②super-admin 或 軸④capability(module_frame.manage);非**軸③(範本非專案) module-frame-defaults#5/#6
D2 oscal resource-libraries(POST create / publish) 資源庫(clone framework_version 三件組)是平台級共用資產還是租戶級? 軸①platform(clone 全域框架)或 軸②租戶
D3 flow_engine flow-templates(create/update/delete/duplicate/publish/unpublish,6 支) 流程範本管理權限主體?現只有 _guard_not_builtin(資源守門,非角色)。 軸①platform 或 軸②(見 flow-template-manage spec)
D4 auth tenant CRUD(tenant POST/PUT/DELETE、add_tenant_with_defaults 建/改/刪租戶是平台操作還是租戶管理員操作? 軸①platform-admin(建租戶=平台級) tenant-manage#1/#4
D5 /oscal-project/start(建專案) 任何登入者可建專案(現況 by design,建立者補 manager);要不要限制? 維持開放 或 軸④project.create project-create#2
D6 planning-status#1 FE/BE 權限粒度不對齊(繞 FE 權限面更寬)——BE 該收斂到哪個粒度? 軸③,粒度待對齊 spec planning-status#1
D7 grc job 編輯(job PUT/DELETE/create、batch-complete) 專案任務編輯是 manager-only 還是被指派 auditor 也可寫? 軸③,角色層級待定 project-task-edit#1/#2、planning-batch-ops#4
D8 grc review mark/unmark(control/AO ×4) 標記審查是 auditor / reviewer / manager 哪一層? 軸③,角色層級待定 project-planning#4
D9 survey 設計/管理(survey/folder/question/page/discussion CRUD,~20 支) 問卷設計器/管理是租戶級 B 族還是有 owner 概念? 軸②super-admin 或 軸④survey.manage survey-v2-designer#1、survey-v2-manage#1/#2
D10 task_survey 填答端點(answers update/patch/checkpoint、history revert、configure) 填答/設定是「被指派者本人」還是「任一專案 participant」? 軸③,assignee vs participant 待定 survey-v2-fill#1/#2
D11 remote_agent FR-039 FE 端點(create/update/delete/revoke/unrevoke/rebind/enroll-token gen+revoke) 程式碼註解稱「需 admin」但全無守門;FR-039 demo 要維持開放,還是這些涉及 enroll-token(安全憑證)的要補 admin? enroll-token/revoke/rebind → 軸②admin;demo 控制面(register/heartbeat)維持 F remote-agent-manage#4
D12 feedback 編輯/刪除(PUT/DELETE、file DELETE、export) 回饋編輯刪除是 owner 還是 admin?export(匯出全站 feedback)是否限 admin? 編輯刪除→owner;export→軸②admin feedback-management#2/#5
D13 information_system / device 管理 資訊系統/設備是租戶管理員(軸②)還是有建立者 owner 概念?(device 連 owner 都沒擋) 傾向軸②super-admin(租戶資產) information-system-manage#1、device-manage#1/#6
D14 **【新發現】未認證端點(不在 75 pit) 下列 route 完全無 @jwt_required**,處置待定(加 JWT / 軸⑤ signed-token / owner):
GET /file/download/<uid>GET /file/pdf-preview/<uid>(uploadfile,任意 uid 下載)
GET /schedule-report/preview(report,query 拼路徑→任意檔讀取風險)
GET /task-survey/import-template/<task_uid>(洩該 task 題庫)
GET /survey/import-template/module-frame/download/template+/yaml-template(靜態範例,低風險)
GET /issue/get_members(純讀裸奔)
download/preview→軸⑤或JWT+owner;靜態範本→補 JWT;issue→補 JWT 無(新增)
D15 **【新發現】POST /send-user-test-mail @jwt_required、寄測試信到 hardcoded email**(raymond.jedicotech@gmail.com);疑似 debug 殘留。 建議移除或補 JWT+admin 無(新增)

D1 是最大結構性問題:module_frame 43 支全裸奔且軸別本身有爭議(tracker 軸③ vs 程式碼軸②),必須先定調才能規劃 Phase 2b/3 分工。


2. 交叉驗證:75 SEC pit → 矩陣對映

tracker §SEC 六族 + 漏網 8 條,每條對映到矩陣模組/狀態。對不上的回頭補。

A 族(未認證,3 條)

pit 對映端點 狀態
user-log#1 GET /log/api-logs/export 軸①(角色)+ 認證 認證已修(182bb2f7);角色守門(SEC-002)=軸① Phase 3
compliance-framework-manage#5 GET /oscal-framework-version/download/oscal/<uid> 軸⑤ ✅ 已修待驗(40a7d9c5,signed-token)
user-profile#3 GET /otp-qrcode/<uid> 軸⑤ ✅ 已修待驗(40a7d9c5,signed-token+自我限定 bind_uid)

B 族(系統設定僅 JWT,→ 軸②/④ Phase 3/4)

pit 對映模組/端點 狀態
ldap-config#1 user_auth_provider ldap/connect-test、system_config LDAP group 待補 軸② P3
notify-channels#2 notify_config test(已有 admin 守門)、system_config notify 部分已守;config 寫入待補 P3
smtp-config#6 system_config SMTP group + notification mail/test 待補 軸② P3
issue-integrate-config#2/#6/#7 system_config issue-integration group 待補 軸② P3;#2 另=D 族 token 明文(見下)
system-menu-manage#3 system_menu POST/PUT/DELETE 待補 軸② P3
role-manage#1 auth role POST/PUT/DELETE/status 待補 軸② P3
user-form#2、user-manage#1、user-import#1 auth user POST/PUT/DELETE/change-status/batch-upload/upload 待補 軸② P3
tenant-manage#1/#4 auth tenant CRUD 待定調 D4(軸①?)
department-manage#8 auth org-unit CRUD 待補 軸② P3
device-manage#1/#6 device POST/PUT/DELETE 待定調 D13(傾向軸② P3);#6=E 族(IP 格式 FE)
information-system-manage#1 information_system POST/PUT/DELETE 待定調 D13
storage-config#3 (storage-config 設定頁)→ system_config storage group 待補 軸② P3
background-services#1 見 F 族(設計如此) no-action 候選
_overview#2/#5 system-admin 首頁彙總(多為讀) _overview#5=F 族

C 族(GRC 專案寫入無角色守門,→ 軸③ Phase 2b)

pit 對映端點 狀態
planning-basic-info#1 grc project/<uid> PUT(update_project) 待補 軸③ P2b
project-planning#4 grc review mark/unmark 待定調 D8
project-task-edit#1/#2 grc job PUT/create/DELETE 待定調 D7;#2 另=E 族(審核人 FE)
planning-batch-ops#4 grc jobs/batch-complete 待定調 D7
project-create#2 /oscal-project/start 待定調 D5
project-ap-list#1 grc project/<uid>/ap/<ap_uid> PUT(dark stub dead(2A stub,不做事);DELETE 已守
project-ssp-basic#1 oscal /ssp/<uid>/system-characteristic PUT 已守(require_manager)→ pit 應已解;#1 另=E 族(name 必填 FE)
project-ssp-components#2 oscal /ssp/<uid>/components 已守(require_manager)→ pit 應已解
project-ssp-inventory#1 oscal /ssp/<uid>/inventory-items 已守 → pit 應已解
project-ssp-leveraged#5 oscal /ssp/<uid>/leveraged 已守 → pit 應已解
project-ssp-parties#3 oscal /ssp/<uid>/parties 已守 → pit 應已解
module-frame-defaults#5/#6 module_frame defaults PUT/DELETE 待定調 D1(軸別爭議)

交叉驗證發現:C 族的 5 條 project-ssp-* pit 在實查中已有 require_manager 守門(SSP-scoped 34 支全數已守)——這些 pit 可能是 spec §12 撰寫時的舊觀察、之後已補。建議 user 定調時將其標為「已解,複驗即可」。真正待補的 C 族集中在 grc(job/review/update_project/batch)與 flow_engine。

D 族(資訊洩漏,逐案)

pit 對映 狀態
issue-integrate-config#2 system_config GET secret 明文回傳(實查確認 jedi_system_config 無 mask) 待修(遮罩),逐案
user-log#3(response 未遮罩)/ #4(api_logs 無 tenant 隔離) log 模組 逐案(併軸① P3 時檢視)
forget-password#1(帳號列舉)、user-profile#2(讀他人完整資料) auth 逐案

E 族(驗證只在 FE)

forget-password#2、device-manage#6、survey-v2-fill#1、project-task-edit#2、project-ssp-basic#1 — 非授權缺口,屬 FE 驗證,本案不處理,維持 tracker E 族後排。

F 族(刻意不擋 / no-action)

cloud-integrations#4/#2(OAuth callback state token / webhook channel token,實查確認)、my-audits#8(SQL 已鎖自己)、remote-agent-manage#4(FR-039 控制面 register/heartbeat)、evidence/_overview#6/#7(FR-039 demo)、ai-dashboard#6/#2(health 公開)、background-services#1、_overview#5、login#7/#3(captcha 順序 / 設計)→ 全數 no-action,已於各模組矩陣標明理由(防下次重查)。

漏網 8 條

compliance-framework-manage#5(A)、user-profile#3(A)、information-system-manage#1(B/D13)、planning-status#1(D6)、user-profile#6(LDAP 綁定不驗本人,低風險→逐案)、user-manage#2(排序唯讀,非缺口)、ai-dashboard#2(F)、login#7(F)——8 條全部對映到矩陣列(無漏)。


3. 逐模組矩陣

每支端點一列。事實欄(現況守門/呼叫 service)源自 _partials/,此處補「軸別/角色/Phase/狀態」判斷欄。dead(未註冊/stub)僅列關鍵者,完整 dead 清單見各 partial。

3.1 api/oscal(framework 已守軸①、SSP 已守軸③;缺口 3)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/oscal-framework(+/versions/parse-jobs 全家族 CRUD/import/confirm) POST/PUT/DELETE require_platform_admin platform 已守 18 支,framework_app/version/edit/parse
/oscal-framework-version/download/oscal/ GET @signed_token_required 4 已修待驗(40a7d9c5) 發 token 端點 .../oscal//token(@jwt);FE window.open 帶 ?st=
/oscal/resource-libraries POST 僅 JWT ①/② 待定 待定調 D2 clone framework_version
/oscal/resource-library//publish POST 僅 JWT ①/② 待定 待定調 D2 狀態轉換
/ssp-excel-imports、/ssp-docx-imports(parse/discard/confirm,6 支) POST/DELETE 條件式:ssp→require_project_manager;framework_version/module_frame→僅 tenant ③(+②) manager 2b/待定 部分已守 ssp 分支已守;非 ssp 分支軸別隨 D1/D2
/ssp//excel-import/upload POST require_project_manager manager 已守
/ssp//(system-characteristic/components/inventory/leveraged/parties/ssp-resources/document-pool/control-implementation/…,30+ 支寫入) POST/PUT/DELETE SspPermissionChecker.require_manager manager 已守 SSP-scoped 全數已守;解 project-ssp-* pit
/ssp//control-implementations/export GET require_participant 參與者 已守 敏感 GET 已守
/ssp//control-implementations/import(+validate) POST require_participant 參與者 已守 讀預覽 participant,confirm 為 manager
/ssp//export GET require_participant 參與者 2b 已修待驗(99945a10) export_from_ssp_version 補 require_participant;export_from_module_frame(軸②)不動

3.2 api/module_frame(全裸奔,軸別待定調 D1)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/module-frame(CRUD)、/clone、/item(+xml)、/import(+yaml/verify) POST/PUT/DELETE 僅 JWT ②/④? 待定 待定調 D1 範本 CRUD ~13 支
/module-frame//control-defaults(+objective-defaults) PUT/DELETE 僅 JWT ②/④? 待定 待定調 D1 module-frame-defaults#5/#6
/module-frame//(reference-documents/components/inventory/leveraged/parties/ssp-resources/system-characteristic) POST/PUT/DELETE 僅 JWT ②/④? 待定 待定調 D1 範本子結構 ~25 支
/module-frame//control-defaults/import(+template/verify/validate) POST 僅 JWT ②/④? 待定 待定調 D1
/module-frame//control-defaults/export、/template/download、/ssp-import-template、/ssp//excel-template、/ssp-import-template、/ssp-export GET 僅 JWT ②/④? 待定 待定調 D1 敏感 GET(含 MF/SSP 實際資料)
/module-frame/download/template、/download/yaml-template GET 無 JWT 待定 待定調 D14 靜態範例檔,低風險
(ModuleFramesRoute/StartRoute) POST 僅 JWT dead init 註解未註冊

3.3 api/project(audit-round 家族已守軸③;缺口=建專案定調)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/oscal-project/start POST 僅 JWT(建立者補 manager) ④? 待定 待定調 D5 project-create#2
/projects//audit-rounds、launch-audit、close-round、launch-reverify POST _check_role(manager) manager 已守
start-auditing、launch-reverify、generate-draft、finalize、AR judge/observation/risk、AP set-* POST/PUT _check_role/_check_auditor(manager override) auditor 已守
ap/docx-imports、ar-imports(parse/confirm/discard) POST/GET/DELETE resolve_ap/round_and_check_auditor/participant auditor/參與者 已守
poam remediation/milestone(新增/改/刪) POST/PUT/DELETE _check_manager manager 已守
audit-rounds/list、round/ap/findings/risks/poam-items(GET/list POST) GET/POST 僅 JWT 參與者(讀) 2b? 讀取 專案域讀未驗參與者;RLS 擋租戶,低優先,可併 2b 順補

3.4 api/project_summary_report + subtask_status_history(軸③,缺口)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/project-summary-report(create/update/delete) POST/PUT/DELETE 僅 JWT(未查專案歸屬 待定 2b 待補 project-summary-report#1;角色層級待 spec
/project-summary-report/export/ GET 僅 JWT 待定 2b 待補 敏感 GET(PDF),無 ownership
/project-summary-report/history-revert POST 僅 JWT 待定 2b 待補
/subtask-status-history(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT 待定 2b 待補 需確認可反查專案輪次

3.5 api/grc(缺口集中;多項待定調)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/projects/batch-delete、/project/ DELETE POST/DELETE is_admin 或 owner/manager manager 已守
/project/ PUT(update_project) PUT assert_project_manager manager 2b 已修待驗(80514dff) D6 planning-basic-info#1
/project//ap/<ap_uid> PUT PUT 僅 JWT(dark stub dead 2A stub,不做事
/project//ap/<ap_uid> DELETE DELETE manager manager 已守
/job/ PUT、DELETE、/assessment-object//jobs POST PUT/DELETE/POST assert_project_manager manager 2b 已修待驗(80514dff) D7;delete_job 新增 project_uid 參數
/jobs/batch-complete POST 僅 JWT manager 2b 待補(handoff) D7;每 job 完成走 flow_engine complete_job,隨 guard 復活受守;自身 project 反查待復活波
control/AO review mark/unmark(×4) POST/DELETE assert_project_manager manager 2b 已修待驗(80514dff) D8 project-planning#4
job-execution comments POST POST 僅 JWT 參與者? 2b 待補(handoff) 角色未定調(參與者 vs manager);需 assert_project_participant canonical
job/comment PUT/DELETE PUT/DELETE 作者本人(owner) owner 已守 owner-of-record
/jobs/export GET 僅 JWT 參與者 2b 待補(handoff) 敏感 GET;參與者級讀,需 assert_project_participant canonical(見 §7)
/jobs/import/confirm POST manager manager 已守 validate/import 為讀
/start-task-execution POST manager manager 已守
projects/list、jobs/list、jobs/my/list、audits/my/list POST 僅 JWT(可見性過濾/自我 scope) 讀取 POST 實為查詢

3.6 api/flow_engine(證據&任務守門停用待復活;範本待定調)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/task/complete、/task/revert POST **已有(停用) assert_workflow_project_participant(body return 參與者 2b 待補(復活)** FR-038 2B 重建 v2 反查鏈
/job-evidence PUT、DELETE PUT/DELETE **已有(停用) 同上 參與者 2b 待補(復活)** 停用期間僅 RLS
/job-evidences POST、/job-evidence POST POST 僅 JWT 參與者 2b 待補 證據新增裸奔
/process/comments PUT、/job-execution/task/link PUT PUT 僅 JWT 參與者? 2b 待補
/flow-templates(create/update/delete/duplicate/publish/unpublish) POST/PUT/DELETE 僅 JWT(僅 _guard_not_builtin 資源守門) ①/② 待定 待定調 D3 範本管理權限主體
/stage/advance POST role gate(force 需 MANAGER) 角色/manager 已守
/flow-templates/validate POST 僅 JWT 讀取 純驗證

3.7 api/auth + user_auth_provider + captcha(B 族軸②,tenant 待定調 D4)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
/user(POST/PUT/DELETE)、/user/change-status、/user/batch-upload、/users/upload POST/PUT/DELETE 僅 JWT(delegate jedi-auth,主專案無守門) admin 3 待補 user-manage#1/user-form#2/user-import#1
/user-profile/ PUT PUT route self-check(uid==本人) owner 已守 本人改自己
/role(POST/PUT/DELETE)、/role/status POST/PUT/DELETE 僅 JWT admin 3 待補 role-manage#1
/tenant(POST/PUT/DELETE)、add_tenant_with_defaults POST/PUT/DELETE 僅 JWT ①? 待定 待定調 D4 tenant-manage#1/#4
/org-unit(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT admin 3 待補 department-manage#8
/user/download/sample GET 僅 JWT admin 3 待補 敏感 GET(scoped 內容)
/otp-qrcode/ GET @signed_token_required(bind_uid) 4 已修待驗(40a7d9c5) /otp-qrcode-token(@jwt) 發放、token 綁本人 uid=只能拿自己 QR
/totp-secret POST 僅 JWT(get_user_context 綁本人) owner no-action 取本人 TOTP secret
/send-user-test-mail POST 無 JWT(hardcoded email) 待定 待定調 D15 疑 debug 殘留
/login、/refresh、/logout、/forget-password、/change-pwd-*、/otp-verify、/otp-resend、/verify-captcha POST/GET 無/refresh JWT F no-action 公開登入流程
user_auth_provider /auth-provider POST/DELETE POST/DELETE 僅 JWT(可指定任意 user_uid) ②/owner 待定 3 待補 應限本人或 admin
user_auth_provider /ldap/connect-test POST 僅 JWT admin 3 待補 ldap-config#1

3.8 api/survey + task_survey(survey 設計待定調 D9;填答待定調 D10)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
survey/folder/question/page/discussion(CRUD/reorder/move/clone/release/import,~20 支) POST/PUT/DELETE 僅 JWT(jedi_survey,全裸) ②/④? 待定 待定 待定調 D9 survey-v2-designer/manage
/survey/import-template GET @signed_token_required 4 已修待驗(40a7d9c5) /survey/import-template/token(@jwt) 發放;BatchImportDialog 改帶 ?st=
task-survey update/configure/import-answers PUT/POST 僅 JWT(有專案 context 未驗) 待定 待定 待定調 D10
project-survey answers(update/patch/checkpoint)、history/revert PUT/POST 僅 JWT assignee? 待定 待定調 D10 survey-v2-fill#1/#2
/task-survey/import-template/<task_uid> GET @jwt_required 4 已修待驗(40a7d9c5) 唯一 consumer 在 _archived=dead,直接補 JWT 即可(4b 判定)

3.9 api/participant + remote_agent(participant 已守;agent 待定調 D11)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
task-assignee、project-participant、project-control-participant、control-group-participant(POST/PUT/DELETE,12 支 live) POST/PUT/DELETE assert_project_manager manager 已守 scope 各異(project/group/control)
task-assignees/batch POST 僅 JWT(stub return [] dead 2A stub
process-participant / project-group-participant(全組) POST/PUT/DELETE 部分無守門 dead init 註解未註冊
remote-agents(create/update/delete) POST/PUT/DELETE 僅 JWT admin? 3 待定調 D11 註解稱需 admin
agents/enroll-token(gen/revoke)、agents//(revoke/unrevoke/rebind) POST 僅 JWT admin? 3 待定調 D11 安全憑證,優先定調
agents/register、agents/heartbeat POST 無 JWT F no-action FR-039 控制面(mTLS/token)

3.10 api/misc(cloud_integration 多已守;系統設定 B 族待補;新發現未認證)

Route path Method 現況守門 軸別 角色 Phase 狀態 備註
cloud_integration google-drive(disconnect/auth-url/retry/rebuild-all) DELETE/POST is_admin / super-admin admin 已守
cloud_integration verify-and-repair-folders POST project manager manager 已守
cloud_integration init-folders POST 僅 JWT(刻意不擋 is_admin) ③? 待定 待定 註解稱避免擋 PM,但也無 PM 檢查;併 D13 檢視
cloud_integration webhook/register POST 僅 JWT(doc 稱 admin only) admin 3 待補
cloud_integration OAuth callback、webhook push GET/POST 無 JWT(state/channel token) F no-action cloud-integrations#2/#4
system_config(config CRUD、by-group/key) POST/PUT/DELETE 僅 JWT admin 3 待補 smtp/ldap/notify/issue B 族
system_config GET(by-group/uid/key) GET 僅 JWT(secret 明文 ② + D admin 3 待補 issue-integrate-config#2(遮罩 D 族)
system_menu(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT admin 3 待補 system-menu-manage#3
information_system(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT 待定 3 待定調 D13 information-system-manage#1
device(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT(無 owner) 待定 3 待定調 D13 device-manage#1
bulletin(POST/PUT/DELETE) POST/PUT/DELETE 僅 JWT admin 3 待補 bulletin-manage#4
feedback(POST/PUT/DELETE/file/export) POST/PUT/DELETE 僅 JWT owner/② 待定 待定 待定調 D12 feedback-management#2/#5
evidence_classification trigger-classify POST project manager manager 已守
evidence_classification run/state、archive、ground-truth PUT/POST 僅 JWT(僅 tenant 解析) ③/② 待定 2b 待補 classify 專案域;GT 匯入或屬 admin
notification、mail/test POST 僅 JWT admin 3 待補 測試發送
notify_config//test POST is_admin admin 已守 notify-channels#2 部分
ai-chatbot(POST/DELETE)、ai-dashboard/auto-generate POST/DELETE 僅 JWT(自我 scope user.id) F/self no-action
ai-dashboard/health GET 無 JWT F no-action 刻意公開
log/api-logs/export GET 僅 JWT(認證已修) platform 3 待補 SEC-002;LOG 能力 platform-only(軸①)
log/api-logs(list) POST 僅 JWT 3? 讀取 隨 export 一併檢視角色
uploadfile /file/download/、/pdf-preview/ GET @signed_token_or_jwt(雙軌) 4 已修待驗(40a7d9c5) /file/access-token/(@jwt) 發放;原生呼叫帶 ?st=、既有 axios bearer 軌不動(TOKEN_401002 refresh 保留)
uploadfile /file/upload(+uploads)、/upload/ DELETE POST/DELETE 僅 JWT(delete 無 owner) owner 待定 待定 delete 應限 owner
report /schedule-report/preview GET 無 JWT(query 拼路徑) ⑤/② 待定 待定調 D14 任意檔讀取風險(新發現)
report /schedule-report/file-list POST 僅 JWT 3? 讀取 檔案清單
issue /get_members GET 無 JWT 待定 待定調 D14 純讀裸奔(新發現)
translate、label POST/GET 僅 JWT/查詢 no-action 非寫入/非敏感

4. 「已守」端點整併政策(2026-07-07 user 拍板:要整合,行為不動、住址收編,優先級在補洞之後)

「已守」~110 支不是同一套寫法,分三類處理:

內容 處置 時機
A. 已用 canonical participant 12 / SSP 34 / framework 18(用 assert_project_manager / SspPermissionChecker / require_platform_admin 只把 import 從舊 shim 換 common.authz 新路徑,零行為變更 Phase 2b 順手
B. 私有 helper 複製 7 份 audit-round 家族 ~30 支:_check_role/_check_manager/_check_auditor 散在 app/grc/service/ 7 個 service(audit_service.py:27、poam_app_service.py:91、audit_round_app_service.py:99、job_import_service.py:81、task_execution_service.py:47、assessment_plan_app_service.py:230、assessment_result_app_service.py:111)——未登記的「第 5 套」,但行為正確 已收編(Phase 2c,見 §7.5):common/authz/project.py 泛化 assert_project_role(allowed_roles=...),7 份 _check_* 改為薄殼委派(call site 全不動、行為保真);複製邏輯已刪 Phase 2c 已完成
C. inline is_admin 散用 22 處 cloud_integration / notify_config / flow_engine stage 等直接讀 ctx.is_admin——兩個 is_admin 地雷的散彈面 require_super_admin() / viewer_is_super_admin()(同為帳號層語意,零行為變更),消滅語意混用 Phase 3 順手

5. 下一步

  1. user 逐項定調 §1 ✅ 已定調(見 §1 定調結果)。
  2. D14 schedule-report/preview(任意檔讀取)插隊優先處理
  3. Phase 2b(軸③補守門+A 類換路徑,含 flow_engine guard 復活)與 Phase 3(軸①② decorator+C 類 inline is_admin 收編)並行推進,逐列銷帳。
  4. Phase 2c(B 類收編:assert_project_role 泛化+7 份 check* 委派)排 2b 收尾後。
  5. §3 標「待補」者為 Phase 2b/3 工作面;「已守」複驗即可;「dead」不動。

本檔為 living tracker:Phase 2b/3/4 落地時逐列更新狀態欄(待補→已守)。


6. Phase 3 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

主體域 route decorator 建置 + 掛點完成。decorator 本體common/authz/decorators.pyrequire_platform_admin_route / require_super_admin_route,後者支援 error_code= 覆寫), 委派 platform.py / admin.py 純 context 判定,掛在 @jwt_required() 之內、@inject 之外。 單元測試 test/test_authz_decorators.py(7 案:raise/pass/fail-closed/custom-code 全綠)。 pytest test/ 對比 baseline 零新增失敗(既有 71 fail / 43 err 全為 stale/env,與本案無關)。

掛點統計:軸① platform 7 支、軸② super-admin 109 支(含 flow-template 6 支用 alias), 共 116 支,分佈 16 模組。

6.1 插隊件

端點 處置 備註
GET /schedule-report/preview(D14) @jwt_required + @require_super_admin_route + _safe_report_path() path 消毒(擋 ../絕對路徑/分隔符 + realpath 容器檢查) FE 唯一呼叫方在 _archived(2026-03-31 停用、路由已移除),無 live consumer;同時把 file-list 的 path 併入 _safe_report_path 硬化。新增 error code REPORT_400001
POST /send-user-test-mail(D15) 直接移除 route(class + __init__ 兩處註冊) debug 殘留 hardcoded email

6.2 軸① platform-admin(@require_platform_admin_route,7 支)

模組 端點 定調
log POST /log/api-logsGET /log/api-logs/export SEC-002 首發,LOG 能力 platform-only
auth POST/PUT/DELETE /tenant(3 支,含 add_tenant_with_defaults D4 建租戶=平台級
oscal POST /oscal/resource-librariesPOST /oscal/resource-library/<uid>/publish D2 clone 全域框架三件組

6.3 軸② super-admin 過渡名單 → ✅ Phase 4a 已換能力點(軸④)

@require_super_admin_route 皆為過渡;Phase 4 能力點 seed 齊後逐支換 2026-07-07 Phase 4a 完成換裝(見 §8)。策略(user 定調)=復用既有 granular cap、精準對 HTTP verb(POST→<res>.create/PUT→<res>.update/DELETE→<res>.delete/敏感 GET→.read), resource 用模組既有 DB resource_type;只 3 組缺口新 seed(system_config/feedback.export/ notification.test)。下表「建議能力點」欄的 .manage 命名為原始草案,實作採 granular verb 映射。

模組 / 端點群 支數 建議能力點
auth user CRUD + change-status + import(upload/validate/batch-upload/sample-download) 8 user.manage(read/create/update/delete)
auth role CRUD + status 4 role.manage
auth org-unit CRUD 3 org_unit.manage
system_config CRUD(by-uid + by-group/key,含 SMTP/LDAP/notify/issue group) 5 system_config.manage
system_menu CRUD 3 system_menu.manage
device CRUD 3 device.manage(D13 租戶資產)
information_system create/update/delete 3 information_system.manage(D13)
bulletin CRUD 3 bulletin.manage
notification mail/test + notification 測試發送 2 notification.test
user_auth_provider ldap/connect-test 1 auth_provider.manage(ldap-config#1)
remote_agent 管理面(create/update/delete/health/reconcile/enroll-token gen+revoke/revoke/unrevoke/rebind) 10 remote_agent.manage(D11;register/heartbeat 維持 F)
feedback export 1 feedback.export(D12;編輯/刪除=owner,另案)
module_frame 全模組寫入(CRUD/clone/import/defaults/元件/資產/party/reference/inventory/leveraged/ssp-resources/system-characteristic) 44 module_frame.manage(D1;Phase 4 換此點)
survey 設計/管理(survey/folder/question/page/discussion CRUD + reorder/move/clone/release/import) 24 survey.manage(D9)
flow_engine flow-templates(create/update/delete/duplicate/publish/unpublish) 6 flow_template.manage(D3;錯誤碼沿用 GRC_FLOW_TEMPLATE_NO_PERMISSION(403041),非預設碼)

6.4 C 類 inline is_admin 收編(帳號層語意,零行為變更)

已收編(14 處,全為 ambient get_user_context().is_admin 請求路徑讀取 → common.authz canonical)

  • notify_config_test_service.test_channel:inline gate → require_super_admin(NOTIFY_CHANNEL_TEST_FORBIDDEN)(測試 patch 路徑同步改指 common.authz.admin.get_user_context
  • cloud_integration route:google_drive_sync_route(3 param 讀 + webhook/register inline gate require_super_admin())、google_drive_integration_route(disconnect/auth-url 2 param 讀)→ viewer_is_super_admin()
  • grc route:project_route(4)、job_route(1)、job_import_route(1)、user_route(1,update user)→ viewer_is_super_admin()

刻意不收(scope 邊界)

  • import service 的 user_context.is_admin(ap_docx/ar/ssp_excel/ssp_docx 共 8 處)user_context顯式參數(route 傳入),非 ambient;已是正確帳號層 DTO 讀取。改成 viewer_is_super_admin()(讀 ambient ContextVar)會回退成 ambient 依賴、破壞可測試性與 worker-thread 安全 → 維持顯式參數形式。
  • common/decorator/common_decorator.py:superuser_required:讀 legacy Flask session(非 UserContextDTO),且全站無 caller(dead)→ 不動。

6.5 本波未處理(列出回報,非默默略過)

項目 原因 / 後續
module_frame 敏感 GET(control-defaults/export、ssp-export、ssp-import-template 含實際資料)→ 軸② D1 有涵蓋,但為 GET;若 FE 以 window.open/iframe 原生呼叫,盲加守門有 A 族回歸前科 → 需先查 FE 呼叫方式再掛,留待 GET 專項(同 D14 方法論)
module_frame/survey 靜態範本 GET(download/template、yaml-template、survey import-template,D14 無 JWT) 靜態範例檔、低風險;補 JWT 需查 FE,留 GET 專項
user_auth_provider /auth-provider POST/DELETE 矩陣標 ②/owner 待定(本人 or admin 未拍板)→ 跳過
軸③ / 2b / 2c 全部 非本波(Phase 3 只做主體域軸①②)

7. Phase 2b 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

軸③ 資源域守門,一律 app service 層、common.authz canonical import。

7.1 已完成(已修待驗)

端點群 守門 角色 commit
§4 A 類:participant 5 + SSP 11 + framework 4 service + oscal DI + ui_route import 換 common.authz.*(零行為變更) cb558280
/ssp/<uid>/export(export_from_ssp_version) SspPermissionChecker.require_participant 參與者 99945a10
grc update_project(PUT project) assert_project_manager manager 80514dff
grc create_job / update_job / delete_job assert_project_manager(走 resolve_project_id;delete_job 新增 project_uid 參數) manager 80514dff
grc review mark/unmark control+ao ×4 assert_project_manager(走 project_domain_service.get_by_uid manager 80514dff

單測:test_fr048_ssp_export_guard(2)、test_fr048_grc_guards(4)。pytest test/ 對比 baseline 零新增失敗(76 fail / 41 err 全為既有 stale/env;passed 662→675=本波 6 + Phase 3 的 7)。DI 全 boot 驗證受限於本 sandbox 缺 GITLAB/GITHUB token(jedi_issue import 期實例化,Phase 3 已記同狀況);wiring 以嚴格 inspection 確認(participant_role_service / associations_container.project_domain_service 皆既有 provider)。

7.2 留待下一波(列出回報,非默默略過)

端點群 原因 建議
flow_engine 守門復活(task/complete、revert、job-evidence PUT/DELETE/POST、process/comments、task/link;assert_workflow_project_participant 開頭 return 需以 v2 反查鏈(workflow_execution→輪次/SSP→project,走 task_assignees.project_id)重建;v1 AP-task mapping 已 dark。量大、屬 FR-038 2B 排程,prompt 明示可拆下 session。 下一波壓軸;判定沿用任一參與者、fail-closed、admin 放行;不另立新軸
grc jobs/batch-complete 每 job 完成實走 flow_engine complete_job,隨上項 guard 復活即受守;自身 project 反查(job_uids 可跨專案)待復活波一併處理 併 flow_engine 波
grc jobs/exportjob-execution comments POST 角色為「參與者」層級,但 common.authz 目前無 assert_project_participant(只有 manager / SSP);comments 角色(參與者 vs manager)未定調 先於 common/authz/project.pyassert_project_participant(design §2.1「任一參與者」屬軸③),再逐支掛
**project_summary_report(create/update/delete/export/history-revert)、subtask_status_history(POST/PUT/DELETE) spec 明載「無角色細分**」=參與者-成員資格層級(非 manager);缺口是「未驗專案歸屬」而非角色。同樣需 assert_project_participant canonical;且 update/delete/export 依 uid,需先反查 report/subtask→project 同上,補 assert_project_participant 後掛;update/delete 需 domain service 提供 report→project 反查
**evidence_classification run/statearchiveground-truth import 矩陣標 軸③/② 待定**;classify 已守 manager,但 run-scoped 端點目前僅 tenant 解析,ground-truth import 可能屬 admin(軸②)。非 D 定調行 待 user 定調 run-scoped 角色(專案參與者 vs tenant admin)

關鍵待辦common/authz/project.pyassert_project_participant(任一參與者放行)=解鎖 jobs/export、comments、project_summary_report、subtask 四組的前置。這是小 canonical 補強(design §2.1 已認可「任一參與者」為軸③一支),非新軸。


7.3 Phase 2b-2 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

完成 2b 交棒單剩餘 + 跨 phase 縫隙。守門一律 common.authz canonical。

已完成(已修待驗)

端點群 守門 角色 commit
common/authz/project.pyassert_project_participant(任一參與者,登決策表) 6d5deac8
evidence_classification put_state / archive_run(run_folder→專案)+ ground-truth import(租戶任一專案 manager) manager(模組 EC_NOT_MANAGER) manager a68c1c82
grc /jobs/export assert_project_participant 參與者 ed5eb47d
flow_engine 守門復活:task/complete、revert、job-evidence PUT/DELETE(既有 wrapper 自動復活)+ job-evidence(s) POST、task/link PUT(加掛);v2 反查鏈 workflow_execution→USER job→task_assignees.project_id(common.authz.workflow 收斂為政策層,解析器在 WorkflowExecutionService) 任一參與者 0f50bbd0
D14 補 JWT:/module-frame/download/template+/yaml-template/issue/get_members(FE 實查安全) JWT 88d1050f

單測新增 16 案(participant 5 / evidence 5 / workflow 政策 6)全綠。回歸驗證:對 baseline f1ea97c3 比對 FAILED/ERROR 集合,76 failed / 41 errors 逐一相同(零新增失敗),passed 675→691=本波 16 新測;無既有通過測試回歸。

留待下一波(列出回報,非默默略過)

端點群 原因 建議
grc job-execution comments POST 角色未定調;且需 workflow_execution→project 反查(JobCommentService 無 workflow_execution_service 依賴,或改由 job_execution_uid 走已復活的 flow 鏈) 定調角色後,注入 workflow guard 或 assert_project_participant
**project_summary_report(create/update/delete/export/history-revert)、subtask_status_history(POST/PUT/DELETE) assert_project_participant 已就緒;但 ProjectSummaryReportContainer / subtask container 未接 project_participant_container**(需加 DI wiring);update/delete/export 依 uid,PSR 走 entity.project_id 反查、subtask 需確認 subtask→project 反查 補 container wiring(participant_role_service)→ create 走 find_project_id_by_uid、update/delete/export 走 entity.project_id → assert_project_participant
MF 敏感 GET(軸②):control-defaults export/download、ssp-import-template、control-defaults GET、ssp-export FE 實查:api.js 標「GET (blob)」者為 axios(bearer-safe,可加 @require_super_admin_route);ssp-export / 其餘需逐支確認非 window.location 逐支 FE 確認 blob → 掛 @require_super_admin_route(併 module_frame.manage 過渡名單)
軸⑤ signed-token(Phase 4)新增/survey/import-template(BatchImportDialog window.location LIVE)、/task-survey/import-template/<task_uid>(_archived window.location) 原生下載帶不了 bearer,盲加 JWT 踩 ffa01a78 前科 Phase 4 signed-token(與 otp-qrcode / framework download 同機制)
process/comments PUT route 直呼 jedi_flow_engine ElementVariableService,無主專案 service 可掛守門 Phase 5 下沉套件;低風險留言
grc jobs/batch-complete 自身入口 每 job 走 complete_job 已受守(transitive);自身入口 project 反查(job_uids 可跨專案)未加 逐 job 反查驗(低優先,transitive 已覆蓋主風險)

7.4 Phase 2b-3 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

收掉 2b-2 §7.3 尾巴。守門一律 common.authz canonical。

已完成(已修待驗)

端點群 守門 角色 commit
project_summary_report create/update/delete/export/history-revert(含 history service) assert_project_participant(container 補接 project_participant_container;entity.project_id 反查) 參與者 02e7674d
grc job 留言 POST(add_comment) 復用 WorkflowExecutionService.assert_project_participant(job_execution→workflow_execution_id) 參與者 e2c448ff
grc jobs/batch-complete 入口 assert_project_manager(route project_uid thread 進;per-job transitive 守門保留) manager e2c448ff
MF 敏感 GET:control-defaults GET list/detail・export・template/download、ssp-import-template ×2・excel-template・ssp-export @require_super_admin_route(FE 逐支實查 axios/blob,bearer-safe) super-admin a83a8619

單測新增 7 案(PSR 4 / job 留言 3)全綠。batch-complete 守門=既有 assert_project_manager 呼叫(test_fr048_grc_guards 已覆蓋;本 service import WorkflowExecutionService 有既有循環 import,不另單獨 import 測)。回歸:對 baseline c05ed713 比對,76 failed / 41 errors 相同(零新增失敗),passed 691→698=本波 7 新測。

FE 查證結論(檔案下載類,逐支)

端點 FE 呼叫 結論
control-defaults export / template/download axios.get(url, {responseType:'blob'})ModuleFrameTemplateService.js:208/219 ✅ blob → 掛 @require_super_admin_route
ssp-import-template ×2 / excel-template / ssp-export api.js 全標「GET (blob)」 ✅ blob → 掛
control-defaults GET list/detail this.get(...)(BaseService axios) ✅ JSON 讀 → 掛

留待後續(非默默略過)

端點群 原因 / 後續
**subtask_status_history(POST/PUT/DELETE) 唯一 FE consumer 在 _archived/projects/oscal/ProjectTaskManage.vue(v1-dead,無 live 流量);task_id 為 opaque 字串、無 documented project 反查、無既有反查方法 → 猜反查鏈有正確性風險(verify-first / 禁重複造輪)。待確認 task_id 指涉**(或隨 v1 端點退役)再處理
process/comments PUT route 直呼 jedi_flow_engine ElementVariableService,無主專案 service 可掛守門 → Phase 5 下沉套件;低風險留言
survey / task-survey import-template 原生 window.locationPhase 4 signed-token(見 §7.3;盲加 JWT 踩 ffa01a78 前科)

Phase 2b(含 2b/2b-2/2b-3)軸③ 資源域收口大致完成;剩餘為 v1-dead(subtask)、套件下沉(process/comments)、Phase 4 signed-token(原生下載)三類非本軸工作。


7.5 Phase 2c 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

§4 B 類收編完成app/grc/service/ 7 份私有 _check_* 複製品 → 委派 common.authz canonical,行為保真、零行為變更、call site 全不動

canonicalcommon/authz/project.py 新增 assert_project_role(participant_domain_service, project_id, user_id, allowed_roles=("manager",), error_code=None)——project 層 participant 精確查詢守門,不做 control→group→project fallback(那是 assert_project_manager 的軸,兩支並列在 __init__.py 決策表、語意不可互相冒充)。error_code 未給時按 allowed_roles 選預設(含 auditor→GRC_NOT_AUDITOR,否則 GRC_NOT_MANAGER);manager override 由 caller 用 allowed_roles=("auditor","manager") 明示。

7 份對映

method 委派參數 特殊處理
audit_service _check_role manager→("manager",)/其他→(required_role,"manager") uid→resolve+404 保留;override 於 2026-07-07 user 定調後對齊(見下)
poam_app_service _check_manager 預設 ("manager",)
audit_round_app_service _check_role manager→("manager",)/auditor→("auditor","manager") manager override 保住
job_import_service _check_manager_role 預設 ("manager",) uid→resolve+404 保留
task_execution_service _check_manager 預設 ("manager",) resolve+404 保留、維持回傳 project entity
assessment_plan_app_service _check_auditor ("auditor","manager") manager override
assessment_result_app_service _check_auditor ("auditor","manager") manager override

✅ 已定調並對齊(2026-07-07 user 拍板「讓 manager 可代行」)audit_service._check_role 原為 exact match 無 manager override(與 audit_round / assessment_* 不同調),收編時先保真並標註。user 定調後已對齊:auditor 端點改 allowed_roles=(required_role, "manager"),manager 可代行 AR 相關寫入(generate-draft / judge / observation / risk),全站 override 語意一致。此為 2c 收尾唯一的行為變更(放寬,非收緊)。

驗證:baseline worktree(2c 前 commit 8068d954)集合 diff——pytest test/ --continue-on-collection-errors FAILED/ERROR 節點集比對,新增失敗 0;2 支 task_execution 測試在全套跑 FAILED→PASSED 為既有跨測試順序 flakiness(隔離跑兩樹皆 5 綠),與本案無關。7 份方法體皆只剩委派一行+註解,殘留複製邏輯 0;now-unused import(ForbiddenError / ProjectParticipantQueryEntity)順手清除。


8. Phase 4a 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs

軸④ capability guard 建置 + §6.3 過渡名單換裝完成。

① guard 本體common/authz/capability.py@require_capability(*names, mode="any"/"all", error_code=?)。 委派 DI CapabilityGuardauth_container.capability_guard,自帶 @transaction),走 web-menu 既有查詢鏈 user→roles→role.capabilities→cap.name(不另立查詢路徑);flask.g per-request memoize;帳號層 super_admin break-glass 直接放行(viewer_is_super_admin())。錯誤碼 GRC_CAPABILITY_REQUIRED(GRC_403022)。單測 test_authz_capability.py 14 案全綠。commit eb590ae4

② 能力點策略(user 定調)復用既有 121 顆 granular cap(<resource>.<action>)、精準對 HTTP verb(POST→create/PUT→update/DELETE→delete/敏感 GET→read),不新造 .manage 平行方案 (避免與 FE 選單所讀的 granular RBAC 分裂=第五套)。Administrator[root,id=2] 已全持有既有 write cap, 換裝零 role_capabilities 變更。只 3 組缺口新 seed(migration 2026-07-07-fr048-phase4a-capability-seed.sql, 已套 DEV、驗冪等):system_config.{create,update,delete}(is_platform,泛用設定寫入觸及平台級)、 feedback.export(feedback.create 語意為使用者提交、不可復用)、notification.test(無 notification resource)。commit 7828653e

③ 換裝(35 檔,commit e8b28a4a@require_super_admin_route@require_capability("<res>.<verb-action>")。 模組→resource:auth user/role/departmentbulletindeviceinformation-systemsystem-menuremote-agent-managemodule-frame(15 檔)、survey(4 檔,survey_route 見下)、ldap-configreportsystem_configfeedback.exportnotification.test。flow_template 用 3 支 verb-specific alias 保留自訂碼 GRC_FLOW_TEMPLATE_NO_PERMISSION軸① platform decorator 本波不換(tenant/log/framework 維持 @require_platform_admin_route)。

④ 語意變化(重要,須手測確認):判定從「帳號層 super_admin」→「持有能力點 super_admin break-glass」。持 Administrator 角色的一般帳號現在也能過(原本 403)——放寬到 RBAC 正軌,符合設計 §2.0;但權限面「誰能過」變了,須讓 user 知悉。

⑤ 驗證:clean worktree 集合 diff(baseline c29acbd0 無 4a/test e8b28a4a 4a-only,隔離 4b 未 commit 干擾)——FAILED/ERROR 節點集 128↔︎128 新增失敗 0,passed +14(capability 單測);17 支換裝 route 模組 import smoke 全過。DEV live 403/pass 三態測列手測 checklist(BE user 自起)。

⚠️ 與 Phase 4b(signed-token)同 working tree 交錯api/survey/routes/survey_route.py 的 survey CRUD 換裝已在工作區完成,但該檔 import 行同時被 4b 改(issue_signed_token/signed_token_required)、且依賴 4b 未 commit 的 common/authz/signed_token.py暫不納入 4a commit,待與 4b 協調後一併 commit。其餘 35 檔 不受影響。

未處理/延後:tenant.* 標 is_platform(Phase 5 併軸鋪路,有存量副作用,刻意延後);STG/POC migration (待 user 指示);FE 角色矩陣新 cap 顯示 / i18n(查 FE 慣例後回報)。


9. Phase 4b 落地紀錄(2026-07-07,branch fix/v1.8.0-bugs;本節由 review session 補記——4b 執行 session 未寫矩陣)

軸⑤ signed-token 機制+五件收口。BE 40a7d9c5、FE 79c0133(compliance-manager-fe,17 檔含共用 signedDownloadUtil.js)。

機制common/authz/signed_token.pyitsdangerous.URLSafeTimedSerializer,key 自 JWT_SECRET_KEY 派生、scope 當 salt(跨 scope 重放無效),payload {scope, resource_uid, user_id},TTL 120s。issue_signed_token / verify_signed_token / @signed_token_required(純簽章軌,bind_uid_arg 可綁 path 參數)/ @signed_token_or_jwt(雙軌,bearer 軌刻意不吞例外→保留 TOKEN_401002 觸發 FE refresh)。錯誤碼 GRC_401011。單測 test_authz_signed_token.py 12 案。

五件:①otp-qrcode(發放端 /otp-qrcode-token+token 綁發放者本人 uid=自我限定,修掉「知 uid 拿他人 TOTP」本體洞)②framework OSCAL download(.../oscal/<uid>/token)③file download/pdf-preview(/file/access-token/<uid>+雙軌,既有 axios 呼叫方零改動)④survey import-template(LIVE window.location 件)⑤task-survey import-template(實查 consumer 僅 _archived=dead → 直接補 @jwt_required,不上 token)。

Review 補核(2026-07-07):全套件 baseline 集合 diff(c29acbd0→HEAD,涵蓋 4a+4b)新增失敗 0、passed 692→724;signed-token/capability 單測 26 案獨立跑全綠。

⚠️ 4b 未完項(待補)

  1. e2e 未寫(4b prompt/設計 §6 明定前科件必附):test repo 補「登入→個人資料頁 QR 有載出」「框架管理頁 OSCAL 下載成功」+負向案(登出直打資源 URL→401)。
  2. token 過期行為未實測(發 token→過 TTL→401)。
  3. 手測 checklist 由 review session 代出(見交付訊息)。

10. 裸奔稽核後補洞(Phase 2b-4,2026-07-07,branch fix/v1.8.0-bugs

主體收尾後的裸奔稽核(5 群 subagent + 主導逐條驗 code)挖出的真漏網——大多數 agent 報的裸奔是誤判 (只看 route 沒追 service 層守門)。逐條先驗 live 再改,通過驗證的真缺口 4 條處置如下。

10.1 已補 / 已處置

# 端點群 現況 處置 狀態
1 task_survey 填答 7 支:answer update/patch/checkpoint(question_answer_service)、history revert(question_answer_history_service)、update_task_survey / configure / import-answers(task_survey_service service 零身分檢查,任何登入者可改任何人填答 D10:填答=assignee 本人(manager 可代填);管理=manager。新 guard app/task_survey/common/task_survey_guard.pyassert_task_survey_writer / assert_task_survey_manager),assignee 走 task_assignees(task_id,user_id) 自然鍵、manager 走 assert_project_manager canonical(project_id 由 task_assignees.project_id 反查)。新 error code TASK_SURVEY_403001。DI 補接 project_participant_containerparticipant_role_service ✅ 已修待驗
2 **poam update_poamapp/grc/service/poam_service.py:96 service 零守門 dead,不改**:route PoamDetailResource PUT 在 api/grc/__init__.py:167-170 已註解停用(FR-038 DEAD-V1 §1B,FE PoamViewRoundPoamView/audit-round/*)。PoamService 全站僅此 dead route 引用,無 live 風險。真 live 的 v2 是 poam_app_service(有 _check_manager)。 dead(複驗即可)
3 report file-listapi/report/routes/scheduler_report_route.py:44 POST) 只有 @jwt_required,與 preview 不一致 對齊 preview 掛 @require_capability("report")(見 #4 修正) ✅ 已修待驗
4 report 能力點名稱錯(4a 換裝後遺症) preview 用 @require_capability("report.read"),但 DB report.read(DEV live 查證:只有既有 report id=49/is_platform=f/desc「report?v=1 read access」,掛 Administrator)→ 該守門 fail-closed 擋掉 super_admin 以外所有人=preview 現行已壞 user 定調①:report.read→既有 report。preview + file-list 皆改 @require_capability("report"),零 migration。 ✅ 已修待驗

驗證:新增單測 test/test_fr048_task_survey_guard.py(6 案:writer assignee/manager-override/deny、manager pass/deny/unresolvable)全綠;test_audit_event_instrumentation.py 補守門 fixture(1 案回綠 + _build_* 注入 guard deps)。回歸:baseline worktree(起點 108df8f8)集合 diff——FAILED/ERROR 128↔︎126新增失敗 0;少 2 為既有 test_task_execution_service 跨測序 flaky,隔離跑綠),passed 724→730=本波 6 新測。

10.2 稽核誤判澄清(備查,避免下次重查)

裸奔稽核 agent 報的其餘「缺口」經主導逐條追 service 層後確認已守 / 非缺口,記此免重查:

稽核指稱 澄清
flow_engine 12 支「裸奔」 §7.2/§7.3 已復活 assert_workflow_project_participant 守門鏈(task/complete、revert、job-evidence PUT/DELETE/POST、task/link);agent 只看 route 未追 wrapper 復活狀態 → 誤判
oscal 13 支「裸奔」 §3.1 已列:SSP-scoped 30+ 支全數 SspPermissionChecker.require_manager、framework 家族 require_platform_admin;agent 未追 route decorator/service resolve → 誤判
cloud 4 支「裸奔」 §3.10/§F 族已列:disconnect/auth-url/webhook is_adminrequire_super_admin、verify-and-repair manager;OAuth callback/webhook push 刻意 F(state/channel token)→ 誤判
participant「那批裸奔」 §3.9 已列:task-assignee/project/control/group participant 12 支 live 全 assert_project_managerbatch/process-participant 為 2A stub / 未註冊 dead → 誤判

本 §10 收口後,Phase 2b 軸③ 資源域含裸奔稽核補洞大致封版;剩 v1-dead(subtask)、套件下沉(process/comments)、Phase 4 signed-token(原生下載)三類非本軸工作(見 §7.4 末)。