SSP 文件解析器 API 規格書

文件版本:1.0 撰寫日期:2026-04-30 文件性質:SA — API 介面契約 依據raw-requirement/requirement.md + Phase 1 brainstorming 9 項決策

狀態:草稿,等使用者 review 簽字後才會撰寫 SD(design.md)


1. 模組概述

1.1 功能定位

SSP 文件解析器(SSP Docx Parser)讓使用者上傳 Word 格式的 System Security Plan(SSP),系統用純規則層抽取每個合規控制項的實作描述(implementation description)與評估目標(assessment objectives),自動填入 ssp_control_implementation / module_frame_control_defaults 等對應表,省去人工逐條 copy-paste。

1.2 v1 範圍

做:

  • 純規則 + python-docx + regex + fuzzy match 解析 docx
  • 抽取控制項實作描述、評估目標((a)(b)(c) 表格第 2 欄)
  • 抽取 SSP 整體 metadata 寫到 information_systems
  • 三個入口(合規資源庫 Step 2 / Step 3 / 專案規劃端)共用同一套 API
  • 三步驟 wizard:上傳 → 預覽編輯(含拖拉指派 + diff preview)→ 結果摘要
  • 同步處理(5MB docx 預估 5 秒內)
  • 失敗段落拖拉到任意控制項主述或特定 (a)(b)(c) objective
  • 一份 docx 涉及單一 baseline / framework

不做(v2 候選):

  • LLM fallback(規則打不到時不自動猜,全靠拖拉)
  • OSCAL JSON 匯出
  • Leveraged External Systems table 結構化抽取
  • 系統元件 / 軟體元件清單抽取
  • 嵌入圖片抽取
  • Rich text 格式保留(與既有 plain Textarea UI 一致)
  • docx 範本下載
  • 多 baseline 跨 framework 解析
  • 中途離開後 resume(中途離開作廢)
  • async / cron / worker queue

1.3 端點總覽

# Method URL Route Class 說明
1 POST /api/1.0/ssp-docx-imports/parse SspDocxImportParseRoute 上傳 docx + 解析(同步)
2 GET /api/1.0/ssp-docx-import/<parse_uid> SspDocxImportRoute 取得 parse job 狀態與預覽結果(Step 1 用)
3 POST /api/1.0/ssp-docx-import/<parse_uid>/confirm SspDocxImportConfirmRoute 確認匯入,寫入目標表(Step 2)
4 DELETE /api/1.0/ssp-docx-import/<parse_uid> SspDocxImportRoute 放棄此次 parse job(清檔)

2. Acceptance Criteria(驗收條件)

依優先順序:

AC 描述 涵蓋的 brainstorm 決策
AC-1 使用者在「專案規劃 / SSP 控制項現況頁」可看到「匯入 docx」按鈕(與既有「匯入 Excel」並排),點擊後開啟 3 步驟 wizard D-Q1 D2, D-Q2 α
AC-2 使用者在「合規資源庫 / 新增 module_frame」第 2 步驟可選擇「從 docx 匯入控制項」啟動解析(Full mode) D Q1 → A1
AC-3 使用者在「合規資源庫 / 新增 module_frame」第 3 步驟可選擇「從 docx 匯入現況」啟動解析(Statement-only mode) D Q1 → A2
AC-4 上傳 docx 時必須先選 framework(CMMC L1 / NIST 800-171 / ISO27001),未選不能繼續 C-Q1 = C2
AC-5 5MB / 273 段 / 25 控制項範例 docx 同步解析時間 < 10 秒 F → v1A 同步
AC-6 規則層命中(信心 ≥ 0.8)的控制項自動配對到 baseline 對應的 control_id A → 純規則
AC-7 規則層失敗(信心 < 0.8)的段落出現在「待人工確認」清單,使用者可拖拉到任意控制項的主述或特定 (a)/(b)/(c) objective G-Q1 β
AC-8 同一段落可拖拉到多個控制項(複製到每個) G-Q2 β
AC-9 拖拉到已有 docx 內容的目標時,跳出 conflict modal 讓使用者選 append / replace G-Q3 β
AC-10 Re-parse 時 Step 1 預覽顯示「現有值 vs docx 值」,使用者可逐筆選擇 keep_current / use_docx / skip;空欄位預設 use_docx,衝突欄位預設 keep_current E-Q1 E3, E-Q2
AC-11 評估目標 (a)(b)(c) 每筆獨立顯示一行,獨立選 keep/use_docx/skip E-Q4 α
AC-12 Step 1 提供批次操作:「全部使用 docx」/「全部保留現有」/「只匯入空白項」 E-Q3
AC-13 docx 解析至 baseline 內找不到的控制項時,留 implementation_description 為空,UI 提示使用者後續手動補(不阻擋匯入) C-Q3 α
AC-14 使用者選定的 framework 與 docx 內主要控制項 ID 模式不符時(例如選 CMMC L1 但 docx 全是 NIST 800-53 ID),標 FATAL 拒絕匯入 G-Q4
AC-15 docx 內找不到任何 heading / 控制項 ID 時,標 FATAL 拒絕匯入 G-Q4
AC-16 規則命中率 < 50% 或 baseline 缺漏 > 30% 控制項時,Step 1 頂部顯示 PARTIAL warning banner,使用者可選擇繼續或放棄 G-Q4
AC-17 parse job 創建後 1 小時內若 status 仍非 'completed' 或 'failed',自動標 'expired',使用者必須重新上傳 G-Q5 α + 短 expire
AC-18 Step 2 確認匯入時,使用者標「不匯入」的段落記在 parsed_result.skipped_paragraphs(JSONB 欄位) G-Q6 β
AC-19 docx 內所有 rich text 格式(粗體、斜體、底線)抽取時轉成純文字;表格內容用換行分隔 row、: | 分隔 cell I-Q1 α
AC-20 docx 內嵌入圖片(網路架構圖、資料流)完全忽略不抽取 I-Q2 α
AC-21 評估目標 table 第 2 欄當 description,第 1 欄忽略(objective_text 由 baseline 提供) I-Q3 γ
AC-22 入口 A 操作權限:tenant admin OR project manager;入口 B 操作權限:project manager only F-Q2, F-Q3 α
AC-23 docx 抽出的 SSP metadata(Introduction tables)寫入 information_systems 對應欄位(name, description, security_sensitivity_level, deployment_model, authorization_boundary 等),與專案 project_information_systems 關聯 B → metadata 寫 information_systems

3. 名詞定義

詞彙 定義
SSP System Security Plan,OSCAL 規範的系統安全計畫文件
module_frame 「合規資源庫」table — 租戶層級的合規範本(如:「我們公司的 CMMC L1 SSP 範本」),給專案啟動時 copy 用
baseline 一個合規 framework + level 的控制項清單(如 CMMC L1 = 17 個控制項)
framework 合規規範(CMMC / NIST 800-171 / ISO27001)
catalog 一個 framework 的控制項目錄(OSCAL 概念)
profile 一個 catalog 的 subset,用於某個 baseline(OSCAL 概念)
assessment objective 控制項下的 (a)(b)(c) 評估細項
implementation_description 控制項實作現況的描述文字
Full mode docx 解析模式:抽出控制項清單 + 現況描述(用於合規資源庫 Step 2)
Statement-only mode docx 解析模式:只抽出現況描述(用於合規資源庫 Step 3 + 專案規劃端)
parse job 一次完整的 parse 任務(從上傳到匯入完成),對應 ssp_docx_parse_jobs 表的一 row

4. API 端點規格

4.1 POST /api/1.0/ssp-docx-imports/parse — 上傳 docx + 解析(同步)

  • Route ClassSspDocxImportParseRoute
  • 認證:JWT Required
  • 權限:依 source_type 分支驗證
    • source_type = 'module_frame' → tenant admin OR manager
    • source_type = 'project_ssp' → project manager
  • Requestmultipart/form-data
欄位 型別 必填 說明
file File Y docx 檔(≤ 10MB,副檔名必須 .docx
framework string Y cmmc-l1 | cmmc-l2 | nist-800-171 | iso27001(C-Q1 = C2 手動選)
source_type string Y module_frame | project_ssp
source_uid string Y 對應的 module_frames.uid 或 SSP 的 uid(取決於 source_type)
mode string Y full(限 source_type=module_frame)| statement_only
  • Response (200)(成功,parse job 創建並同步解析完成):
{
  "status": true,
  "data": {
    "parse_uid": "uuid-string",
    "status": "awaiting_review",
    "summary": {
      "total_paragraphs": 273,
      "matched_controls_count": 22,
      "unmatched_paragraphs_count": 8,
      "missing_baseline_controls_count": 3,
      "framework_detected_id_pattern": "AC.L1-b.1.x",
      "warning_level": null
    }
  }
}
  • Response (200)(PARTIAL warning,仍可繼續但需確認):
{
  "status": true,
  "data": {
    "parse_uid": "uuid-string",
    "status": "awaiting_review",
    "summary": {
      "total_paragraphs": 273,
      "matched_controls_count": 8,
      "unmatched_paragraphs_count": 50,
      "missing_baseline_controls_count": 12,
      "warning_level": "partial",
      "warnings": [
        "規則命中率 < 50% — 建議檢查 framework 選擇是否正確",
        "Baseline 中 12/17 控制項在 docx 找不到對應描述"
      ]
    }
  }
}
  • Response (422)(FATAL 失敗):
{
  "status": false,
  "msg": "上傳的 docx 不包含任何可辨識的控制項 ID(GRC_422001)",
  "data": {
    "parse_uid": null,
    "fatal_reason": "no_control_id_found"
  }
}
  • Error codes
    • GRC_400005 上傳檔案不是 docx 格式
    • GRC_400006 上傳檔案超過大小限制(10MB)
    • GRC_400007 framework 必填
    • GRC_400008 source_type 必填且必須為 module_frame / project_ssp
    • GRC_400009 mode 為 full 時 source_type 必須為 module_frame
    • GRC_403002 無權限操作此 source_uid
    • GRC_404021 source_uid 對應的資源不存在
    • GRC_422001 docx 內無法辨識任何控制項 ID(FATAL)
    • GRC_422002 docx framework 偵測與使用者選擇不符(FATAL)
    • GRC_422003 docx 結構損壞 / 解析失敗

4.2 GET /api/1.0/ssp-docx-import/<parse_uid> — 取得 parse 結果(Step 1 用)

  • Route ClassSspDocxImportRoute

  • 認證:JWT Required

  • 權限:建立此 parse job 的使用者本人 OR 同 source_uid 的有權使用者

  • Path Parameters

    • parse_uidssp_docx_parse_jobs.uid
  • Response (200)(status=awaiting_review,給 Step 1 預覽 UI 用):

{
  "status": true,
  "data": {
    "parse_uid": "uuid",
    "status": "awaiting_review",
    "source_type": "project_ssp",
    "source_uid": "ssp-uid",
    "framework": "cmmc-l1",
    "mode": "statement_only",
    "summary": {
      "total_paragraphs": 273,
      "matched_controls_count": 22,
      "unmatched_paragraphs_count": 8,
      "missing_baseline_controls_count": 3,
      "warning_level": null
    },
    "matched_controls": [
      {
        "control_id": "AC.L1-b.1.i",
        "control_name": "Authorized Access Control",
        "score": 0.95,
        "score_reason": "H3 標題完整 ID 命中",
        "current_implementation_description": "我們透過 AD 控制使用者群組...",
        "parsed_implementation_description": "我們透過 AD + Okta 控制使用者群組,並...",
        "default_action": "use_docx",
        "objectives": [
          {
            "objective_key": "a",
            "objective_text": "authorized users are identified",
            "current_description": "已透過 SOP-001 識別",
            "parsed_description": "已透過 SOP-001 識別並由 IT 部審核",
            "default_action": "use_docx"
          }
        ]
      }
    ],
    "unmatched_paragraphs": [
      {
        "paragraph_idx": 142,
        "text": "我們的安全事件處理由 SOC team 24x7 監控...",
        "context": {
          "preceding_h1": "Control Implementation",
          "preceding_h2": null,
          "preceding_h3": null
        },
        "rule_score": 0.45,
        "rule_guess_control_id": "IR.L2-...",
        "rule_guess_confidence": 0.45
      }
    ],
    "missing_baseline_controls": [
      {
        "control_id": "SC.L1-b.1.x",
        "control_name": "Boundary Protection",
        "reason": "baseline 中存在但 docx 沒提到"
      }
    ],
    "predicted_module_frame_controls": [
      // 僅在 mode=full 時提供  D-Q5 β 預選清單,給 Step 2 勾選 UI
      { "control_id": "AC.L1-b.1.i", "preselected": true },
      { "control_id": "AC.L1-b.1.ii", "preselected": true }
    ],
    "created_at": "2026-04-30 10:00:00",
    "expires_at": "2026-04-30 11:00:00"
  }
}
  • Response (200)(status=completed,已匯入完成的 job 查詢):
{
  "status": true,
  "data": {
    "parse_uid": "uuid",
    "status": "completed",
    "import_summary": { "created": 22, "updated": 5, "skipped": 3 },
    "completed_at": "2026-04-30 10:05:00"
  }
}
  • Response (200)(status=failed / expired):
{
  "status": true,
  "data": {
    "parse_uid": "uuid",
    "status": "failed",
    "error_code": "GRC_422001",
    "error_message": "docx 內無法辨識任何控制項 ID"
  }
}
  • Error codes
    • GRC_404022 parse_uid 不存在
    • GRC_403003 無權限存取此 parse job

4.3 POST /api/1.0/ssp-docx-import/<parse_uid>/confirm — 確認匯入(Step 2)

  • Route ClassSspDocxImportConfirmRoute

  • 認證:JWT Required

  • 權限:建立此 parse job 的使用者本人;且 status 必須為 awaiting_review

  • Path Parametersparse_uid

  • Request Body

{
  "decisions": [
    {
      "control_id": "AC.L1-b.1.i",
      "action": "use_docx | keep_current | skip",
      "objectives": [
        { "objective_key": "a", "action": "use_docx" },
        { "objective_key": "b", "action": "keep_current" }
      ]
    }
  ],
  "manual_assignments": [
    {
      "paragraph_idx": 142,
      "targets": [
        { "control_id": "IR.L2-x", "level": "implementation", "merge_action": "append" },
        { "control_id": "AC.L1-b.1.i", "level": "objective", "objective_key": "c", "merge_action": "replace" }
      ]
    }
  ],
  "skipped_paragraph_idxs": [85, 142, 200],
  "predicted_controls_user_selection": [
    "AC.L1-b.1.i", "AC.L1-b.1.ii", "AC.L1-b.1.iv"
    // 僅在 mode=full 時必填  使用者在 Step 1 微調過的最終控制項勾選清單
  ]
}
欄位 必填 說明
decisions[] Y 每個 matched_control 一筆,action 決定 keep/use_docx/skip
decisions[].objectives[] N objective 層級的決策,未列出的 objective 沿用 control 層的 default_action
manual_assignments[] N 使用者拖拉指派的段落(unmatched_paragraphs)
manual_assignments[].targets[] Y(若有 manual_assignment) 段落可拖拉到多個 target(G-Q2 β)
manual_assignments[].targets[].merge_action Y append | replace(G-Q3 β)
skipped_paragraph_idxs[] N 使用者明確選「不匯入」的段落 idx,記錄供審計(AC-18 / G-Q6 β)
predicted_controls_user_selection[] Y(mode=full)/ N(其他) 合規資源庫 Step 2 使用者最終勾選的控制項清單
  • Response (200)
{
  "status": true,
  "data": {
    "parse_uid": "uuid",
    "status": "completed",
    "import_summary": {
      "created": 22,
      "updated": 5,
      "skipped": 3,
      "manual_assigned": 8,
      "manual_skipped": 3,
      "missing_left_blank": 3
    },
    "redirect_url": "/projects/<uid>/ssp/control-implementations"
  }
}
  • Error codes
    • GRC_404022 parse_uid 不存在
    • GRC_412009 parse job 狀態不是 awaiting_review,無法確認匯入
    • GRC_412010 parse job 已 expired
    • GRC_400010 decisions 缺漏(matched_controls 中有未決策的 control_id)
    • GRC_400011 predicted_controls_user_selection 缺漏(mode=full 時必填)

4.4 DELETE /api/1.0/ssp-docx-import/<parse_uid> — 放棄此次 parse job

  • Route ClassSspDocxImportRoute

  • 認證:JWT Required

  • 權限:建立此 parse job 的使用者本人

  • Response (200)

{
  "status": true,
  "data": { "parse_uid": "uuid", "status": "discarded" }
}
  • 副作用:MinIO 上的 file 同步刪除;DB row 軟刪除(is_active=False)。

  • Error codes

    • GRC_404022 parse_uid 不存在
    • GRC_412011 已完成的 job 無法再放棄(status=completed)

5. Error Codes(新增)

依 CLAUDE.md 命名規則 GRC_<HTTP狀態碼><3位序號>,序號接續既有 common/code/grc_error_code.py

Constant Name 訊息 Code
GRC_DOCX_INVALID_FILE 上傳檔案不是 docx 格式 GRC_400005
GRC_DOCX_FILE_TOO_LARGE 上傳 docx 超過大小限制(10MB) GRC_400006
GRC_DOCX_FRAMEWORK_REQUIRED 必須選擇合規 framework GRC_400007
GRC_DOCX_SOURCE_TYPE_INVALID source_type 必須為 module_frame 或 project_ssp GRC_400008
GRC_DOCX_MODE_FULL_REQUIRES_MODULE_FRAME full mode 僅限 source_type=module_frame GRC_400009
GRC_DOCX_DECISIONS_INCOMPLETE decisions 缺漏,請對所有匹配控制項做選擇 GRC_400010
GRC_DOCX_PREDICTED_CONTROLS_REQUIRED full mode 必須提供 predicted_controls_user_selection GRC_400011
GRC_DOCX_NO_PERMISSION_FOR_SOURCE 無權限操作此 source_uid GRC_403002
GRC_DOCX_PARSE_JOB_NO_PERMISSION 無權限存取此 parse job GRC_403003
GRC_DOCX_SOURCE_NOT_FOUND source_uid 對應的資源不存在 GRC_404021
GRC_DOCX_PARSE_JOB_NOT_FOUND parse job 不存在 GRC_404022
GRC_DOCX_PARSE_JOB_NOT_AWAITING parse job 狀態不是 awaiting_review,無法確認匯入 GRC_412009
GRC_DOCX_PARSE_JOB_EXPIRED parse job 已過期,請重新上傳 GRC_412010
GRC_DOCX_PARSE_JOB_ALREADY_COMPLETED 已完成的 job 無法放棄 GRC_412011
GRC_DOCX_NO_CONTROL_ID_FOUND docx 內無法辨識任何控制項 ID GRC_422001
GRC_DOCX_FRAMEWORK_MISMATCH docx 主要控制項 ID 模式與選擇的 framework 不符 GRC_422002
GRC_DOCX_PARSE_FAILED docx 結構損壞,解析失敗 GRC_422003

6. Permission Matrix

操作 入口 角色
Upload + Parse(POST /parse) A 合規資源庫 tenant admin OR manager
Upload + Parse(POST /parse) B 專案規劃 project manager
GET preview(GET /<parse_uid>) 任一 parse job 建立者
Confirm import(POST /confirm) A 合規資源庫 tenant admin OR manager(建立者本人)
Confirm import(POST /confirm) B 專案規劃 project manager(建立者本人)
Discard(DELETE) 任一 parse job 建立者

備註:

  • 角色驗證在 app service 層透過 domain service(依 CLAUDE.md DDD 規範)
  • project manager = project_participants.role == 'manager'
  • tenant admin = users.is_admin == True
  • auditor 角色不可進行任何 docx import 操作(F-Q3 α 嚴格)

7. UI 整合點(high-level,細節留 frontend-spec.md)

7.1 入口 A1 — 合規資源庫新增 Step 2

  • 位置:ModuleFrame.vue 編輯/新增 wizard 第 2 步(選控制項)
  • 新增按鈕:「從 docx 匯入控制項」(與既有手動勾選並排)
  • 點擊後開啟 SspDocxImportDialog 子 wizard
  • mode = full,source_type = module_frame
  • 子 wizard 完成後:使用者選定的控制項 ID 清單回傳父 wizard,父 wizard 跳轉至 Step 3

7.2 入口 A2 — 合規資源庫新增 Step 3

  • 位置:ModuleFrame.vue 編輯/新增 wizard 第 3 步(填現況)
  • 新增按鈕:「從 docx 匯入現況」(與既有手動填寫並排)
  • mode = statement_only,source_type = module_frame
  • 子 wizard 完成後:使用者選定的現況資料回傳父 wizard,父 wizard 顯示已填內容

7.3 入口 B — 專案規劃 SSP 控制項現況頁

  • 位置:SSP 控制項現況頁,與既有「匯入 Excel」按鈕並排
  • 新增按鈕:「匯入 docx」
  • mode = statement_only,source_type = project_ssp
  • 子 wizard 完成後:頁面 reload 顯示更新後的現況

7.4 共用:SspDocxImportDialog 三步驟

  • Step 0 — 上傳:framework dropdown + FileUpload 元件 → 點「下一步」呼叫 POST /parse
  • Step 1 — 預覽編輯(最複雜的 UI):
    • 自動匹配清單(matched_controls)— diff preview + 每筆 keep/use_docx/skip
    • 待人工確認清單(unmatched_paragraphs)— 拖拉到任一控制項主述或 objective
    • Baseline 缺漏清單(missing_baseline_controls)— 純 warning 訊息
    • 批次操作 toolbar:全部使用 docx / 全部保留現有 / 只匯入空白項
    • PARTIAL warning banner(若有)
    • 拖拉到已有內容時跳 Conflict modal(append / replace 二選一)
  • Step 2 — 結果:import_summary + 「完成」按鈕

8. 待解決 / 後續討論

編號 問題 可能影響
OPEN-1 information_systems 寫入時:使用者已手動建了該專案的 information_system 怎麼辦?docx 匯入要 merge / 新建 / 報錯讓使用者選?brainstorm 沒展開細節 AC-23 細節
OPEN-2 多個使用者同時操作同一個 source_uid(如多人匯入同一份 SSP)時的 race condition:兩個 parse_uid 同時 confirm,後者覆蓋前者?要不要加 lock F → 多人協作 v1 不展開但需有對策
OPEN-3 docx 解析的 framework 偵測邏輯:用什麼比例 / 邏輯判定「framework mismatch」?例如 80% H3 ID 不符選擇的 framework 才報錯? AC-14 / GRC_422002 細節
OPEN-4 predicted_controls_user_selection 的 default 行為:若使用者完全沒勾選任何(清空),是不是該擋下?或當作「全不選」 mode=full 邊界條件
OPEN-5 拖拉指派到 objective 層時,objective_key 是否驗證屬於該 control?(e.g., 防止拖到不存在的 objective) AC-7 / AC-8 細節
OPEN-6 1 小時 expire 是否合適?對顧問處理 100+ 控制項可能不夠 AC-17 細節

這些 OPEN 不影響 SA 主結構,會在 SD(design.md)階段細化解決方案。


9. 風險與假設

9.1 假設

  • 既有 jedi-oscal 套件的 ssp_control_implementation / ssp_control_implementation_objective 現況可直接寫入(API 已存在)
  • 既有 module_frame_control_defaults / module_frame_control_objective_defaults 表結構穩定
  • information_systems + project_information_systems 的 CRUD service 已可呼叫
  • python-docx 套件已存在(既有 docs/scripts 已使用)

9.2 風險

  • R1:客戶提供的 docx 結構過於不規則 → 規則命中率 < 50% → 大量人工拖拉,使用者體驗差 → 觸發 PARTIAL warning,但 v1 沒有 LLM 救援,使用者可能放棄
  • R2information_systems 寫入 / merge 邏輯(OPEN-1)若沒 set 好,可能造成多 row 髒資料
  • R3:5MB docx 同步解析超時:若實測超過 5 秒,前端 UX 受影響 → 需要在 SD 階段加 timeout 監控

10. 參考資料

  • 原始白話需求:raw-requirement/requirement.md
  • 客戶範例 docx:raw-requirement/reference/ASIA-CMMC-SSP-DRAFT-202604.docx
  • 既有合規資源庫文件:docs/api/module-frame/
  • 既有 SSP 控制項現況設計:api/oscal/serializers/ssp/ssp_control_implementation.py
  • jedi-oscal 套件 SSP models:~/Projects/Jedicogy/module/jedi-python-package/jedi-oscal/jedi_oscal/infra/model/ssp/
  • jedi_information_system 套件:jedi_information_system/(主專案內)
  • CLAUDE.md(DDD / 權限 / error code 規範)

Sign-off 區(Phase 2.1 結束時填寫)

角色 名字 日期 備註
Product / 需求
Tech Lead
FE Lead

簽完後進 Phase 2.2(design.md / SD 撰寫)。


11. Post-launch 新增 endpoints / 欄位 (2026-05-03)

跟 docx 匯入相關的編輯介面(編輯頁的「參與單位 / 人員」對話框)需要 BE 提供 CRUD endpoints 直接管理某個 module_frame 的 oscal_parties,不再透過 docx import 流程。

11.1 GET /module-frame/<uid>/parties

列出該 module_frame 連結的所有 parties。

Request:無 body

Response 200

{
  "status": true,
  "data": [
    {
      "uid": "uuid",
      "party_type": "person | organization",
      "name": "Pan Pan",
      "role": "system-security-officer",
      "title": "Security Manager",
      "email_address": "bls.tech.pan@gmail.com",
      "telephone_number": null,
      "address": null,
      "matched_user_id": 23,
      "matched_org_unit_id": null,
      "matched_user_name": "Pan Pan",
      "matched_org_unit_name": null
    }
  ]
}

11.2 POST /module-frame/<uid>/parties

新增 party + 連結到該 module_frame。

Request body

{
  "party_type": "person | organization",
  "name": "string",
  "role": "system-owner | ...",
  "title": "string?",
  "email_address": "string?",
  "telephone_number": "string?",
  "address": "string?",
  "matched_user_id": 23,
  "matched_org_unit_id": null
}

role 為 5 個固定 role 之一(responsible-organization / information-provider / information-receiver / system-owner / system-security-officer)。

11.3 PUT /module-frame/<uid>/parties/<party_uid>

編輯 party 內容 / 重新連結。party_typename 不可改(保 dedupe key 穩定,re-import 時才能找到同一筆)。role 改變時自動 re-point responsible_party 連結。

11.4 DELETE /module-frame/<uid>/parties/<party_uid>

只移除「該 module_frame 跟此 party 的連結」(responsible_party row),party row 本身保留供其他 module_frame 重用。

11.5 Error codes

Code HTTP 說明
GRC_INVALID_PARTY_TYPE 400 party_type 不是 person / organization
GRC_PARTY_NAME_REQUIRED 400 name 缺漏
GRC_PARTY_ROLE_REQUIRED 400 role 缺漏
GRC_PARTY_NOT_FOUND 404 party_uid 不存在
GRC_MODULE_FRAME_NOT_FOUND 404 module_frame uid 不存在

11.6 Confirm (/ssp-docx-import/<uid>/confirm) 新欄位

欄位 型別 用途
content_overrides dict FE inline 編輯 overlay:{"<control_id>": {"implementation": "...", "objectives": {"(a)": "..."}}},BE 在 strategy.write 前 mutate parsed.matched_controls 對應欄位

11.7 Parse 回傳新增欄位

GET /ssp-docx-import/<uid> 回傳的 parsed_result 多了:

欄位 用途
baseline_controls[].catalog_control_uid catalog_control UUID — FE pre-create module_frame 必須送這個給 add_profile(不是 control_id 字串)
matched_controls[].catalog_objective_descriptions catalog 來的 AO 題目(dict: AO key → 題目文字),跟 current_objectives_descriptions(user 答案)刻意分開