🗄️ 本 FR 已於 2026-06-14 ARCHIVED。 盤點後續演變成「全 OSCAL 關聯式 schema 重新設計」(單檔 45→42 表),方向已偏離本文件原本「只盤缺口、供下一輪動表」的定位。User 決定整個重來、不續作 FR-037,新一輪將另開 FR。本 session 成果(v2 schema + 審查報告)保留於本資料夾,收尾說明見
handoff/2026-06-14-FR037-ARCHIVE-SUMMARY.md。
| 項目 | 內容 |
|---|---|
| FR | FR-037 |
| 提出 | 2026-06-10 |
| 狀態 | 🗄️ ARCHIVED(2026-06-14,整個重來、不續作) |
| arc 關聯 | relates to FR-028(SSP OSCAL 對齊)/ FR-036(SSP 表族整併同期動表) |
| 影響 repo | jedi-oscal(OSCAL ORM 全在此)+ BE(compliance.poams)+ 3 DB 環境 |
| 比對基準 | docs/reference/oscal_model_schemas/ 5 份官方 JSON schema + NIST 概念頁 vs jedi-oscal/jedi_oscal/infra/model/ ORM |
定位:這是一份「缺口清單」,不是實作計畫。目的是把 OSCAL 5 個模型的 required 欄位與我們
oscal.*表逐欄位對齊一次,讓未來動表時知道一次要補哪些。實作分期、哪些真要做、做到什麼 conformance 等級,留後續 implementation-plan 決策(見 §7)。
必填要分「一定必填」與「條件式必填」——差別決定要不要補。
| 模型 | 缺的必填 | 必填理由 |
|---|---|---|
| SSP | information-type 子樹(title/description;categorization.system;C/I/A impact.base) | system-characteristics 必填 system-information → 必填 information-types[],一路必填 |
| 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/…) |
| SSP | ssp_components.status / .description |
nullable,OSCAL 必填 |
| AR | assessment_result_datas.title / .started_at |
nullable,OSCAL result.title/start 必填 |
| AR | assessment_result_findings.title |
nullable,OSCAL 必填 |
| AP | assessment_plan_tasks.task_type |
被 manual/hybrid 占用,OSCAL task.type(milestone/action) 等於沒存 |
| Catalog | catalog_control_parts.prose |
反向:我們 NOT NULL 比 OSCAL 嚴,會擋匯入 |
| 模型 | 結構 | 一旦使用即必填 |
|---|---|---|
| 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 |
「一定必填且完全缺」只有 Tier A 3 條 + Tier B 6 條約束。Tier C 不啟用該功能就不算違規,做不做取決於產品 conformance 目標(見 §7)。
"required": [...] 陣列就是 OSCAL 硬性 required 欄位。本盤點逐一抽出比對。oscal.catalogs → catalog_groups → catalog_controls → parts/parameters)所有 OSCAL Catalog required 欄位都有對應欄位:
| OSCAL object | required | 我們的欄位 | 狀態 |
|---|---|---|---|
| catalog | uuid, metadata | catalogs.uid / catalogs.metadata_id |
✅ |
| group | title | catalog_groups.name |
✅(name 兼當 id+title,見下註) |
| control | id, title | catalog_controls.control_id / .control_title |
✅ |
| part | name | catalog_control_parts.part_name |
✅ |
| parameter | (無 required) | catalog_control_parameters |
✅ |
唯一要修的 required 級問題(約束倒置):
catalog_control_parts.prose 是 NOT NULL,但 OSCAL part prose 是 optional。匯入「只有子 part 才有文字、自己無 prose 的結構性 part」會被我們的 NOT NULL 擋下。我們在這裡比 OSCAL 嚴,應放寬為 nullable。非 required 缺口(fidelity,OSCAL optional,本輪不列入硬缺口):
props[] / links[] 在 control/group/part/param 都沒建模(800-53 大量靠 props 如 label/sort-id,目前被擠進 control_id/order_no 或丟失)。catalog_controls/catalog_control_parts 無 self-FK);group 倒是有 parent_group_id 可巢狀。constraints/guidelines/select/how-many/usage/class 沒建模;values 壓成單一 default_value。id(group/part)未存(只有內部隨機 uid),靠 OSCAL id 交叉連結({{ insert: param }}、href="#id")無法直接解析。name 為 String(100),OSCAL title 是 MarkupLine(可超長/含 markup);現行框架(CMMC/ISO/800-53)group 標題都短,低風險。
catalog_control_assessments是內部 AP 範本層,不是 OSCAL Catalog 構件,不列入 OSCAL 盤點。
oscal.profiles → profile_controls)我們的 Profile 是「已解析的選取控制項清單」表示法——存了 import/include/exclude 的結果,丟掉了宣告規則。
| OSCAL object | required | 我們的對應 | 狀態 |
|---|---|---|---|
| profile | uuid, metadata, imports | profiles.uid / .metadata_id / —(imports 被壓成單一 catalog_id + profile_controls 列) |
uuid/metadata ✅;imports ⚠️ |
| import | include-all 或 include-controls(href 非 required) | profile_controls.include 布林(逐控制項) |
⚠️ 語意攤平,無 include-all/matching/with-child-controls |
| merge | flat/as-is/custom(擇一) | — | ❌(OSCAL optional) |
| modify.set-parameters | param-id, values | — | ❌(OSCAL optional,但這是 baseline 注入機構參數值之處) |
| modify.alters | control-id(在 alter 內 required) | — | ❌(OSCAL optional) |
要修的 required 級問題:
profile.imports 是 root required,目前無結構表示。現況 = 單一 catalog_id(FK,不可 null,比 OSCAL 嚴——OSCAL import.href 非 required)+ 扁平 profile_controls。匯出時必須保證 imports[] 非空。最小解:把 catalog_id + include=True 的列合成單一 import.include-controls.with-ids;正解:建 profile_imports 表支援多 catalog + include-all。merge / modify / set-parameters / alters / matching pattern 全是 OSCAL optional,不列本輪 required 缺口,但這是我們 Profile 表示法最大的功能性落差(只知道選了哪些控制項,不知道怎麼選、怎麼客製)。
| OSCAL | 我們的表 | 狀態 |
|---|---|---|
| system-security-plan(uuid/metadata/system-implementation/control-implementation) | ssps + 子表 |
✅ |
| system-characteristics(system-name/description) | ssp_system_characteristics.name / .description |
✅ |
| component(uuid/type/title) | ssp_components.uid / .component_type / .title |
✅ |
| inventory-item(uuid/description) | ssp_inventory_items.uid / .description |
✅ |
| implemented-requirement(uuid/control-id) | ssp_control_implementations.uid / .control_identifier |
✅ |
| statement(statement-id/uuid) | ssp_control_implementation_objectives.statement_identifier / .uid |
✅ |
| 表.欄位 | 現況 | OSCAL | 嚴重度 |
|---|---|---|---|
ssp_components.status |
nullable | component.status required | 🔴 |
ssp_components.description |
nullable | required | 🔴 |
ssp_leveraged_authorizations.party_uuid |
nullable | leveraged-authorization required | 🔴 |
ssp_leveraged_authorizations.date_authorized |
nullable | required | 🔴 |
ssp_system_characteristics.status |
值 active/archived |
status.state ∈ operational/under-development/under-major-modification/disposition/other |
🔴 詞彙完全不對 |
| 缺的 OSCAL 結構 | required 欄位 | 嚴重度 | 說明 |
|---|---|---|---|
| information-type + C/I/A impact(FIPS 199) | system-information.information-types[](system-characteristics required);information-type 的 title/description;categorization.system;confidentiality/integrity/availability-impact 各自的 base |
🔴 | 完全沒表沒欄位。FIPS-199 分類無法匯入匯出,這是 SSP 最大洞 |
| security-impact-level(C/I/A 三軸 objective) | security-objective-confidentiality / -integrity / -availability 三者皆 required | 🔴 | 我們只有單一 security_sensitivity_level,無法拆三軸 |
| by-component(+ export/provided/responsibility/inherited/satisfied) | by-component 的 component-uuid/uuid/description;巢狀 implementation-status.state | 🔴 | 控制項實作只到 requirement+objective,無元件層分解 → leveraged authorization 的責任繼承鏈(provided/inherited/satisfied)整段無法表達 |
| system-user + authorized-privilege | user.uuid;authorized-privilege.title + functions-performed | 🟠 | users[] 整段無表(只有 system-characteristics 上單一 owner_uid 純量) |
| set-parameter | param-id + values(control-impl / impl-req / by-component 三層) | 🟠 | 參數值完全無法 round-trip |
| diagrams(authorization-boundary / network-architecture / data-flow) | diagram.uuid(逐圖)+ 各自 description | 🟡 | 沒 diagram 表;network-architecture / data-flow 整段也缺 |
| system-id 陣列 + identifier-type | system-id.id(required) | 🟡 | 現 system_identifier 單值、無 identifier-type、非陣列 |
profile_id FK 而非 OSCAL href → 內部 OK,匯出靠 mapper 合成 href。responsible_role,非 role-id 參照、非多值。ssp_inventory_item_components(OscalSspInventoryItemComponent)仍 FK 到 legacy 表 oscal.ssp_system_implementation_items,與新的 ssp_inventory_implemented_components 並存(mid-migration)。動 inventory↔︎component join 前先確認哪張是 live。oscal.assessment_plans → groups → controls → tasks)| OSCAL object.field | required | 我們的欄位 | 狀態 |
|---|---|---|---|
| assessment-plan.uuid / metadata | R | assessment_plans.uid / .metadata_id |
✅ |
| import-ssp.href | R | assessment_plans.ssp_id(String(36) stringified int,無 FK) |
🟠 存內部 id 非 href;匯出需 mapper 合成 |
| reviewed-controls.control-selections | R | assessment_plan_controls(+ groups)逐控制項 include 布林 |
🟠 攤平;無 include-all/exclude/statement-ids |
| task.uuid / title | R | assessment_plan_tasks.uid / .title |
✅ |
| task.type(milestone/action) | R | assessment_plan_tasks.task_type 值為 manual/automated/hybrid |
🔴 欄位被占用,OSCAL 的 type 等於沒存;manual/hybrid 應移到 prop |
| assessment-subjects / activity / step / assessment-assets / terms-and-conditions / local-definitions | optional(各自有內部 required) | — | ❌ 皆未建模(OSCAL optional) |
我們的 AP 擴充(非 OSCAL):assessment_plan_groups(鏡射 catalog group,OSCAL AP 無 group 概念)、assessment_task_controls(task↔︎control 直連 M:N,OSCAL 是透過 activity→step→reviewed-controls 間接關聯)。匯出時這兩層無 OSCAL 對應位置。
AP 本輪 required 缺口:🔴 task.type 詞彙占用;🟠 import-ssp.href 型別語意;🟠 reviewed-controls 攤平有損。其餘缺的都是 OSCAL AP root optional。
oscal.assessment_results → datas → controls → findings/evidences)| OSCAL object.field | required | 我們的欄位 | 狀態 |
|---|---|---|---|
| assessment-results.uuid / metadata / results | R | assessment_results.uid / .metadata_id / result_datas[] |
✅ |
| import-ap.href | R | assessment_results.assessment_plan_id(內部 FK) |
🟠 非 href |
| result.uuid | R | assessment_result_datas.uid |
✅ |
| result.title | R | assessment_result_datas.title(nullable) |
🔴 OSCAL required 但可空 |
| result.description | R | —(只有 remarks) |
🔴 沒欄位 |
| result.start | R | assessment_result_datas.started_at(nullable) |
🔴 |
| reviewed-controls.control-selections | R | assessment_result_controls 逐控制項 + verdict |
⚠️ 語意不同:OSCAL 是「選取範圍」,我們存「逐控制項判定」 |
| observation(uuid/description/methods/collected) | R(在 assembly 內) | —(assessment_result_evidences 僅鬆散覆蓋 relevant-evidence,且掛 control 非 observation) |
🔴 整段未建模 |
| risk(uuid/title/description/statement/status + characterization/facets/response/risk-log/threat-id) | R(在 assembly 內) | — | 🔴 整段未建模;remediation 僅剩 poams.remediation_plan 自由文字 |
| finding.uuid | R | assessment_result_findings.uid |
✅ |
| finding.title | R | assessment_result_findings.title(nullable) |
🔴 |
| finding.description | R | assessment_result_findings.description(NN) |
✅ |
| finding.target(type/target-id/status + implementation-status.state) | R | — | 🔴 finding 的核心結構整段缺;我們用 controls.verdict(pass/fail/partial/na)近似,但掛 control 不掛 finding,且值域 ≠ OSCAL(satisfied/not-satisfied) |
| finding.origins / related-observations / related-risks | optional | — | ❌ |
severity/category/ao_uid/recommendation/confidence 是我們的擴充欄位(OSCAL finding 原生無 severity,通常放 prop)。
compliance.poams 扁平單表 vs OSCAL plan-of-action-and-milestones)OSCAL POA&M 是獨立模型(非 AR 一部分):root required uuid/metadata/poam-items;poam-item required uuid/title/description。
| OSCAL POA&M field | required | compliance.poams |
狀態 |
|---|---|---|---|
| root uuid + metadata | R | —(扁平列,無文件根/metadata) | ❌ 無 POA&M 文件概念 |
| poam-item.uuid | R | uid |
✅ |
| poam-item.title | R | — | 🔴 無 title 欄 |
| poam-item.description | R | remediation_plan(最接近) |
🟠 語意是 remediation 非 item description |
| related-findings | optional | ar_finding_id(單一 int) |
🟠 只能接一筆,OSCAL 允許多筆 |
| related-observations / related-risks | optional | — | ❌ |
| origins(負責 actor) | optional | assignee_uid |
🟠 |
| milestones / tasks | — | due_date / closed_at |
🟠 單一日期,無 milestone 清單 |
status(open/in_progress/closed)是我們的擴充(OSCAL 用 prop)。
Metadata 4 個 root required(title/last-modified/version/oscal-version)全到位(oscal.oscal_metadatas);prop/link/role/party/location/responsible-party 的 required 欄位也都有。但有幾個結構性大洞:
| OSCAL 構件 | required | 狀態 | 嚴重度 |
|---|---|---|---|
| back-matter → resource | resource.uuid;rlink.href;base64.value;citation.text | ❌ 完全未建模。root 上的 document_id 是「原始檔 blob」FK,與 OSCAL resource 無關 |
🔴 整體最大結構洞——附件/citation/嵌入證據無法 round-trip |
| hash(back-matter rlink 內) | value, algorithm | ❌ 未建模(oscal_documents.hash 是整檔完整性 hash,非此) |
🟠(依附 back-matter) |
| document-id(metadata.document-ids[] / resource.document-ids[]) | identifier | ❌ 未建模 | 🟠 |
| revision(metadata.revisions[]) | version(逐筆) | ⚠️ oscal_metadatas.revision 是單一 String(20) 純量,存不了 revision 歷史清單 |
🟡 |
非 required(OSCAL optional):party 的 email-addresses/telephone-numbers/addresses 都壓成單一純量(OSCAL 是陣列);address 攤成自由 Text(丟 city/state/postal/country);responsible-role 無專表(與 responsible-party link 混用);prop.uuid/group、link.text、role.short-name、party external-ids/location-uuids/member-of-organizations、metadata.actions 皆未建模。
| # | 表.欄位 | 動作 |
|---|---|---|
| 1 | catalog_control_parts.prose |
NOT NULL → nullable(我們比 OSCAL 嚴,擋匯入) |
| 2 | ssp_components.status / .description |
nullable → NOT NULL(或 mapper 強制) |
| 3 | ssp_leveraged_authorizations.party_uuid / .date_authorized |
nullable → NOT NULL |
| 4 | ssp_system_characteristics.status |
詞彙改 OSCAL state 值域(operational/under-development/…) |
| 5 | assessment_result_datas.title / .started_at |
nullable → NOT NULL(或匯出前強制) |
| 6 | assessment_result_findings.title |
nullable → NOT NULL |
| 7 | assessment_plan_tasks.task_type |
OSCAL type(milestone/action)另立欄位;manual/hybrid 移 prop |
| # | 缺的結構 | 層 | 規模 |
|---|---|---|---|
| 8 | information-type + C/I/A impact(FIPS 199) | SSP | 中(新表 ~2-3 張) |
| 9 | security-impact-level(C/I/A 三軸) | SSP | 小(欄位或小表) |
| 10 | by-component + export/provided/responsibility/inherited/satisfied | SSP | 大(責任繼承鏈一整族) |
| 11 | system-user + authorized-privilege | SSP | 中 |
| 12 | set-parameter(三層) | SSP | 中 |
| 13 | diagrams(auth-boundary/network/data-flow) | SSP | 小 |
| 14 | finding.target + implementation-status | AR | 中(finding 核心) |
| 15 | observation(+ origin/subject/relevant-evidence) | AR | 中 |
| 16 | risk(+ status/characterization/facets/response/risk-log/threat-id) | AR | 大 |
| 17 | result.description | AR | 小(單欄) |
| 18 | back-matter / resource(+ hash / document-id) | 共用 | 大(影響所有模型 round-trip) |
| 19 | POA&M poam-item.title/.description + 文件根 | POA&M | 中 |
| 20 | profile.imports 結構化(多 catalog / include-all) | Profile | 中 |
三個必填 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 永遠可解析。據此分兩情境:
profile_id/ssp_id/assessment_plan_id → 查 目標.uid → 合成 href="#<uuid>"。內部 round-trip 不需新欄位。import_href(String,存原始 href)+ optional import_remarks,否則來源在匯入即丟失。順帶技術債:assessment_plans.ssp_id 現存 stringified int(memory feedback_ap_ssp_id_varchar_stringified_int)。VARCHAR(36) = UUID 長度,正好可改存 ssp.uid,語意直接貼 OSCAL href、匯出免查表——但屬 breaking 資料遷移,單獨評估。
結論:import 欄位有解、不一定要動表——只求內部 round-trip 走 mapper id→uid(0 動表);要支援匯入外部文件 / 完整保真才加 import_href(+remarks),並建議 ssp_id 改存 uuid。
ssp_*,需協調動表順序避免互踩(建議 FR-036 結構先定,本 FR 的 SSP 欄位補在其上)。feedback_jedi_package_dev_path_first)。docs/reference/oscal_model_schemas/oscal_{catalog,profile,ssp,assessment-plan,assessment-results}_schema.json~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/model/{base,catalog,profile,ssp,ap,ar}/compliance.poams(BE 主專案)