A.1 盤點:oscal 合規框架與 flow 流程設計模組端點 × 三段式能力點

卡片 CM-2512(FR-132 T-1.2)。唯讀盤點,不改程式、不改 DB。

§1

範圍與數量

  • 範圍:合規框架(框架、版本、catalog 編輯、PDF 匯入)、合規資源庫(api/module_frame/ 與 api/oscal/ 下的資源庫端點)、專案 SSP(api/oscal/routes/ssp/)、流程範本(flow_template_route.py),以及 T-1.1 留下的兩支 api/flow_engine 端點(process-definition、stage-objects)。
  • 主專案 route 檔 37 支、161 個 method:
    • api/oscal/routes/framework/ 4 支、25 個
    • api/oscal/routes/ 根目錄 2 支、3 個(資源庫列表/新增、範本 SSP 參照)
    • api/oscal/routes/ssp/ 13 支、58 個(另有 3 個 Resource 已註解停用:SspScopedExcelImportRoute GET/DELETE、SspScopedExcelImportConfirmRoute POST,api/oscal/__init__.py:110-111,不計入)
    • api/module_frame/routes/ 15 支、62 個
    • api/module_frame/routes/ 的 62 個裡含 /ssp/<uid>/excel-template(程式住資源庫、資源是 SSP,列在 SSP 段)
    • api/flow_engine/routes/ 3 支、13 個(流程範本 11、階段物件 1,以及 flow_engine_route.py 的流程定義 1 個;同檔其餘 3 個已由 T-1.1 歸 grc)
  • 外部套件 route 檔 0 支:jedi-flow-engine 只有 api/guards.py(runtime() 取執行期組件,不是守門 decorator,套件自己註明「本套件沒有 route」);jedi-oscal-v2 沒有 api/ 目錄。兩支套件提供的守門被主專案用到的:無。
  • DEV 查詢時間:2026-10-06 17:27(本機 localhost:5432/guidant_ai_dev)。範圍內舊能力點 12 個(compliance-framework.*、module-frame.*、flow_template.* 各 4)。workflow.* 4 個已由 T-1.1 判刪除,本份不重複。
  • 三個 blueprint 都沒有 license 軸(require_license 零命中)。路徑前綴一律 /api/1.0,下表省略。
§2

先看這幾點

  1. compliance-framework.* 四個能力點一支 route 都沒守。 合規框架 25 個 method 的 route 層只有 jwt_required;寫入在 service 層用 require_platform_admin()(只限原廠總部租戶的管理員),讀取完全不檢查。四個能力點只綁在 /compliance-framework/compliance-framework-manage 選單與角色範本,決定選單看不看得到。
  2. SSP 58 個 method 全部沒有能力點,只靠 service 層 SspPermissionChecker:讀=專案任一成員,寫=專案 manager 且當前輪次仍在規劃階段(common/authz/ssp.py:91-104、domain/oscal/service/ssp_project_resolver.py is_writable)。成員檢查是完整的(13 支 service 逐 method 對過,沒有漏掉的),缺的是公司層這一道。
  3. SSP 的控制項樹不只 SSP 頁在用。 GET /ssp/<uid>/control-tree 被證據分類、稽核審閱、AP 編寫、稽核員總覽等六個頁面呼叫。oscal.ssp.read 一旦掛上守門,出貨角色範本的一般成員組必須帶它,否則專案成員打開稽核頁就 403。
  4. 資源庫子物件的動詞今天是照 HTTP 動詞配的:替既有資源庫新增元件要 module-frame.create、刪元件要 module-frame.delete、匯入預設值到既有資源庫也要 create。新規則把子物件寫入一律歸 update(見定案 3),等於行為改變,要在 migration 補配時一併處理。
  5. SSP 與資源庫共用兩支「雙目標匯入」端點(/ssp-excel-imports/*、/ssp-docx-imports/*):同一支 route 依上傳時選的目標,寫進專案 SSP 或資源庫範本。能力點只能在 service 層依目標判,route decorator 表達不了。
  6. 流程定義端點不是舊入口。 GET /flow-engine/process-definition/<uid> 讀的是查核項目層的 BPMN(jedi_flow_engine 的 workflow_templates,與流程範本 flow_templates 是兩張表),被專案規劃頁與三支資源庫頁面透過 /workflow/workflow-setup 打開(FE config/router/index.js:457-466 註解明載必須保留)。歸資源庫,見定案 6。
§3

新名總覽(建議)

新資源 read create update delete 獨立動詞 取代的舊名 平台層
oscal.framework ✓ ✓ ✓ ✓ import(PDF 匯入) compliance-framework.* create/update/delete/import
oscal.resource-library ✓ ✓ ✓ ✓ 無 module-frame.* 否
oscal.ssp ✓ ✓ ✓ ✓ import、export 無(新開) 否
flow.template ✓ ✓ ✓ ✓ publish(發布/取消發布) flow_template.* 否
  • 資源名、flow 獨立模組、publish 都已定案,見文末定案結論。
  • oscal.ssp.create/.delete、oscal.framework 以外的部分 create/delete 今天沒有對應端點(SSP 隨建專案產生),照「每個資源固定四個」仍要建出來。
§4

主表

欄位:route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 範圍層 | 備註。

守門縮寫:J=jwt_required;C(x)=require_capability("x");PA=service 層 require_platform_admin();V(x)=service 層 viewer_has_capability("x");ST=簽章下載憑證(signed_token_required)。SSP 段的專案層守門寫「成員:參與者」或「成員:manager+可寫」(=manager 且當前輪次仍在規劃階段)。程式位置的行號是 method 定義行。

合規框架(oscal.framework)

框架程式 api/oscal/routes/framework/,service 在 app/oscal/service/framework_*.py。對應頁 /compliance-framework/compliance-framework-manage(ComplianceFrameworkManage.vue、ComplianceFrameworkVersionManage.vue),catalog 編輯 FrameworkVersionEditView.vue,PDF 匯入 FrameworkImportPage.vue。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/oscal-frameworks POST oscal_framework_route.py:49 J oscal.framework.read 無 ComplianceFrameworkManage tenant 列表
/oscal-frameworks/menu GET oscal_framework_route.py:35 J oscal.framework.read 無 menuStore.fetchComplianceFrameworkMenu:ModuleFrame、SspDocxImportPage、ImportExcelPage、DownloadSspBlankTemplateDialog tenant 輔助端點;呼叫者多是資源庫頁,靠一般成員組本來就帶 read
/oscal-framework/<uid> GET oscal_framework_route.py:68 J oscal.framework.read 無 ComplianceFrameworkManage、ComplianceFrameworkVersionManage tenant
/oscal-framework POST oscal_framework_route.py:78 J+PA oscal.framework.create 無 ComplianceFrameworkManage 平台
/oscal-framework/<uid> PUT oscal_framework_route.py:92 J+PA oscal.framework.update 無 同上 平台
同上 DELETE oscal_framework_route.py:101 J+PA oscal.framework.delete 無 同上 平台
/oscal-framework-versions POST oscal_framework_version_route.py:38 J oscal.framework.read 無 ComplianceFrameworkVersionManage tenant 版本列表
/oscal-framework-version/<uid> GET oscal_framework_version_route.py:57 J oscal.framework.read 無 同上 tenant
/oscal-framework-version POST oscal_framework_version_route.py:68 J+PA oscal.framework.update 無 同上 平台 新增版本=改框架(子物件歸 update,定案 3)
/oscal-framework-version/<uid> PUT oscal_framework_version_route.py:82 J+PA oscal.framework.update 無 同上 平台
同上 DELETE oscal_framework_version_route.py:92 J+PA oscal.framework.update 無 同上 平台 子物件歸 update
/oscal-framework-version/download/oscal/<uid>/token GET oscal_framework_version_route.py:155 J oscal.framework.read 無 signedDownloadUtil.js:75(版本頁「下載 OSCAL」) tenant 發短期下載憑證;守門要掛在這支(下載端點本身不帶 bearer)。定案 5
/oscal-framework-version/download/oscal/<uid> GET oscal_framework_version_route.py:120 ST(綁 uid) oscal.framework.read 無 同上 tenant 不掛能力點,由發憑證那支守
/oscal-framework-version/<uid>/catalog-tree GET framework_version_edit_route.py:34 J oscal.framework.read 無 FrameworkVersionEditView、ComplianceFrameworkVersionManage、ModuleFrame、EditIncludeControlsDialog tenant 資源庫頁也打(挑控制項)
/oscal-catalog-group/<uid> PUT framework_version_edit_route.py:50 J+PA+限草稿 oscal.framework.update 無 FrameworkVersionEditView 平台
同上 DELETE framework_version_edit_route.py:64 J+PA+限草稿且無引用 oscal.framework.update 無 同上 平台 子物件歸 update
/oscal-catalog-control/<uid> PUT framework_version_edit_route.py:80 同 group oscal.framework.update 無 同上 平台
同上 DELETE framework_version_edit_route.py:94 同 group oscal.framework.update 無 同上 平台
/oscal-catalog-control-assessment/<uid> PUT framework_version_edit_route.py:110 同 group oscal.framework.update 無 同上 平台
同上 DELETE framework_version_edit_route.py:124 同 group oscal.framework.update 無 同上 平台
/oscal-framework-parse-jobs GET framework_parse_job_route.py:39 J(只列本租戶) oscal.framework.read 無 FrameworkImportService(框架頁進行中匯入的提示條) tenant 輔助端點;給 read 不給 import,避免看框架頁的人被提示條 403
/oscal-framework-parse-jobs/parse POST framework_parse_job_route.py:58 J+PA oscal.framework.import 無 FrameworkImportPage 平台 上傳 PDF
/oscal-framework-parse-jobs/<uid> GET framework_parse_job_route.py:127 J oscal.framework.import 無 FrameworkImportPage、FrameworkVersionEditView 平台 解析預覽;今天無任何檢查,見「守門缺口」1
同上 DELETE framework_parse_job_route.py:140 J+PA oscal.framework.import 無 FrameworkImportPage 平台 廢棄解析單
/oscal-framework-parse-jobs/<uid>/confirm POST framework_parse_job_route.py:159 J+PA oscal.framework.import 無 同上 平台 寫入 catalog/版本

FrameworkAppService.publish_version(app/oscal/service/framework_app_service.py:246)沒有任何 route 呼叫,不列。

合規資源庫(oscal.resource-library)

程式 api/module_frame/routes/ 與 api/oscal/routes/ 根目錄兩支。對應頁 /module-frame/module-frame(ModuleFrame.vue),範本編輯 ModuleFrameTemplateEditView.vue(經 ModuleFrameTemplateService.js),舊版項目頁 ModuleFrameItems.vue。現有守門全部在 route 層,每個 method 都有,無遺漏。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/oscal/resource-libraries/list POST api/oscal/routes/resource_library_route.py:29 J(無能力點) oscal.resource-library.read 無 ModuleFrame tenant 列表沒守,單筆 GET 卻要 read。見「守門缺口」2
/oscal/resource-libraries POST resource_library_route.py:45 J+C(module-frame.create) oscal.resource-library.create module-frame.create ModuleFrame、SspDocxImportPage tenant 從框架版本複製三件組建新資源庫
/module-frame/<mf_uid>/template-ssp GET api/oscal/routes/module_frame_template_ssp_route.py:33 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateEditView tenant 輔助端點,查範本 SSP 編號
/module-frames/menu GET module_frame_route.py:28 J+C(module-frame.read 或 project.create) oscal.resource-library.read module-frame.read、project.create ProjectCreateView tenant 輔助端點;project.create 放行要改成 grc.project.create
/module-frame/<uid> GET module_frame_route.py:49 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrame tenant
/module-frame POST module_frame_route.py:60 J+C(module-frame.create) oscal.resource-library.create module-frame.create 同上 tenant
/module-frame/<uid> PUT module_frame_route.py:76 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_route.py:88 J+C(module-frame.delete) oscal.resource-library.delete module-frame.delete 同上 tenant
/module-frame/clone POST module_frame_route.py:99 J+C(module-frame.create) oscal.resource-library.create module-frame.create ModuleFrame tenant
/module-frame/item/<uid> GET module_frame_item_route.py:34 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameItems tenant 查核項目
/module-frame/item POST module_frame_item_route.py:51 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update(行為改變,定案 3)
/module-frame/item/<uid> PUT module_frame_item_route.py:67 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_item_route.py:82 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/item/xml/<uid> PUT module_frame_item_route.py:101 J+C(module-frame.update) oscal.resource-library.update module-frame.update WorkflowSetupEditor(/workflow/workflow-setup) tenant 存查核項目層 BPMN;讀取端是流程定義那支,見流程段
/module-frame/import/verify POST module_frame_import_route.py:26 J+C(module-frame.create) oscal.resource-library.create module-frame.create 無 UI(FE 常數 MODULE_FRAME_IMPORT_VERIFY 沒人引用) tenant 檔案建資源庫。疑似死碼,見「守門缺口」6
/module-frame/import POST module_frame_import_route.py:48 J+C(module-frame.create);覆蓋既有時 V(module-frame.update)(module_frame_import_service.py:143) oscal.resource-library.create module-frame.create、module-frame.update 無 UI tenant 同上
/module-frame/import/yaml POST module_frame_import_route.py:79 J+C(module-frame.create) oscal.resource-library.create module-frame.create 無 UI tenant 同上
/module-frame/<uid>/system-characteristic GET module_frame_system_characteristic_route.py:31 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateEditView tenant 範本 SSP 的系統特性
同上 PUT module_frame_system_characteristic_route.py:48 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
/module-frame/<uid>/components GET module_frame_components_route.py:21 J+C(module-frame.read) oscal.resource-library.read module-frame.read ComponentsLeveragedInventoryTab tenant
同上 POST module_frame_components_route.py:35 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/components/<item_uid> PUT module_frame_components_route.py:53 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_components_route.py:70 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/inventory GET module_frame_inventory_route.py:21 J+C(module-frame.read) oscal.resource-library.read module-frame.read ComponentsLeveragedInventoryTab tenant
同上 POST module_frame_inventory_route.py:35 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/inventory/<item_uid> PUT module_frame_inventory_route.py:53 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_inventory_route.py:70 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/leveraged GET module_frame_leveraged_route.py:32 J+C(module-frame.read) oscal.resource-library.read module-frame.read ComponentsLeveragedInventoryTab tenant
同上 POST module_frame_leveraged_route.py:46 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/leveraged/<item_uid> PUT module_frame_leveraged_route.py:66 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_leveraged_route.py:83 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/parties GET module_frame_party_route.py:29 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateEditView(FE 走共用 composable,apiBase 切換) tenant
同上 POST module_frame_party_route.py:45 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/parties/<party_uid> PUT module_frame_party_route.py:67 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_party_route.py:86 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/ssp-resources GET module_frame_ssp_resources_route.py:36 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameSspInfoSystemsPanel(apiBase=module-frame) tenant 受評標的
/module-frame/<uid>/ssp-resources/items POST module_frame_ssp_resources_route.py:54 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/ssp-resources/items/<item_uid> PUT module_frame_ssp_resources_route.py:74 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_ssp_resources_route.py:91 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/control-defaults GET module_frame_control_default_route.py:53 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateEditView tenant 控制項預設值
/module-frame/<uid>/control-defaults/<ci> GET module_frame_control_default_route.py:79 J+C(module-frame.read) oscal.resource-library.read module-frame.read 同上 tenant
同上 PUT module_frame_control_default_route.py:103 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant upsert
同上 DELETE module_frame_control_default_route.py:130 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/objective-defaults GET module_frame_control_objective_default_route.py:53 J+C(module-frame.read) oscal.resource-library.read module-frame.read 同上 tenant AO 預設值
同上 PUT module_frame_control_objective_default_route.py:77 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant upsert
/module-frame/<uid>/objective-defaults/<od_uid> GET module_frame_control_objective_default_route.py:113 J+C(module-frame.read) oscal.resource-library.read module-frame.read 同上 tenant
同上 DELETE module_frame_control_objective_default_route.py:138 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/reference-documents GET module_frame_reference_document_route.py:64 J+C(module-frame.read) oscal.resource-library.read module-frame.read 同上 tenant 程序書池
同上 POST module_frame_reference_document_route.py:87 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 子物件歸 update
/module-frame/<uid>/reference-documents/<doc_uid> PUT module_frame_reference_document_route.py:120 J+C(module-frame.update) oscal.resource-library.update module-frame.update 同上 tenant
同上 DELETE module_frame_reference_document_route.py:149 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant 子物件歸 update
/module-frame/<uid>/control-defaults/<ci>/reference-documents POST module_frame_reference_document_route.py:182 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 程序書掛到控制項
.../control-defaults/<ci>/reference-documents/<doc_uid> DELETE module_frame_reference_document_route.py:208 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant
/module-frame/<uid>/objective-defaults/<od_uid>/reference-documents POST module_frame_reference_document_route.py:244 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 程序書掛到 AO
.../objective-defaults/<od_uid>/reference-documents/<doc_uid> DELETE module_frame_reference_document_route.py:270 J+C(module-frame.delete) oscal.resource-library.update module-frame.delete 同上 tenant
/module-frame/<uid>/control-defaults/export GET module_frame_template_import_route.py:69 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateImportDialog tenant 範本內容匯出 Excel,不開 export(定案 4)
/module-frame/<uid>/control-defaults/template/download GET module_frame_template_import_route.py:98 J+C(module-frame.read) oscal.resource-library.read module-frame.read 同上 tenant 空白匯入範本
/module-frame/<uid>/control-defaults/import/template POST module_frame_template_import_route.py:138 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 匯入第 1 步:驗檔格式。匯入到既有資源庫=改它(定案 2、3)
/module-frame/<uid>/control-defaults/import/verify POST module_frame_template_import_route.py:172 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 第 2 步:逐列解析
/module-frame/<uid>/control-defaults/import/validate POST module_frame_template_import_route.py:206 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 第 3 步:前端改過再驗
/module-frame/<uid>/control-defaults/import POST module_frame_template_import_route.py:240 J+C(module-frame.create) oscal.resource-library.update module-frame.create 同上 tenant 第 4 步:寫入
/module-frame/<uid>/ssp-import-template GET ssp_import_template_route.py:57 J+C(module-frame.read) oscal.resource-library.read module-frame.read ImportExcelPage、ModuleFrameTemplateEditView tenant 九分頁 SSP 匯入範本(空白或帶入現有內容)
/ssp-import-template GET ssp_import_template_route.py:142 J+C(module-frame.read) oscal.resource-library.read module-frame.read DownloadSspBlankTemplateDialog tenant 依框架版本的空白範本
/module-frame/<uid>/ssp-export GET mf_ssp_export_route.py:54 J+C(module-frame.read) oscal.resource-library.read module-frame.read ModuleFrameTemplateEditView(MODULE_FRAME_SSP_DOCUMENT_EXPORT) tenant 範本 SSP 匯出 docx/pdf,不開 export(定案 4)

專案 SSP(oscal.ssp)

程式 api/oscal/routes/ssp/,service 在 app/oscal/service/ssp_*。13 支全部只有 jwt_required,專案層守門逐 method 對過 service(require_participant/require_manager),沒有漏。FE 經 SspService.js 呼叫,使用頁:ProjectPlanningView、SspTabPanel、SspBasicSection、SspLeveragedSection、ComponentsLeveragedInventoryTab、DocumentPoolPanel、DocumentLinkDialog、SspImportDialog、RoundApAuthoringView、ProjectAuditorOverview、DashboardView(未逐支核)。範圍層一律 org_unit(SSP 跟著專案)。

子物件不拆資源,寫入一律 oscal.ssp.update(理由見「SSP 是否拆子資源」)。

route 路徑(前綴 /ssp/<ssp_uid>) HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/system-characteristic GET ssp_system_characteristic_route.py:26 J;成員:參與者 oscal.ssp.read 無 SspBasicSection(useSspSystemCharacteristic) org_unit
同上 PUT ssp_system_characteristic_route.py:41 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/components GET ssp_components_route.py:30 J;成員:參與者 oscal.ssp.read 無 ComponentsLeveragedInventoryTab org_unit
同上 POST ssp_components_route.py:43 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/components/<item_uid> PUT ssp_components_route.py:61 同上 oscal.ssp.update 無 同上 org_unit
同上 DELETE ssp_components_route.py:76 同上 oscal.ssp.update 無 同上 org_unit
/inventory-items GET ssp_inventory_items_route.py:29 J;成員:參與者 oscal.ssp.read 無 同上 org_unit
同上 POST ssp_inventory_items_route.py:42 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/inventory-items/<item_uid> PUT ssp_inventory_items_route.py:60 同上 oscal.ssp.update 無 同上 org_unit
同上 DELETE ssp_inventory_items_route.py:75 同上 oscal.ssp.update 無 同上 org_unit
/leveraged GET ssp_leveraged_route.py:23 J;成員:參與者 oscal.ssp.read 無 SspLeveragedSection(useSspLeveraged) org_unit
同上 POST ssp_leveraged_route.py:36 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/leveraged/<item_uid> PUT ssp_leveraged_route.py:54 同上 oscal.ssp.update 無 同上 org_unit
同上 DELETE ssp_leveraged_route.py:69 同上 oscal.ssp.update 無 同上 org_unit
/parties GET ssp_party_route.py:24 J;成員:參與者 oscal.ssp.read 無 useSspParties org_unit
同上 POST ssp_party_route.py:39 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/parties/<party_uid> PUT ssp_party_route.py:59 同上 oscal.ssp.update 無 同上 org_unit
同上 DELETE ssp_party_route.py:75 同上 oscal.ssp.update 無 同上 org_unit
/ssp-resources GET ssp_resources_route.py:29 J;成員:參與者 oscal.ssp.read 無 ModuleFrameSspInfoSystemsPanel(apiBase=ssp) org_unit 受評標的
/ssp-resources/items POST ssp_resources_route.py:46 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/ssp-resources/items/<item_uid> PUT ssp_resources_route.py:64 同上 oscal.ssp.update 無 同上 org_unit
同上 DELETE ssp_resources_route.py:79 同上 oscal.ssp.update 無 同上 org_unit
/control-tree GET ssp_control_implementation_route.py:139 J;成員:參與者 oscal.ssp.read 無 ProjectPlanningView、RoundApAuthoringView、RoundAuditReviewView、ProjectAuditorOverview、EvidenceClassificationReview、AutoClassifyBatch org_unit 稽核頁共用,見「先看這幾點」3
/control-implementations GET ssp_control_implementation_route.py:109 J;成員:參與者 oscal.ssp.read 無 SspService(SoA 列表) org_unit
/control-implementation/<ci> GET ssp_control_implementation_route.py:50 J;成員:參與者 oscal.ssp.read 無 同上 org_unit
同上 POST ssp_control_implementation_route.py:67 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit upsert
同上 PUT ssp_control_implementation_route.py:87 同上 oscal.ssp.update 無 同上 org_unit upsert
/control-implementation/<ci>/objective/<si> POST ssp_control_implementation_route.py:156 同上 oscal.ssp.update 無 同上 org_unit
同上 PUT ssp_control_implementation_route.py:177 同上 oscal.ssp.update 無 同上 org_unit
.../objective/<si>/document/<file_id> DELETE ssp_control_implementation_route.py:199 同上 oscal.ssp.update 無 同上 org_unit
/control-implementation/<ci>/reference-documents POST ssp_control_implementation_route.py:222 同上 oscal.ssp.update 無 同上 org_unit
/control-implementation/<ci>/reference-document/<doc_uid> DELETE ssp_control_implementation_route.py:243 同上 oscal.ssp.update 無 同上 org_unit
.../objective/<si>/reference-documents GET ssp_control_implementation_route.py:261 J;成員:參與者 oscal.ssp.read 無 同上 org_unit
同上 POST ssp_control_implementation_route.py:278 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
.../objective/<si>/reference-document/<doc_uid> DELETE ssp_control_implementation_route.py:300 同上 oscal.ssp.update 無 同上 org_unit
/document-pool GET ssp_document_pool_route.py:43 J;成員:參與者 oscal.ssp.read 無 DocumentPoolPanel org_unit 程序書池
同上 POST ssp_document_pool_route.py:55 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
/document-pool/<doc_uid> DELETE ssp_document_pool_route.py:73 同上 oscal.ssp.update 無 同上 org_unit
/control-implementation/<ci>/document-mappings GET ssp_document_pool_route.py:90 J;成員:參與者 oscal.ssp.read 無 DocumentLinkDialog org_unit
同上 POST ssp_document_pool_route.py:103 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
.../document-mapping/<doc_uid> DELETE ssp_document_pool_route.py:122 同上 oscal.ssp.update 無 同上 org_unit
.../objective/<si>/document-mappings GET ssp_document_pool_route.py:140 J;成員:參與者 oscal.ssp.read 無 同上 org_unit
同上 POST ssp_document_pool_route.py:154 J;成員:manager+可寫 oscal.ssp.update 無 同上 org_unit
.../objective/<si>/document-mapping/<doc_uid> DELETE ssp_document_pool_route.py:174 同上 oscal.ssp.update 無 同上 org_unit
/control-implementations/export GET ssp_control_impl_import_route.py:34 J;成員:參與者 oscal.ssp.export 無 SspService.exportExcel(SspTabPanel) org_unit SoA 匯出 Excel
/control-implementations/import POST ssp_control_impl_import_route.py:57 J;成員:參與者(只驗不寫,ssp_control_impl_import_service.py:220) oscal.ssp.import 無 SspImportDialog org_unit 匯入第 1 步:上傳並驗證。整條匯入流程同一個動詞
/control-implementations/import/validate POST ssp_control_impl_import_route.py:80 J;成員:參與者 oscal.ssp.import 無 同上 org_unit 第 2 步:前端改過再驗
/control-implementations/import/confirm POST ssp_control_impl_import_route.py:101 J;成員:manager+可寫 oscal.ssp.import 無 同上 org_unit 第 3 步:寫入
/excel-import/upload POST ssp_scoped_excel_import_route.py:42 J;成員:manager+可寫(ssp_excel_import_app_service.py:823) oscal.ssp.import 無 SspTabPanel(SspExcelImportService) org_unit 九分頁 Excel 匯入專案 SSP;之後的預覽/確認走下段雙目標端點
/excel-template GET api/module_frame/routes/ssp_import_template_route.py:100 J+C(module-frame.read);成員:參與者 oscal.ssp.export module-frame.read SspService(SspTabPanel) org_unit 程式住在 module_frame 但資源是 SSP。帶入現有內容模式會輸出整份 SSP,歸 export;今天掛 module-frame.read 是錯資源
/export GET ssp_export_route.py:55 J;成員:參與者 oscal.ssp.export 無 ProjectPlanningView(SSP_DOCUMENT_EXPORT) org_unit 匯出 docx/pdf/odt

雙目標匯入(SSP 或資源庫,依上傳時的目標判)

上傳時選 source_type:ssp=專案 SSP;module_frame=既有資源庫;framework_version(Excel)或不帶來源(Word「新建」)=用檔案新建資源庫。能力點必須在 service 層依目標判(今天資源庫兩條已經這樣做:V(module-frame.create)、assert_can_overwrite_resource_library 查 V(module-frame.update),app/oscal/service/resource_library_overwrite_guard.py:12-34)。下表「新三段名」寫成依目標三選一。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/ssp-excel-imports/parse POST ssp_excel_import_route.py:36 J;依目標:ssp→成員 manager+可寫;module_frame→V(module-frame.update)+租戶可寫;framework_version→V(module-frame.create)(ssp_excel_import_app_service.py:809-829) ssp→oscal.ssp.import;module_frame→oscal.resource-library.update;framework_version→oscal.resource-library.create module-frame.create、module-frame.update ImportExcelPage、SspTabPanel 依目標 定案 2
/ssp-excel-import/<parse_uid> GET ssp_excel_import_route.py:71 J;ssp→成員:參與者;其他→只比租戶 同上依目標 無 ImportExcelPreviewPage 依目標 預覽
同上 DELETE ssp_excel_import_route.py:85 J;ssp→成員 manager;其他→只比租戶 同上依目標 無 同上 依目標 廢棄解析單
/ssp-excel-import/<parse_uid>/confirm POST ssp_excel_import_route.py:103 J;ssp→成員 manager+可寫;module_frame→V(module-frame.update);framework_version→只比租戶(ssp_excel_import_app_service.py:550 起) 同上依目標 module-frame.update 同上 依目標 新建資源庫那條確認端沒查能力點,見「守門缺口」3
/ssp-docx-imports/parse POST ssp_docx_import_route.py:33 J;ssp→成員 manager+可寫;新建→V(module-frame.create);既有資源庫→V(module-frame.update)(ssp_docx_import_app_service.py:150-173) ssp→oscal.ssp.import;既有→oscal.resource-library.update;新建→oscal.resource-library.create module-frame.create、module-frame.update SspDocxImportPage 依目標
/ssp-docx-import/<parse_uid> GET ssp_docx_import_route.py:64 J;ssp→成員:參與者;其他→只比租戶 同上依目標 無 同上 依目標
同上 DELETE ssp_docx_import_route.py:77 J;ssp→成員 manager;其他→只比租戶 同上依目標 無 同上 依目標
/ssp-docx-import/<parse_uid>/confirm POST ssp_docx_import_route.py:96 J;ssp→成員 manager+可寫;既有→resolve_import_confirm_target(update,或 create+本人建的空殼);新建且未建殼→只比租戶 同上依目標 module-frame.create、module-frame.update 同上 依目標 同 Excel,見「守門缺口」3

流程範本(flow.template)

程式 api/flow_engine/routes/flow_template_route.py,11 個 method 已全掛守門(:23-32 定義四個 decorator),錯誤碼用 GRC_FLOW_TEMPLATE_NO_PERMISSION。對應頁 /flow/template-manage(FlowTemplateManageView.vue、FlowTemplateEditorView.vue),FE 用 hasCap('flow_template.create/update/delete') 控按鈕(FlowTemplateManageView.vue:27-29、FlowTemplateEditorView.vue:50-51)。範圍層 tenant(compliance.flow_templates 只有 tenant_id)。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/flow-engine/flow-templates GET flow_template_route.py:39 J+C(flow_template.read 或 project.create) flow.template.read flow_template.read、project.create FlowTemplateManageView、ProjectCreateView tenant 建專案第一步選範本,所以放行 grc.project.create 要保留
同上 POST flow_template_route.py:55 J+C(flow_template.create) flow.template.create flow_template.create FlowTemplateManageView tenant
/flow-engine/flow-templates/<uid> GET flow_template_route.py:69 J+C(flow_template.read 或 project.create) flow.template.read flow_template.read、project.create FlowTemplateEditorView、FlowTemplatePreviewDialog(ProjectCreateView 內) tenant
同上 PUT flow_template_route.py:77 J+C(flow_template.update) flow.template.update flow_template.update FlowTemplateEditorView tenant
同上 DELETE flow_template_route.py:87 J+C(flow_template.delete) flow.template.delete flow_template.delete FlowTemplateManageView tenant 軟刪
/flow-engine/flow-templates/<uid>/duplicate POST flow_template_route.py:99 J+C(flow_template.create) flow.template.create flow_template.create 同上 tenant
/flow-engine/flow-templates/<uid>/publish PUT flow_template_route.py:117 J+C(flow_template.update) flow.template.publish flow_template.update FlowTemplateEditorView tenant 定案 7;不開 publish 就歸 update
/flow-engine/flow-templates/<uid>/unpublish PUT flow_template_route.py:130 J+C(flow_template.update) flow.template.publish flow_template.update 同上 tenant 同上
/flow-engine/flow-templates/validate POST flow_template_route.py:147 J+C(flow_template.update) flow.template.update flow_template.update 同上 tenant 編輯器的檢查,不寫入
/flow-engine/stage-objects GET api/flow_engine/routes/stage_object_route.py:16 J flow.template.read 無 FlowTemplateEditorView、FlowTemplatePreviewDialog(含 ProjectCreateView) tenant 輔助端點(屬性面板下拉)。建專案預覽也打,守門要比照列表用「read 或 grc.project.create」

查核項目層流程定義(T-1.1 留下)

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/flow-engine/process-definition/<uid> GET api/flow_engine/routes/flow_engine_route.py:27 J;無任何檢查 先解析歸屬:專案→grc.project.read+專案成員;資源庫→oscal.resource-library.read(定案 6) 無 WorkflowSetupEditor(/workflow/workflow-setup),由 ProjectPlanningView:1390、ModuleFrameItems:103、ModuleFrameTemplateEditView:107、ModuleFrameDetail:150 開啟 tenant 讀查核項目的 BPMN(WorkflowTemplateService.get_workflow_template_by_uid,套件 jedi_flow_engine/app/service/workflow_template_service.py:46);寫入端是 PUT /module-frame/item/xml(update)。定案 6
§5

SSP 是否拆子資源

建議不拆,全部歸 oscal.ssp。

  • 若拆,會有:oscal.ssp(系統特性)、oscal.ssp-component(元件、資產清單、受評標的)、oscal.ssp-party(人員與組織)、oscal.ssp-leveraged(沿用授權)、oscal.ssp-control-implementation(控制實作與 AO 現況)、oscal.ssp-document(程序書池與關聯),六個資源各四個基本動詞共 24 個能力點。
  • 不拆的理由:
    • 寫入的專案層門檻全部一樣(manager+當前輪次仍在規劃),沒有任何一塊交給別的角色寫;公司層拆了也表達不出專案層沒有的差異。
    • SSP 頁是一張表單分好幾個分頁,客戶不會只授權「能改人員、不能改元件」。
    • 拆了以後角色漏勾一格,SSP 頁某個分頁的按鈕就 403 且沒有錯誤訊息,正是本 FR 要避免的形狀。
  • 不拆時:讀=oscal.ssp.read;所有子物件新增、修改、刪除=oscal.ssp.update;Excel/Word 匯入整條流程=oscal.ssp.import;docx/pdf 匯出、SoA Excel 匯出、帶入內容的匯入範本=oscal.ssp.export。
  • 反悔條件:客戶要求「控制實作現況交給被稽核單位寫、但不能改 SSP 基本資料」時,把控制實作拆成 oscal.ssp-control-implementation,其餘不動。這也要專案層守門同時拆(今天控制實作寫入只給 manager)。
§6

流程執行歸 flow 或 grc

建議歸 grc,照 T-1.1 已列的放法。

  • 流程執行(任務提交、退回、待辦、佐證、稽核階段推進與回退)全部在專案底下,守門是專案成員角色,資源是任務與稽核輪次,跟流程範本的設計沒有關係。放在 flow 會變成「有 grc.job 卻要另勾 flow 才能提交任務」。
  • flow 模組因此只剩流程範本一個資源,是否保留獨立模組見定案 8。
§7

現有能力點反查(DEV 12 個)

舊名 主表有 route 守它? 新名 其他引用處
compliance-framework.read 無 oscal.framework.read 綁 /compliance-framework/compliance-framework-manage 選單;角色範本 core/role_templates.py:18(一般成員組);宣告 core/host_capabilities.py:32
compliance-framework.create 無(寫入改由 service 層 require_platform_admin 守) oscal.framework.create 選單同上;core/host_capabilities.py:30(is_platform=True);scripts/sql/2026-06-30-compliance-framework-platform-capability.sql
compliance-framework.update 無(同上) oscal.framework.update 選單同上;core/host_capabilities.py:33
compliance-framework.delete 無(同上) oscal.framework.delete 選單同上;core/host_capabilities.py:31
module-frame.read 有:資源庫讀取全部、範本 SSP 參照、SSP 匯入範本三支、範本匯出 oscal.resource-library.read 角色範本 :17(一般成員組);宣告 core/host_capabilities.py:24;/ssp/<uid>/excel-template 也掛它,改名時改成 oscal.ssp.export
module-frame.create 有:建、複製、子物件新增、匯入 oscal.resource-library.create service 層:ssp_excel_import_app_service.py:814、ssp_docx_import_app_service.py:168、resource_library_overwrite_guard.py:29;宣告 :22
module-frame.update 有:改、子物件修改、BPMN 儲存 oscal.resource-library.update service 層:resource_library_overwrite_guard.py:14,26、module_frame_import_service.py:143;宣告 :25
module-frame.delete 有:刪除、子物件刪除 oscal.resource-library.delete 宣告 :23
flow_template.read 有:列表、單筆 flow.template.read 角色範本 :49(唯讀檢視者組);宣告 core/host_capabilities.py:62
flow_template.create 有:新增、複製 flow.template.create FE FlowTemplateManageView.vue:27、FlowTemplateEditorView.vue:50;宣告 :60
flow_template.update 有:改、發布、取消發布、驗證 flow.template.update(發布另議) FE FlowTemplateManageView.vue:28、FlowTemplateEditorView.vue:51;宣告 :63
flow_template.delete 有:刪除 flow.template.delete FE FlowTemplateManageView.vue:29;宣告 :61

workflow.* 四個也在 DEV 查詢結果內,已由 T-1.1 判刪除(見 a1-grc.md「定案結論」2),本份不重複。

沒有任何 route 用到的能力點(孤兒)

compliance-framework.* 四個。它們決定框架管理頁選單看不看得到,一般成員組也帶 read;但端點不看它:讀取不檢查,寫入看的是「是不是總部租戶的管理員」。改名後主表對應 route 掛上 oscal.framework.*,寫入要不要同時保留 require_platform_admin 見定案 1。

§8

定案結論

決策者 2026-10-06 逐條裁決。

  1. 合規框架寫入掛 oscal.framework.create/update/delete/import(is_platform = true),service 層 require_platform_admin() 保留,兩道都過。
  2. 資源庫不開 import:檔案新建=create、匯入既有=update。
  3. 子物件寫入一律歸主資源 update(資源庫、SSP、框架三處);A.2 補配時查 DEV 有無「有 create/delete 沒 update」的角色。
  4. 資源庫與框架匯出歸 read,只有 SSP 開 oscal.ssp.export。
  5. 框架版本下載 OSCAL:/token 掛 oscal.framework.read,下載端點只驗憑證。
  6. 流程定義端點 GET /flow-engine/process-definition/<uid>:不用「擇一放行」。 先解析這張 BPMN 屬於哪個專案或資源庫:屬專案→檢查 grc.project.read+專案成員;屬資源庫→檢查 oscal.resource-library.read。修法寫入 T-4.6。理由:先解析資源再判單一權限才是正統做法,擇一是症狀不是設計(design 通則 C)。
  7. 開 flow.template.publish,涵蓋發布與取消發布。
  8. flow 維持獨立模組。 理由:流程範本是全租戶共用的設計資產,改壞影響之後所有新專案,不該隨 grc.*.* 萬用字元被專案主管順帶拿到;矩陣多一個單列分組是小代價。T-1.6 看整體矩陣後若不合再翻,只換前綴。
  9. 資源名 oscal.resource-library、oscal.framework。
  10. 資源庫範圍層 tenant。

流程範本列表「flow_template.read 或 project.create」維持(建專案選範本情境,design 通則 B)。

§9

確認要補的守門缺口

本卡只讀,修正另開卡。

  1. (併入 T-4.6)框架解析單詳情沒有任何檢查:GET /oscal-framework-parse-jobs/<uid>(framework_parse_job_route.py:127,service get_detail 不呼叫 require_platform_admin),同租戶任何登入者給 uid 就能看解析結果。因匯入只限總部租戶,實際暴露面只在總部租戶內。
  2. (併入 T-4.6)資源庫列表沒守 read:POST /oscal/resource-libraries/list(resource_library_route.py:29)只驗登入,單筆 GET /module-frame/<uid> 卻要 module-frame.read。沒有 read 的人看不到單筆、卻列得出全部名稱。
  3. (併入 T-4.6)用檔案新建資源庫的確認端不查能力點:Excel framework_version 目標(ssp_excel_import_app_service.py:550 _confirm_create_resource_library)與 Word 新建且尚未建殼的情況,確認時只比租戶。能力點只在上傳那一步查,同租戶另一個沒有建立權的人拿到 parse_uid 就能代為確認。
  4. (併入 T-4.6)流程定義端點沒有任何檢查:修法見定案 6。
  5. (記錄,A.2 順手處理)SSP 權限檢查器有兩支沒人用的方法:SspPermissionChecker.require_read_access/require_write_access(common/authz/ssp.py:167-213,支援範本 SSP 的雙模式)全專案零呼叫,13 支 SSP service 都走只認專案 SSP 的 require_participant/require_manager。範本 SSP 改走 /module-frame/<uid>/... 那組端點,所以沒有破口;但這兩支的 _require_admin_for_template 用的是帳號層 is_admin(檔內自己標了「既有誤用」),日後有人接上要先改。補守門時順手決定刪掉或接上。
  6. (記錄,A.2 順手處理)資源庫舊匯入三支沒有前端呼叫:/module-frame/import/verify、/module-frame/import、/module-frame/import/yaml(module_frame_import_route.py),FE 常數 MODULE_FRAME_IMPORT_VERIFY、MODULE_FRAME_IMPORT 無人引用,YAML 匯入連常數都沒有。疑似死碼,退役前先查有沒有外部腳本在打。
  7. (記錄)三個 blueprint 都沒有授權模組軸:合規框架、資源庫、SSP、流程範本都不檢查 require_license。這幾項不在 INFRASTRUCTURE_MODULES(common/authz/license.py),要不要掛、掛哪個模組由授權那條線決定,本份只記錄。
§10

改名時要一起動的地方(給下一棒)

  • core/host_capabilities.py:22-25(module-frame)、:30-33(compliance-framework)、:60-63(flow_template);新增 oscal.ssp.* 六個與 oscal.framework.import
  • core/role_templates.py:17-18(一般成員組:module-frame.read、compliance-framework.read)、:49(唯讀檢視者:flow_template.read);一般成員組要加 oscal.ssp.read(控制項樹被稽核頁共用)
  • route decorator:api/module_frame/routes/ 15 支全部、api/oscal/routes/resource_library_route.py:43、module_frame_template_ssp_route.py:31、api/flow_engine/routes/flow_template_route.py:23-32
  • 範圍外放行條件:module_frame_route.py:26 與 flow_template_route.py:24 的 project.create → grc.project.create(T-1.1 也列了這兩處)
  • service 層字串:app/oscal/service/ssp_excel_import_app_service.py:814、ssp_docx_import_app_service.py:168、resource_library_overwrite_guard.py:14,26,29、app/module_frame/service/module_frame_import_service.py:143
  • DEV route_capabilities:/compliance-framework/compliance-framework-manage、/module-frame/module-frame、/flow/template-manage 三頁的綁定
  • FE:FlowTemplateManageView.vue:27-29、FlowTemplateEditorView.vue:50-51 的 hasCap('flow_template.*');資源庫與框架頁目前不讀 hasCap
  • common/authz/menu_license_filter.py:15 註解提到 compliance-framework,改名後同步

沿革見 FR-132-LOG.md 與 git log。