日期:2026-05-21 狀態:分析完成,待 raymond 確認決策點後開工 範圍:B1 ~ B6 六個 phase 的現況盤點、設計選項、疑問清單
| 資源 | 路徑 | B 階段用途 |
|---|---|---|
SystemSecurityPlanYamlMapper |
jedi-oscal/.../ssp_yaml_mapper.py |
B5 YAML/JSON 序列化基礎 |
ModuleFramePartyService.list_parties() |
app/module_frame/service/ |
B2 MF 來源 parties |
ModuleFrameSspResourcesService.list_resources() |
app/module_frame/service/ |
B2 MF 來源 devices/info_systems |
ModuleFrameControlDefaultService |
app/module_frame/service/ |
B2 MF 來源控制項說明 |
ModuleFrameControlObjectiveDefaultService |
app/module_frame/service/ |
B2 MF 來源 AO 說明 |
ModuleFrameReferenceDocumentService |
app/module_frame/service/ |
B2 MF 來源程序書 |
SspVersioningService |
app/oscal/service/ |
B2 SSP 版本來源 |
SspControlImplImportService.export_excel() |
app/oscal/service/ |
唯一現有 export 模式參考 |
ControlImplementationObjectiveMapper |
jedi-oscal/.../ssp/ |
B5 AO 序列化參考 |
| CMMC SSP reference docx | docs/features/.../reference/ASIA-CMMC-SSP-DRAFT-with-user-info-202604.docx |
B1 章節結構來源 |
| OSCAL block | 現況 | B5 需補 |
|---|---|---|
metadata.title/version/published/last-modified/oscal-version |
✅ 已支援 | — |
metadata.parties[] |
❌ 未序列化 | 需補 |
metadata.responsible-parties[] |
❌ 未序列化 | 需補 |
metadata.roles[] |
❌ 未序列化 | 視需求補 |
system-characteristics.system-name/description/status |
✅ 基礎支援 | — |
system-characteristics.security-sensitivity-level |
✅ | — |
system-characteristics.authorization-boundary |
❌ 未序列化 | 需補(SC 有 scope_description) |
system-characteristics.security-impact-level |
❌ 未序列化 | 有 CIA 三欄需補 |
system-implementation.components[] |
✅ | — |
system-implementation.inventory-items[] |
✅ | — |
system-implementation.leveraged-authorizations[] |
✅ | — |
system-implementation.users[] |
❌ 不做(requirement-understanding §2 決議) | — |
control-implementation.implemented-requirements[].description/status |
✅ | — |
control-implementation.implemented-requirements[].statements[] |
❌ 未序列化(ci.objectives 沒走) | 需補(AO = OSCAL statements) |
back-matter.resources[] |
❌ 未序列化(程序書) | 需補 |
結論:ssp_yaml_mapper.py 約完成了 55% 的 OSCAL SSP 序列化。B5 需在 jedi-oscal 裡補 4 個區塊:parties、statements(AO)、back-matter、部分 system-characteristics 欄位。
封面 (Cover)
版本更新紀錄表 (Version History)
目錄 (TOC — Word auto field)
├── 1. 系統概述 (System Overview)
│ ├── 1.1 系統基本資料 (system-characteristics: name / abbreviation / description)
│ ├── 1.2 系統邊界 (authorization-boundary)
│ ├── 1.3 網路架構 (network-architecture)
│ └── 1.4 資料流 (data-flow)
├── 2. FIPS 199 安全分類 (Security Classification)
│ └── 機密性 / 完整性 / 可用性 + overall sensitivity
├── 3. 系統所有人與聯絡人 (Parties / Roles)
│ ├── 3.1 組織 (parties type=organization)
│ └── 3.2 人員 (parties type=person)
├── 4. 授權邊界系統元件 (System Implementation)
│ ├── 4.1 設備清單 (inventory-items / hardware)
│ ├── 4.2 資訊系統 / 元件 (components)
│ └── 4.3 外部利用服務 (leveraged-authorizations)
├── 5. 控制項實作說明 (Control Implementation)
│ └── 5.x <control_id>: description + status
│ └── 5.x.y <statement_id> (AO): description + status
└── 6. 附錄:參考程序書 (Back Matter / Reference Documents)
建議:延用 CLAUDE.md 規範的 python-docx 手寫模式(不用 pandoc,跟既有 system-design DOCX 規範一致)。
generator 分兩層:
SspDocxGenerator(jedi-oscal 或主專案)— 純版面/結構,只知道 python-docx APISspContentAssembler(主專案)— 組裝資料,產出 SspExportDataModel,傳給 generator要不要讀真實的 .docx 樣板檔(template mode)vs 純 python-docx 程式碼生成?
| 選項 | 優點 | 缺點 |
|---|---|---|
| A:template.docx 作為樣式基礎 | 字型/色彩/間距可用 Word 調 | 部署需帶樣板檔;樣板改版要重建 |
| B:純 python-docx 程式碼 | 無外部依賴;自動化友好 | 版面調整要改 code |
建議選 B,與既有 system-design DOCX 模式一致,部署簡單。
兩個來源的資料存放位置不同:
| 資料領域 | MF 來源 | SSP 版本來源 |
|---|---|---|
| Metadata | module_frame 本身的欄位 |
ssp.oscal_metadata → MetadataEntity |
| Parties | oscal_parties (context_type='module_frame') |
ssp.oscal_metadata.parties[] |
| 控制項說明 | module_frame_control_defaults |
ssp.control_implementations[] |
| AO 說明 | module_frame_control_objective_defaults |
ssp.control_implementations[].objectives[] |
| Devices | ssp_system_implementation_items (scope_type='module_frame' 或 via MF 的 SSP) |
ssp.system_implementations[] (hardware) |
| 程序書 | module_frame_reference_documents |
ssp_reference_documents (context_type='ssp') |
SspExportDataModel 中間層(推薦)@dataclass
class SspExportDataModel:
# 來源識別
source_type: Literal["module_frame", "ssp_version"]
source_uid: str # mf.uid 或 ssp_group_uid
# 組裝後的欄位(格式一致)
title: str
version: str
system_name: str
system_name_short: Optional[str]
description: str
sensitivity_level: str
authorization_boundary: Optional[str]
# ... metadata 欄位
parties: List[SspPartyExportItem]
inventory_items: List[SspInventoryItemExportItem]
components: List[SspComponentExportItem]
leveraged_authorizations: List[SspLeveragedExportItem]
control_implementations: List[SspControlImplExportItem]
reference_documents: List[SspRefDocExportItem]SspExportDataModel,不知道來源差異MfSspContentLoader + SspVersionContentLoader)SystemSecurityPlanEntity從 MF 資料「偽造」一個 SystemSecurityPlanEntity,讓現有 yaml_mapper 直接用。
SystemSecurityPlanEntity 是 DB ORM 對應的 entity,「偽造」會讓型別語義混亂;且 yaml_mapper 本身還有缺口需補在 SystemSecurityPlanYamlMapper 加一個 module_frame_to_dict(mf_data) 方法。
結論:選 A,主專案定義 SspExportDataModel,兩個 loader 分別組裝。
SspExportDataModel要放在哪一層?
- 放
domain/oscal/export/— 純 domain dataclass,DDD 合規- 放
app/oscal/service/export/— 跟 loader 放在一起建議放
domain/oscal/export/ssp_export_model.py,因為 Docx generator 和 OSCAL serializer 都可能在 jedi-oscal 層使用它。
MF 來源的 devices/info_systems 要從哪裡撈?
ModuleFrameSspResourcesService是透過 MF 的 linked SSP 撈ssp_system_implementation_items。 但如果一個 MF 沒做過 Excel 匯入(沒有 SSP shell),這裡會回傳空清單。匯出時,MF 沒有 Excel 匯入過的設備應該怎麼處理?
- 選項 A:只匯有資料的,空清單正常(最小成本)
- 選項 B:提示 user「設備/系統資料尚未匯入」
POST /api/1.0/module-frame/<uid>/export?format=docx
POST /api/1.0/projects/<project_uid>/ssp/<version_id>/export?format=docx|pdf|odt|json|xml|yaml
注意:
format放 query string(RESTful,不改 URL 路徑)。
Flask send_file + BytesIO,Content-Disposition 帶檔名:
# 範例
filename = f"SSP_{system_name}_{date.today().isoformat()}.docx"
return send_file(
BytesIO(docx_bytes),
mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
as_attachment=True,
download_name=filename,
)兩個 endpoint 要放在哪個 Blueprint?
- 選項 A:
api/oscal/routes/ssp_export_route.py(新 route,擴 oscal blueprint)- 選項 B:
api/module_frame/routes/mf_export_route.py(MF 端) +api/oscal/routes/ssp_export_route.py(SSP 版本端)建議選 B,因為兩個 endpoint 的 URL 前綴不同(/module-frame vs /projects)。
| 方案 | 優點 | 缺點 | 現有依賴 |
|---|---|---|---|
| A:LibreOffice headless | 高品質轉換;支援 docx→pdf/odt;開源免費 | 需要在 server 安裝 LibreOffice;spawn 子行程 | 無 |
| B:既有 preview-as-pdf endpoint | 不需新依賴;直接複用 | 需先上傳再轉換 → 兩次 HTTP;語義不對(export ≠ preview) | upload-file/<uid>/preview-as-pdf |
| C:python-docx2pdf | Python 原生 | 在 Linux server 需要 Word 或 LibreOffice | 無 |
| D:Gotenberg / WeasyPrint | 容器化,方便 | 額外服務依賴 | 無 |
建議選 A(LibreOffice headless),指令:
libreoffice --headless --convert-to pdf --outdir /tmp /tmp/ssp.docx
libreoffice --headless --convert-to odt --outdir /tmp /tmp/ssp.docx部署環境是否已安裝 LibreOffice?
這決定了 B4 是 1 天工作還是要先處理環境。 請 confirm:
which libreoffice在 prod/staging server 上的結果。
# 現況(ssp_yaml_mapper._control_implementation_to_dict)
r: Dict = {"control-id": str(control_id)}
# 只有 description + status + remarks
# ❌ 缺 ci.objectives → OSCAL statements[]
# 需補
if ci.objectives:
r["statements"] = [
{
"statement-id": obj.statement_identifier,
"uuid": _stringify_uuid(obj.uid),
"description": obj.implementation_description or "",
"remarks": obj.remarks,
}
for obj in ci.objectives
]_metadata_to_dict 目前只輸出 title/version 等標量欄位。需補:
# MetadataEntity 已有 .parties[] (List[PartyEntity])
# 和 .responsible_parties[] (List[ResponsiblePartyEntity])
if metadata.parties:
out["parties"] = [_party_to_dict(p) for p in metadata.parties]
if metadata.responsible_parties:
out["responsible-parties"] = [_rp_to_dict(rp) for rp in metadata.responsible_parties]OSCAL back-matter.resources[]:
back-matter:
resources:
- uuid: "..."
title: "程序書名稱"
rlinks:
- href: "..."SystemSecurityPlanEntity 目前沒有 reference_documents 屬性。 需要在 ssp_entity 加 relationship 或由 B2 的 assembler 填充。
已確認 OscalSystemSecurityPlanSystemCharacteristic ORM 欄位實際清單:
| 欄位 | 現有 | mapper 有無輸出 |
|---|---|---|
name |
✅ | ✅ (system-name) |
description |
✅ | ✅ |
system_identifier |
✅ | ✅ (system-id) |
security_sensitivity_level |
✅ | ✅ |
target_type |
✅ | ✅ |
scope_description |
✅ | ✅(輸出為 scope.description) |
status |
✅ | ✅ (status.state) |
network_architecture |
❌ 不存在 | — |
data_flow |
❌ 不存在 | — |
authorization_boundary |
❌ 不存在(只有 scope_description) | — |
CIA 三欄 (confidentiality/integrity/availability) |
❌ 不存在 | — |
結論:system-characteristics 的 B5 缺口比預期小 — DB 根本沒有這些欄位,無法序列化。 mapper 目前的 7 個欄位已涵蓋現有 DB 資料的全部。缺口 4 實際上不需要補。
若未來需要 CIA 分類和 network-architecture,那是另一個 feature(schema 擴充 + UI)。
| 格式 | 做法 | 成本 |
|---|---|---|
| YAML | 現有 yaml.safe_dump(entity_to_dict(...)) |
✅ 已有 |
| JSON | import json; json.dumps(entity_to_dict(...), ensure_ascii=False, indent=2) |
極低(1行) |
| XML | 見下方選項 | 中 |
| 方案 | 做法 | OSCAL 合規性 |
|---|---|---|
| A:xmltodict | xmltodict.unparse({"system-security-plan": {...}}) |
低(namespace 缺失) |
| B:lxml etree 手寫 | 逐層 SubElement,可加 namespace |
高,但繁瑣 |
| C:xmltodict + 手補 namespace | 先轉再後處理加 xmlns | 中等 |
OSCAL XML 根元素需要:
<system-security-plan xmlns="http://csrc.nist.gov/ns/oscal/1.0"
uuid="...">建議選 C(xmltodict + namespace patch),成本低,足夠滿足結構正確性要求。若後續需要完整 schema validation 再改 B。
OSCAL 標準合規程度:要完整符合 OSCAL 1.1.x spec(含所有 required 欄位),還是「最大努力」輸出?
OSCAL 1.1.x 對
system-security-plan的 required 欄位:
uuid✅metadata.title✅metadata.last-modified✅metadata.version✅metadata.oscal-version✅system-characteristics.system-id[](我們有system_identifier)system-characteristics.system-name✅system-characteristics.description✅system-characteristics.security-sensitivity-level✅system-characteristics.status✅system-implementation.components[](at least 1 required) — 有資料時 ✅,空 MF 時 ❌control-implementation.implemented-requirements[].uuid✅control-implementation.implemented-requirements[].control-id✅建議:「最大努力」— 輸出我們有的資料,缺少的 required 欄位補空字串或省略;加一個
_validate_oscal_completeness()方法輸出 warning log 但不擋住匯出。
AO statements 資料路徑確認
ControlImplementationEntity.objectives這個 list 是否在現有 SSP query 時有被 eager load? 現有 yaml_mapper 呼叫端是哪裡?需要確認 caller 在 load SSP entity 時有無帶objectives。
MF 來源的 control-implementation 資料路徑
MF 的控制項說明在
module_frame_control_defaults(不是system_security_plan_control_implementations)。 OSCAL yaml_mapper 接ControlImplementationEntity— 但 MF source 的資料型態是ModuleFrameControlDefaultEntity。B2 的 assembler 必須把
ModuleFrameControlDefaultEntity→ControlImplementationEntity(或 直接 map 到SspControlImplExportItemdataclass)才能餵給 yaml_mapper。這是 B2 最重要的轉換工作,需要確認兩個 entity 欄位對應關係。
| 頁面 | 入口 | 元件類型 |
|---|---|---|
| 合規資源庫列表/詳細頁 | 右上 action button 或詳細頁 header | SplitButton(同 A1 的下載樣板) |
| 專案規劃頁(AP / SSP 視圖) | 頁面 header action 區 | Button + Dialog(選版本 + 格式) |
[ docx ] [ pdf ] [ odt ] [ OSCAL-JSON ] [ OSCAL-XML ] [ OSCAL-YAML ]
建議用 dialog + radio group,UX 一致,不用 6 個按鈕。
| # | 問題 | 決策 | 確認日期 |
|---|---|---|---|
| D1 | B4 轉換工具:LibreOffice 是否在 server 上可用? | ✅ 已安裝,現有 PDF 轉換已在用 | 2026-05-22 |
| D2 | B2 中間層:用 SspExportDataModel dataclass 還是直接 reuse SystemSecurityPlanEntity? |
✅ SspExportDataModel dataclass(選項 A) | 2026-05-22 |
| D3 | B1 Generator:純 python-docx code vs 讀 .docx 樣板? | ✅ docxtpl(Jinja2-in-Word .docx 模板),非純 code | 2026-05-22 |
D3 補充:匯出 docx 不設計成 round-trip 匯入格式,優先做可讀交付物。round-trip 是未來獨立 feature。
| # | 問題 | 選項 | 傾向 |
|---|---|---|---|
| D4 | B5 OSCAL 合規程度:「最大努力」vs 完整 required 欄位? | 最大努力 / 完整 | 傾向「最大努力」 |
| D5 | B5 XML 方案:xmltodict+patch vs lxml 手寫? | C(xmltodict+patch)/ B(lxml) | 傾向 C |
| D6 | MF 沒有 Excel 匯入時的空設備/系統:靜默空清單 vs 提示? | A(空清單)/ B(提示) | 傾向 A |
| D7 | B3 endpoint 位置:單一 blueprint vs 分開? | B(分開) | 傾向 B |
B1(1~2 天)
→ 分析 CMMC reference docx 章節結構(需讀 .docx 內容)
→ 建 SspDocxGenerator 骨架 + 空殼輸出(封面/TOC/章節框架)
B2(2~3 天) ← 最重要,其他都等這個
→ 定義 SspExportDataModel
→ MfSspContentLoader(MF 來源,7 個 service 聚合)
→ SspVersionContentLoader(SSP 版本來源,從 SSP entity)
→ SspDocxGenerator 填入真實資料
B3(0.5 天) ← B2 完成後很快
→ 兩支 export endpoint + streaming response
B4(0.5~1 天,視 LibreOffice 環境)
→ LibreOffice headless subprocess wrapper
B5(2~3 天) ← 平行於 B3/B4 可開
→ jedi-oscal yaml_mapper 補 4 個缺口
→ SspExportDataModel → ControlImplementationEntity 映射
→ JSON/XML 輸出方法
B6(1 天) ← 等 B3/B4/B5 全部完成
→ FE 匯出按鈕 + dialog + 格式選擇
預估總工時:B1~B5 約 7~10 天(視 B4 環境複雜度),B6 FE 另外 1 天。
which libreoffice on dev serverssp_system_characteristics DB schema:確認 CIA 三欄(confidentiality/integrity/availability)和 network-architecture/data-flow 是否存在# dev DB 確認 schema 指令
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "\d oscal.system_security_plans_system_characteristics"