以 NIST OSCAL 5 份官方 JSON schema 的 required 為權威,逐欄位比對我們 oscal.* ORM,找出缺漏的必填物件 / 欄位,並回答 import 類型欄位的解法。供下一輪 table 調整一次到位。
Catalog / Profile 的必填欄位幾乎全到位;缺口集中在 SSP(實作層)與 AR(評估層),外加跨模型的 back-matter 大洞。「一定必填且完全缺」其實很少——只有 3 條,加上 6 條「欄位在但約束錯」。其餘多屬「不啟用該功能就不違規」的條件式必填。
必填要分「一定必填」與「條件式必填」——差別決定要不要補。
| 模型 | 缺的必填 | 必填理由 |
|---|---|---|
| SSP | information-type 子樹 title / description;categorization.system;C/I/A impact.base | system-characteristics 必填 system-information → 必填 information-types[],一路必填(FIPS 199 分類) |
| SSP | control-implementation.description | control-implementation 是 root 必填,其 description 必填;我們無 header 列描述 |
| AR | result.description | result 必填,description 必填;我們只有 remarks 欄 |
| 模型 | 欄位 | 問題 |
|---|---|---|
| SSP | ssp_system_characteristics.status | 值 active/archived ≠ OSCAL state(operational / under-development / under-major-modification / disposition / other) |
| SSP | ssp_components.status / .description | nullable,OSCAL component 兩者必填 |
| AR | assessment_result_datas.title / .started_at | nullable,OSCAL result.title / start 必填 |
| AR | assessment_result_findings.title | nullable,OSCAL finding.title 必填 |
| AP | assessment_plan_tasks.task_type | 被 manual/automated/hybrid 占用,OSCAL task.type(milestone/action) 等於沒存 |
| Catalog | catalog_control_parts.prose | 反向:我們 NOT NULL 比 OSCAL 嚴(part.prose 其實選填),會擋匯入 |
| 模型 | 結構 | 一旦使用即必填 |
|---|---|---|
| SSP | by-component | component-uuid / uuid / description / implementation-status.state |
| SSP | system-user + authorized-privilege | user.uuid;title + functions-performed |
| SSP | leveraged-authorization | party_uuid / date_authorized (欄位在但 nullable) |
| SSP | set-parameter / diagram | param-id + values;diagram.uuid |
| AR | finding.target | type / target-id / status / implementation-status.state |
| AR | observation / risk | observation: uuid/description/methods/collected;risk: uuid/title/description/statement/status |
| POA&M | poam-item | title / description |
三個必填 import 都是 OSCAL 跨文件參照(href = URI 指向另一份 OSCAL 文件)。
| OSCAL 必填 | 我們存的 | 型別 |
|---|---|---|
SSP import-profile.href | ssps.profile_id | int FK → profiles |
AP import-ssp.href | assessment_plans.ssp_id | String(36) 存 stringified int,無 FK |
AR import-ap.href | assessment_results.assessment_plan_id | int FK → assessment_plans |
uid(UUID v4),所以 內部 id → 目標.uid 永遠可解析。
href="#<uuid>"匯出時 id → 查目標 uid → 合成 href="#<uuid>"。
int FK 存不了「我們沒有的文件」的 href(外部 URL / 對方 uuid)。
import_href(String) + optional import_remarksassessment_plans.ssp_id 現存 stringified int(memory feedback_ap_ssp_id_varchar_stringified_int)。VARCHAR(36) = UUID 長度,正好可改存 ssp.uid,語意直接貼 OSCAL href、匯出免查表——但屬 breaking 資料遷移,單獨評估。
import_href(+remarks),並建議 ssp_id 改存 uuid。
FR-036 正要動這批 ssp_* 表 → 結構缺口最划算一次補。
| OSCAL 結構 | 必填欄位 / 問題 | 嚴重度 | 我們的現況 |
|---|---|---|---|
| information-type + C/I/A impact FIPS 199 分類 | title / description / categorization.system / impact.base | 🔴 | 完全沒表沒欄位 |
| control-implementation.description | root 必填的 description | 🔴 | 無 header 列描述 |
| system-characteristics.status | state 詞彙 | 🔴 | status 值 active/archived 不對 |
| component.status / .description | 必填卻 nullable | 🔴 | ssp_components.* nullable |
| security-impact-level | C/I/A 三軸 objective | 🟠 | 只有單一 security_sensitivity_level |
| by-component(+ provided/inherited/satisfied) | component-uuid/uuid/description | 🟠 | 只到 requirement+objective,無元件層 |
| system-user + authorized-privilege | user.uuid;title+functions-performed | 🟠 | 只有單一 owner_uid 純量 |
| set-parameter(三層) | param-id + values | 🟠 | 參數值無法 round-trip |
| leveraged-authorization | party_uuid / date_authorized | 🟠 | 欄位在但 nullable |
| diagrams(auth-boundary / network / data-flow) | diagram.uuid + description | 🟡 | 沒 diagram 表 |
| system-id 陣列 + identifier-type | system-id.id | 🟡 | system_identifier 單值 |
ssp_inventory_item_components 仍 FK 到 legacy 表 oscal.ssp_system_implementation_items,與新 ssp_inventory_implemented_components 並存(mid-migration)。動 inventory↔component join 前先確認哪張是 live。| OSCAL 必填 | 狀態 |
|---|---|
| uuid / metadata / task.uuid / task.title | ✅ |
| task.type(milestone/action) 欄位被 manual/hybrid 占用 | 🔴 |
| import-ssp.href 存 stringified int | 🟠 |
| reviewed-controls 攤平有損 | 🟠 |
| OSCAL 必填 | 狀態 |
|---|---|
| uuid / metadata / finding.uuid / .description | ✅ |
| result.description | 🔴 |
| result.title / .start(nullable) | 🔴 |
| finding.title(nullable) | 🔴 |
| finding.target + implementation-status | 🔴 |
| observation(整段) | 🔴 |
| risk(status/characterization/response/log) | 🔴 |
| import-ap.href(存內部 FK) | 🟠 |
compliance.poams 扁平單表 vs OSCAL 獨立模型)| OSCAL POA&M | 必填 | compliance.poams |
|---|---|---|
| root uuid + metadata | R | ✗ 無文件根 / metadata,只有扁平列 |
| poam-item.uuid | R | ✓ uid |
| poam-item.title | R | ✗ 無 title 欄 |
| poam-item.description | R | ~ remediation_plan(語意是 remediation 非 description) |
| related-findings | opt | ~ ar_finding_id 單一,OSCAL 允許多筆 |
catalog / group / control / part 的必填欄位都有對應。
catalog_control_parts.prose NOT NULL 比 OSCAL 嚴(part.prose 選填)→ 放寬 nullable是「已解析的選取控制項清單」,存結果不存規則。
profile.imports 是 root 必填,現壓成單一 catalog_id + 扁平 profile_controls → 匯出保證 imports[] 非空Metadata 的 4 個 root 必填(title / last-modified / version / oscal-version)全到位;缺口是幾個結構性大洞。
| OSCAL 構件 | 必填 | 嚴重度 | 狀態 |
|---|---|---|---|
| back-matter → resource | resource.uuid;rlink.href;base64.value;citation.text | 🔴 | 完全未建模。document_id 是原始檔 blob FK,與 OSCAL resource 無關 — 整體最大結構洞,附件/citation/嵌入證據無法 round-trip |
| hash(rlink 內) | value, algorithm | 🟠 | 未建模(依附 back-matter) |
| document-id | identifier | 🟠 | metadata / resource 的 document-ids[] 未建模 |
| revision | version(逐筆) | 🟡 | oscal_metadatas.revision 是單一純量,存不了歷史清單 |
| 1 | 目標 conformance 等級:完整 OSCAL round-trip(含 risk/observation/back-matter)還是內部資料完整 + 主要必填?決定 Tier C 那批大結構做不做 |
| 2 | 分期:Tier B 約束/詞彙可獨立先發低風險 migration;SSP 群建議與 FR-036 動表同期;AR 群 + back-matter 規模大宜獨立 phase |
| 3 | 與 FR-036 界線:FR-036 管「MF defaults 折進樣板 SSP」;本 FR 管「SSP 等表 OSCAL 欄位補齊」。兩者都動 ssp_*,FR-036 結構先定、本 FR 補在其上 |
| 4 | jedi-oscal 發版:OSCAL ORM 全在套件,補欄位=改套件 → path-dependency dev、user 明示才發版 |
| 5 | 三環境零丟失 migration:新增 NOT NULL 給 default 或先 nullable 回填再收緊;詞彙改值要資料轉換;dev→stg→poc 漸進 |