FR-037 OSCAL v1.2.2 關聯式 Schema 審查報告

日期 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)

§0 TL;DR

整體品質高: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 一次建完)。


§1 必填/選填錯置(審查點 1)— 僅 1 處

嚴重度 表.欄位 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"]
  • v1.2.2 的 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)

§3 參照原則(審查點 3)— 原則本身執行正確,但有 4 處 JSONB 違例

FK 化(同 DB 內)與 token/href 保留(跨文件)的大方向全對:implemented_requirements.control_idstatements.statement_idcd_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_findingsimplementation_statement_uuid(finding → SSP statement 的跨文件參照,[0..1])— 整欄無去處,props/links 也收不住。

§4 coverage 漏接(官方有、SQL 完全沒去處)

嚴重度 位置 漏接內容
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)

§5 約束強化建議(DB 完整性,非 OSCAL 對錯)

  1. 多型 nullable FK 缺 XOR CHECK(4 組,全檔系統性問題)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),否則可孤兒列或雙親列。
  2. profile_imports:include-all XOR include-controls 是 OSCAL required 語意(anyOf 兩分支),目前兩欄皆可空且無註明。
  3. catalog_control_params:values 與 select 互斥,可加 CHECK。
  4. root 表 metadata_id 應 UNIQUE(自稱 1:1 但 DB 不擋共用,resources「經 metadata 定位 root」的前提會失效)。
  5. 被參照 token 可加唯一約束:UNIQUE(catalog_id, control_id)UNIQUE(metadata_id, role_id)UNIQUE(metadata_id, uuid)(parties/resources)。
  6. 冗餘索引 3 條:idx_ssp_syschar_ssp / idx_ssp_sysimpl_ssp / idx_ssp_ctrlimpl_ssp(欄位已 UNIQUE);profile_imports.source_profile_id 漏索引。
  7. 註解事實錯誤(部分已隨 S1 修正):檔頭 L33 mapping required 漏 provenance;ap_tasks.timing 三處註解誤寫 within-period、漏 at-frequency;observation.methods 枚舉漏 UNKNOWN;task.type / implementation_status_state 是開放值域(anyOf token),註解寫得像封閉 enum。
  8. 檔頭 L37 速查「import-profile/ssp/ap: [href]」對 profile 的 import 不成立(v1.2.2 profile import 的 required 是 include-all/include-controls 擇一,href 反而選填)。

§6 JSONB 折衷評估(審查點 4)

合理保留(整包存取、無 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 或外部儲存)。

§7 可執行性(實測)

  • 本機拋棄式 PostgreSQL 16:psql --single-transaction -v ON_ERROR_STOP=1 一次成功,45 表 / 673 欄全建、0 表 0 欄漏 COMMENT。保留字("class"/"end")、usage/start 裸用皆無問題。S1 註解豐富化後重跑亦通過。
  • dbdiagram.io 匯入未實測,已知風險:加引號欄位("class"/"end")與 COMMENT ON 的解析支援度、text[]/uuid[] 陣列型別、跨表多型 FK 畫線——建議匯入時若報錯,優先檢查這幾類。
  • 註解豐富化進度:SECTION A+B(9 表 155 條 COMMENT)已完成並驗證;SECTION C~I(36 表)尚未做(user 暫停)。

§8 過度設計評估(user 追加審查維度)

產品使用面盤點結論(證據見下):產品實際用 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 表,建議等需求再開)

  • Component Definition 整個 SECTION E(5 表):產品零使用(用自家 InformationSystem 替代),且無任何 import/export component-definition 的路徑。這是 45 表中最明確的超前設計。
  • Mapping 整個 SECTION I(2 表)+ 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_implementationsssp_control_implementationsap_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)。

§8.5 v2 產出(2026-06-12 user 拍板後)

User 決策:只砍 Mapping 全段(3 表),CD 與 POA&M 保留(重構將全面採 OSCAL 模式);一併做官方對齊修正、不加約束強化。產出 oscal-catalog-and-roots-schema-v2.sql42 表 / 650 欄,原 45/673),已在本機拋棄式 PostgreSQL 16 實跑通過、0 表 0 欄漏 COMMENT。v2 內容:

  • 裁剪:mapping_collections / mappings / mapping_maps 與其索引(§1 唯一「高」級 finding 隨之消滅)
  • 補欄(21 欄):catalog/group 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_type
  • 移除:catalog_groups.remarks 幽靈欄(§4)
  • 修正:§5.7/§5.8 列的全部註解錯誤(timing 三態、methods 含 UNKNOWN、開放值域標註、mapping required 速查、profile import href 速查);多型 FK/互斥語意在 COMMENT 標明「應用層保證」(依拍板不加 CHECK)
  • 未處理(留待決策):§3 的 4 處 JSONB 參照違例(關聯化 vs 政策補例外)、§5 約束強化、§8 wrapper 合併與 deferred 子結構
  • 註解豐富化進度更新:SECTION A+B(S1)與 7 張 root 表(S2 在中止前已完成 SECTION C root 的部分)已豐富化;profile_imports 與 SECTION D~H 仍為原版精簡註解

§9 待 user 決策(彙整,含 handoff §5 原有項)

  1. 8 root 的 status 產品欄移除與否(handoff §5.1)
  2. assessment-common 共用表方案:審查支持共用(兩版定義逐欄 diff 為零),條件是補 XOR CHECK(§5.1)
  3. AP deferred 子結構補不補(handoff §5.3)→ 結合 §8.B 的「兩套並存」問題一起決
  4. mapping model 做不做(handoff §5.4)→ 審查建議:先不做(§8.C)
  5. §3 的 4 處參照違例:關聯化 vs 政策補例外
  6. §4 漏接欄位逐項補欄(多數是加 nullable 欄/JSONB,低風險 additive)
  7. §8.C 的 YAGNI 範圍裁剪與 wrapper 合併