| 日期 | 2026-06-12 |
| 審查對象 | oscal-catalog-and-roots-schema.sql(45 表 / 673 欄) |
| 對照來源 | docs/reference/oscal_model_schemas/*.json(官方 v1.2.2,8 份)逐表逐欄核對 required 陣列與 properties 全集 |
| 方法 | 6 個平行審查 agent 分段對照 + 主審抽查複驗 + 本機拋棄式 PostgreSQL 16 實跑 + 產品使用面盤點(BE + jedi-oscal) |
| 狀態 | 純審查,未改任何 DDL;SECTION A/B 的 COMMENT 已豐富化(見 §7) |
整體品質高:45 表的 required ↔︎ NOT NULL 逐欄核對近乎全對(僅 1 處錯置)、8 root 無 props/links 正確、resource 有 props 無 links 正確、import FK 的 NOT NULL/nullable 四個全對、AR 版與 POA&M 版 observation/risk/finding 逐欄 diff 為零(共用表有 schema 正當性)。問題集中在:①若干 coverage 漏接(欄位完全沒有去處,匯入會 silent drop)②一個幽靈欄位 ③「同文件參照→FK」政策有 4 處 JSONB 違例(政策自相矛盾)④多型 nullable FK 全部缺 XOR CHECK。可執行性已實測通過(PG16 single-transaction 一次建完)。
| 嚴重度 | 表.欄位 | OSCAL 出處 | 該怎樣 | 現在怎樣 |
|---|---|---|---|---|
| 高 | mappings.source_resource_type / target_resource_type |
mapping-resource-reference required=["type","href"] |
兩欄 NOT NULL 並標 [必填] | nullable,註解未標必填 |
其餘 44 表全對。特別核對過的易錯點(全部正確):
poam_items.uuid nullable ✓(poam-item 是八大 model 子物件中少見 uuid 選填者,required=["description","title"])system-characteristics required 不含 security-sensitivity-level / security-impact-level(1.0 曾必填、已放寬)→ SQL nullable ✓ssp.import_profile_id / ap.import_ssp_id / ar.import_ap_id NOT NULL、poams.import_ssp_id nullable ✓assessment_risks 無 remarks 欄 ✓(OSCAL risk 真的沒有 remarks,不是漏)mapping_collections.provenance NOT NULL ✓(root required 含 provenance)resources 有 props、無 links(用 rlinks)✓FK 化(同 DB 內)與 token/href 保留(跨文件)的大方向全對:implemented_requirements.control_id、statements.statement_id、cd_control_implementations.source、mapping source/target href 保留 text ✓;by_components.component_id FK 化 ✓;party-uuid 全軟式 ✓。
但檔頭「同文件參照一律 FK」政策有 4 處自相矛盾(同文件 uuid 參照卻留 JSONB,會 dangling、查詢要剝 JSONB):
| 嚴重度 | 位置 | 同文件參照目標 |
|---|---|---|
| 中 | ssp_inventory_items.implemented_components(component-uuid) |
ssp_components |
| 中 | cd_capabilities.incorporates_components(component-uuid,檔頭政策明文舉過這個例子) |
cd_components |
| 中 | ap_tasks.dependencies(task-uuid)/ associated_activities(activity-uuid;其 subjects 子欄是 required,JSONB 內 DB 無法強制) |
ap_tasks / ap_assessment_activities |
| 中 | poam_items.related_findings/observations/risks 等 related-* 系列(同文件 uuid;產品現況 compliance.poams 就用 ar_finding_id JOIN,查詢需求真實存在) |
assessment_findings 等 |
→ 二擇一:關聯化成 link 表,或在檔頭政策段明文補「related-*/dependencies 類多對多參照例外留 JSONB」。
另一漏收欄(參照類):
assessment_findings 缺 implementation_statement_uuid(finding → SSP statement 的跨文件參照,[0..1])— 整欄無去處,props/links 也收不住。| 嚴重度 | 位置 | 漏接內容 |
|---|---|---|
| 中 | catalogs |
catalog 層 params[](且行內註解誤示已承接——已隨 S1 修正註解) |
| 中 | catalog_groups |
group 層 params[] 與 parts[](目標框架未用,屬有意識棄守,但無 JSONB 兜底) |
| 中 | catalog_groups.remarks |
反向幽靈欄:OSCAL group 沒有 remarks,寫入的資料匯出時無處可放(待決策移除) |
| 中 | ssp_system_characteristics |
system-information wrapper 自身的 props/links;authorization-boundary / network-architecture / data-flow 三段各自的 props/links/remarks(共 9 欄,SQL 有自註「暫未展開」,gap 屬實) |
| 中 | ssp_information_types |
三軸 impact 的 adjustment-justification(FIPS-199 tailoring 關鍵語意欄)+ impact 的 props/links |
| 中 | assessment_results(root) |
root 層 local-definitions(objectives-and-methods / activities)— 沒接也沒列 deferred,是 silent gap |
| 中 | poams(root) |
root 層 local-definitions(components / inventory-items / assessment-assets;FedRAMP POA&M 常用) |
| 中 | poams.system_id |
OSCAL system-id 是 {id, identifier-type} 結構,只存了 id |
| 中 | mappings |
mapping-description / matching-rationale / confidence-score / coverage / source-gap-summary / target-gap-summary 六欄+resource-reference 的 ns/props/links/remarks |
| 低 | mapping_maps |
ns(relationship 的命名空間) |
| 低 | assessment_plans |
AP local-definitions 自身的 remarks 沒人接(components/inventory/users 已明示 deferred) |
ssp_by_components(implemented_requirement_id/statement_id)、cd_control_implementations(cd_component_id/cd_capability_id)、assessment_observations/risks/findings(ar_result_id/poam_id)、profile_imports(source_catalog_id/source_profile_id)。建議 CHECK (num_nonnulls(a, b) = 1),否則可孤兒列或雙親列。profile_imports:include-all XOR include-controls 是 OSCAL required 語意(anyOf 兩分支),目前兩欄皆可空且無註明。catalog_control_params:values 與 select 互斥,可加 CHECK。metadata_id 應 UNIQUE(自稱 1:1 但 DB 不擋共用,resources「經 metadata 定位 root」的前提會失效)。UNIQUE(catalog_id, control_id)、UNIQUE(metadata_id, role_id)、UNIQUE(metadata_id, uuid)(parties/resources)。idx_ssp_syschar_ssp / idx_ssp_sysimpl_ssp / idx_ssp_ctrlimpl_ssp(欄位已 UNIQUE);profile_imports.source_profile_id 漏索引。ap_tasks.timing 三處註解誤寫 within-period、漏 at-frequency;observation.methods 枚舉漏 UNKNOWN;task.type / implementation_status_state 是開放值域(anyOf token),註解寫得像封閉 enum。合理保留(整包存取、無 WHERE/JOIN):props/links 全系列、metadata.revisions/locations/document-ids/actions、party 聯絡欄、parameter constraints/guidelines、profile merge/modify、by-component export/inherited/satisfied(反悔條件:要做 leveraged authorization 責任鏈對賬時關聯化)、risk characterizations/remediations/risk-log、observation relevant-evidence(反悔條件:證據反查需求)、task timing/subjects、control-selections、attestations/assessment-log。
該關聯化或補政策例外(見 §3 的 4 處違例):implemented_components、incorporates_components、task dependencies/associated-activities、related-* 系列。
邊際個案:metadata.responsible_parties(role-id/party-uuids 都指向已拆表的 roles/parties,是天然 join 表;產品已有 ResponsiblePartyEntity link-record 慣例)— 若只在 SSP 層查可留 JSONB;resources.base64(大檔建議 bytea 或外部儲存)。
psql --single-transaction -v ON_ERROR_STOP=1 一次成功,45 表 / 673 欄全建、0 表 0 欄漏 COMMENT。保留字("class"/"end")、usage/start 裸用皆無問題。S1 註解豐富化後重跑亦通過。COMMENT ON 的解析支援度、text[]/uuid[] 陣列型別、跨表多型 FK 畫線——建議匯入時若報錯,優先檢查這幾類。產品使用面盤點結論(證據見下):產品實際用 OSCAL 的廣度約六成(Catalog/Profile/SSP/AP/AR 主幹),深度約三成。對照本設計(前提:這是刻意的「全 OSCAL、不考慮現行產品」重新設計,所以「對 OSCAL 忠實」本身不算過度設計),分三層評:
A. 有真實依據、不算過度(30 表):共用核心 4、Catalog 樹 5(parts 是 AO 落點,定案需求)、Profile 2、SSP 主幹(syschar/sysimpl/components/inventory/leveraged/ctrl-impl/impl-req/statements/by-components 9)、AP 4、AR 2、共用三表 3、POA&M 2。
B. 規格對但「先開表」超前產品需求(8 表,建議保留設計、實作時可後置):
ssp_system_users / ssp_diagrams / ssp_information_types:產品今天沒有對應持久化(users 只有 owner_uid 一欄、diagrams 零使用、information-types 匯入解析後不落 DB)。是 OSCAL SSP 完整性需要,但可列第二波。ssp_statements 拆到 by-component 雙層:OSCAL 結構要求,保留;但現行產品只做到 control 級陳述。ap_local_objectives / ap_assessment_activities / ap_tasks:runtime AO 與任務在產品另有 assessment_plan_tasks + workflow 體系,這三表是 OSCAL 文件層的對應——兩套並存的關係要先想清楚(誰是 source of truth),否則是雙寫負擔。ar_results:產品 AR 已有自己的三表結構,同樣有兩套並存問題。C. 高度 YAGNI 候選(10 表,建議等需求再開):
mapping_collections root:OSCAL 實驗性 model,產品的跨框架對應另有機制(workflow_execution_control_mapping 是流程↔︎控制項,不是框架↔︎框架);handoff §5.4 本就待拍板——建議先不做。poams(OSCAL 文件 root)+ poam_items:產品 POA&M 是扁平 compliance.poams 且與 AP/AR 工作流深度耦合;除非要做 FedRAMP 式 POA&M 文件匯出,OSCAL 文件層可緩。層數深度評:SSP 鏈 ssps → control_implementations → implemented_requirements → statements → by_components 五層是 OSCAL 本身的結構,不是自己挖的;但查「某 SSP 的所有 by-components」要 4 個 JOIN。建議比照 catalog_control_parts.catalog_control_id 的反正規化手法,在深層表加 ssp_id 直達欄(denormalized,註解標明 app 層保證一致)。1:1 wrapper 表(ssp_system_implementations、ssp_control_implementations、ap_reviewed_controls)各只有 2~4 個實質欄位,可以折進父表(ssps 加 6 欄、AP 加 4 欄)省兩次 JOIN——保留拆表的理由只剩「OSCAL 結構一目了然」,屬風格取捨。
若採 C 全砍 + wrapper 合併:45 表 → 約 31 表,OSCAL 主幹保真不受影響。
產品使用面證據摘要:CD 零讀寫(InformationSystem 替代,app/oscal/service/ssp_inventory_items_app_service.py:37);mapping 僅 workflow↔︎control(infra/grc/repository/workflow_execution_control_mapping_repo_impl.py);POA&M 扁平單表(infra/grc/model/poam_model.py);SSP users/diagrams/info-types 無持久化;AR 無 observations/risks/attestations 實裝;catalog parts 僅匯入快照、無動態讀寫;profile merge/modify 零實作;OSCAL 匯出僅 SSP 一條路(app/oscal/service/export/ssp_export_app_service.py:111-133)。
User 決策:只砍 Mapping 全段(3 表),CD 與 POA&M 保留(重構將全面採 OSCAL 模式);一併做官方對齊修正、不加約束強化。產出 oscal-catalog-and-roots-schema-v2.sql(42 表 / 650 欄,原 45/673),已在本機拋棄式 PostgreSQL 16 實跑通過、0 表 0 欄漏 COMMENT。v2 內容:
mapping_collections / mappings / mapping_maps 與其索引(§1 唯一「高」級 finding 隨之消滅)params、group parts、syschar 三段+system-information 的 props/links/remarks(11 欄)、impact adjustment_justification×3、finding implementation_statement_uuid、AR/POA&M root local_definitions、AP local_definitions_remarks、POA&M system_id_identifier_typecatalog_groups.remarks 幽靈欄(§4)status 產品欄移除與否(handoff §5.1)