FR-038 OSCAL GRC 重設計 — API 契約(planning contract)

範圍:規劃層 API 契約。把 design.md §5 路由骨架展開成「逐端點 request / response / 權限 / error code」, 讓 FE(Wave 3)可與 BE 遷移(Wave 2)平行開工 —— FE 對著本契約刻畫面,不用等 BE 寫完。

本檔不是各模組正式 api-spec.md。各模組 docs/api/<模組>/api-spec.md 的權威更新在 Wave 2 各 sub-project 執行時做; 本契約若與 Wave 2 實作出現偏差,以實作 + 該模組 api-spec.md 為準(屆時回頭同步本檔的偏差註記)。

上游基準:design.md(§3 engagement 模型 / §4 套件介面 / §5 路由骨架 / §6 error code)、 requirement-analysis.md(§3.3 輪次 7 態 / §4.5-4.7 AP/AR/POA&M / §5 SSP→OSCAL / §6 FE 影響)、 oscal-v2-deltas.sql(欄位 / enum 值域)。 撰寫日:2026-06-14


0. 全域慣例(每個端點預設遵守,不再逐條重述)

面向 規則
Envelope(成功) { "code": 1, "data": ... }(BE Swagger 寫 code;jedi-common 對 FE 統一成 status:true
Envelope(失敗) { "code": 0, "msg": "...", "data": {...} },HTTP status code 對齊 error class
單筆 GET / 單筆寫入 data 直接放結果物件,不要再包一層
分頁列表 POST + body{ pager:{page,page_size,with_total}, sort:[{field,order}], filters:{...} };response data 是陣列,meta 在頂層{page,page_size,total,total_pages,has_next,has_prev,paging}
URL 命名 kebab-case;單筆操作單數、清單/批次/collection 複數
審計欄位 response 帶 created_user/updated_user(login_name)時,必同時帶 created_user_name/updated_user_name(nickname),app service 層 batch enrich
assignee/負責人欄位 assignee_user_id 必 enrich assignee_user_name(nickname)+assignee_login_name;帶 assignee_org_unit_id 必 enrich assignee_org_unit_name(不曝光內部 id 以外資訊用名稱)
權限檢查 在 app service 層透過 domain service 查 participant.role;route 不查 DB
locale 有翻譯欄位的 entity update 必傳 locale(HTTP:str(get_locale())
時間欄位 ISO 8601 字串(created_at / updated_at / start_at / target_date 等)

標記圖例[沿用] = 現有 contract 不動(只列關鍵端點);[新] = 全新端點;[改] = 既有端點 shape / 行為變更。

路由 prefix 假設:OSCAL 物件類 /oscal/...;產品業務(輪次 / 專案)類沿用現有 /projects/.../oscal-project/...。各 sub-project 落地時對齊既有 blueprint,本契約以相對語意路徑表達。


1. 合規框架 Framework(不大改 — 底層 schema rename,API 對齊)

sub-project BE-B1。FE 100% 現成。本期變動:oscal_frameworks→frameworksoscal_framework_versions→framework_versions、catalog 綁 framework_version_id、發布狀態 publish_status。 套件對應:FrameworkService / CatalogService(design §4.2)。

端點 標記 權限 說明
POST /oscal/frameworks/list [沿用] 全參與者 分頁列表,filters 支援 code/publish_status/search
GET /oscal/framework/{uid} [沿用] 全參與者 框架詳情(含 versions 摘要)
POST /oscal/frameworks [沿用] manager 建框架
PUT /oscal/framework/{uid} [沿用] manager 改框架
POST /oscal/framework-versions/import [改] manager 匯入 PDF/Excel → 標準 catalog/group/control/part(AO) 結構(落 v2 schema)
POST /oscal/framework/{uid}/publish [新] manager 發布版本(publish_status: draft→published);資源庫只能選 published 版本

POST /oscal/frameworks request([沿用],欄位對齊新 schema)

{ "code": "CMMC_L2", "name": "CMMC 2.0 Level 2", "authority": "DoD", "description": "..." }

response data{ uid, code, name, authority, publish_status, main_version_id, created_at, created_user, created_user_name }

POST /oscal/framework-versions/import request([改],multipart)

file: <PDF/Excel>
framework_uid: <str>      // 掛在哪個框架
version: "2.0"            // 版本字串
parser_code: "CMMC_L2"    // 對應 parser adapter(design §4.3)

response data{ framework_version_uid, version, catalog_uid, publish_status:"draft", control_count, group_count, ao_count } (→ 套件 CatalogService.import_catalog_from_pdf + FrameworkService.add_framework_version,匯入後回填 framework_versions.catalog_id

POST /oscal/framework/{uid}/publish request{ "framework_version_uid": "<str>" } response data{ framework_version_uid, publish_status:"published", published_at }(→ FrameworkService.publish_version

key errorGRC_FRAMEWORK_VERSION_NOT_FOUND(GRC_404032)、GRC_IMPORT_INVALID_FILE(GRC_400001)、GRC_IMPORT_FILE_TOO_LARGE(GRC_400002)、GRC_NOT_MANAGER(GRC_403002)。


2. 合規資源庫 Resource Library(不大改 — 語意調整成三件組)

sub-project BE-B1。FE 100% 現成。新定位:catalog 副本 + profile + SSP 範本 三件組,由稽核顧問維護。 套件對應:OscalSnapshotService.clone_resource_library(邊界②/①)、OscalIoService.import_ssp_docx/excelProfileService.resolve_profile

端點 標記 權限 說明
POST /oscal/resource-libraries/list [沿用] 全參與者 分頁列表
GET /oscal/resource-library/{uid} [沿用] 全參與者 三件組詳情(catalog/profile/ssp uid + 控制數摘要)
POST /oscal/resource-libraries [改] manager 建資源庫:選 framework_version → snapshot catalog 副本(邊界①)+ 依 profile resolve 納入控制集
POST /oscal/resource-library/{uid}/import-ssp [沿用] manager 匯入 SSP docx/excel(落 v2 schema;UI/流程沿用 SspImportDialog/ssp-docx-import-v2
POST /oscal/resource-library/{uid}/publish [新] manager 發布資源庫(專案成立只能選 published)

POST /oscal/resource-libraries request([改])

{
  "name": "CMMC L2 標準稽核範本",
  "framework_version_uid": "<str>",   // 選哪個 published framework_version → snapshot catalog
  "description": "...",
  "profile": {                        // baseline 定義:納入哪些 control / AO
    "include_all": false,
    "include_controls": ["AC.L2-3.1.1", "AC.L2-3.1.2", "..."]  // 或依 profile import 規則
  }
}

response data{ uid, name, catalog_uid, profile_uid, ssp_template_uid, framework_version_uid, resolved_control_count, publish_status:"draft", created_user, created_user_name } (→ ProfileService.resolve_profile(邊界①)→ OscalSnapshotService.clone_resource_library(邊界②建三件組副本))

key errorGRC_FRAMEWORK_VERSION_NOT_FOUND(GRC_404032)、GRC_EXCEL_INVALID_FILE(GRC_400065)、GRC_NOT_MANAGER(GRC_403002)。


3. 專案 Project(不大改列表/建立;專案成立流程大改

sub-project BE-B2。FE 列表/建立/總覽 100% 現成。

3.1 列表 / 總覽([沿用])

端點 標記 權限 說明
POST /projects/list [沿用] 全參與者 專案分頁列表(加上 OSCAL AP 統計視角)
GET /project/{uid} [沿用] 參與者 專案總覽(基本資訊 + 參與者 + 輪次摘要)
POST /projects 或既有建立路由 [沿用] manager 建專案主檔基本資訊(不含 clone;clone 由 §3.2 start 觸發)

3.2 專案成立 POST /oscal-project/start[改] — 重點全寫

對齊現有 OscalProjectStartRoute核心變更(requirement §4.3 / Q1)

  • 起點從「MF defaults + template SSP 兩套」改為「clone 資源庫三件組脫鉤(邊界②)」。
  • AP/AR 延後:不在 start 當場建(改由輪次 launch-audit 才建)。
  • 為 SSP 控制項建 job per Q1:workflow/job 綁定點從「AP task」改為「專案 SSP 控制項」,且在 start 建好。

權限:manager / 專案 owner。

request

{
  "name": "精誠機械 CMMC L2 導入",
  "resource_library_uid": "<str>",   // 選哪個 published 資源庫 → clone 三件組
  "basic_info": {                    // 沿用 ProjectBasicInfoForm 欄位
    "client_name": "...", "framework_code": "CMMC_L2", "description": "...",
    "owner_user_id": 123, "start_date": "2026-06-20", "target_date": "2026-12-31"
  },
  "participants": [                  // 角色:manager / auditor / participant
    { "user_id": 123, "role": "manager" },
    { "user_id": 456, "role": "auditor" },
    { "user_id": 789, "role": "participant" }
  ]
}

response data

{
  "project_uid": "<str>",
  "catalog_uid": "<str>",            // 專案 catalog 副本(★單一真相來源)
  "profile_uid": "<str>",
  "ssp_uid": "<str>",                // living SSP(clone 自資源庫範本)
  "control_count": 110,
  "job_count": 110,                  // 每個 SSP 控制項一個 job(Q1)
  "ap_created": false,               // AP/AR 延後到 launch-audit
  "ar_created": false,
  "participants": [ { "user_id":123,"role":"manager","user_name":"王經理","login_name":"wang" }, ... ],
  "created_user": "wang", "created_user_name": "王經理"
}

(→ OscalSnapshotService.clone_resource_library(邊界②);job 建立用主專案 jedi-flow-engine 綁 SSP 控制項。)

contract 判斷(ambiguity → 見 §11-A):路徑用 /oscal-project/start(對齊現有 route),未改成 /projects/{uid}/start,避免破壞既有 FE 呼叫點。

key errorGRC_PROJECT_NAME_DUPLICATED(GRC_409005)、resource-library not found(沿用資源庫 not-found 碼)、GRC_NOT_MANAGER(GRC_403002)。


4. 稽核輪次 Project Audit Rounds([新] — 全新,重點全寫

sub-project BE-B3。對應 compliance.project_audit_rounds(oscal-v2-deltas D-2)。 狀態機留主專案(design §4.4);snapshot 呼叫套件 OscalSnapshotService.snapshot_ssp(邊界③)。 FE:RoundSwitcherBar 既有,改接本組端點(requirement §6「輪次切換」)。

4.0 值域(DB CHECK 強制,FE 直接用)

允許值
round_type initial / close-out / surveillance
status(7 態) not_started / planning / audit_planning / auditing / remediation / pending_reverify / closed
不變式 parent_round_id NOT NULL round_type='close-out'

狀態機流轉(FE 用來決定按鈕可用性)

not_started ─[建立輪次]──────────────────▶ planning            (受評方編 living SSP)
planning    ─[launch-audit, snapshot SSP]─▶ audit_planning      (稽核員寫 AP)
audit_planning ─[開始稽核]───────────────▶ auditing            (填 AO 判定/finding/risk)
auditing    ─[AR 定版]─┬ 無 not_met ─────▶ closed              (直接結案)
                       └ 有 not_met ─────▶ remediation         (系統自動生 POA&M)
remediation ─[整改完+更新 SSP]───────────▶ pending_reverify
pending_reverify ─[launch-reverify]──────▶ (開新 close-out round, status=audit_planning, parent=本輪)

4.1 端點清單

端點 標記 權限 說明
POST /projects/{project_uid}/audit-rounds [新] manager 建輪次(initial / surveillance)
POST /projects/{project_uid}/audit-rounds/list [新] 參與者 輪次分頁/清單(含血緣)
GET /audit-round/{round_uid} [新] 參與者 輪次詳情
POST /audit-round/{round_uid}/launch-audit [新] manager 啟動稽核:snapshot living SSP(邊界③)→ 回填 ssp_id、建/沿用 AP(engagement)、轉 audit_planning
POST /audit-round/{round_uid}/launch-reverify [新] auditor 發起覆核:開新 close-out round(parent_round_id=本輪),重新 snapshot 當下 SSP
POST /audit-round/{round_uid}/start-auditing [新] auditor audit_planning → auditing(AP 草稿補完後開始填判定)
POST /audit-round/{round_uid}/close [新] manager 結案(→ §8 close-round;所有 POA&M closed 才可)

判斷(§11-B):狀態轉換用獨立動詞端點(launch-audit/launch-reverify/start-auditing/close)而非單一 PUT status,因每個轉換有不同前置條件 + 副作用(snapshot / 建 AP / 開新 round),語意明確且權限分流。

4.2 建輪次 POST /projects/{project_uid}/audit-rounds

request

{
  "name": "首次稽核",
  "round_type": "initial",       // initial / surveillance(close-out 不由此端點建,走 launch-reverify)
  "start_at": "2026-07-01"       // 選填
}

response data

{
  "uid": "<str>", "round_no": 1, "name": "首次稽核",
  "round_type": "initial", "status": "planning",
  "parent_round_id": null, "parent_round_uid": null,
  "ssp_id": null, "ssp_uid": null,
  "assessment_plan_id": null, "ap_uid": null,
  "ar_result_id": null,
  "start_at": "2026-07-01", "end_at": null,
  "created_user": "wang", "created_user_name": "王經理"
}

判斷(§11-C):response 同時帶內部 *_id(FK,FE debug 用)與對外 *_uid。FE 慣例以 uid 操作;id 為唯讀傳遞。各 sub-project 可只回 uid,本契約兩者皆列以免歧義。

key errorGRC_ROUND_INVALID_ROUND_TYPE(建 close-out 走錯端點 → 412)、GRC_NOT_MANAGER(GRC_403002)。

4.3 啟動稽核 POST /audit-round/{round_uid}/launch-audit

前置:round.status = planning副作用:snapshot 當前 living SSP(邊界③)→ 回填 ssp_id(凍結不可改);engagement 為 initial/surveillance → 新建 AP(import_ssp_id=快照)+ AR;轉 audit_planningrequest{}(或 { "name": "稽核計畫-第1輪" } 帶給自動生成的 AP 標題) response data

{
  "uid": "<str>", "status": "audit_planning",
  "ssp_uid": "<frozen-snapshot-uid>",   // 凍結快照
  "ap_uid": "<str>",                     // engagement AP(自動生草稿,見 §5)
  "ar_uid": "<str>",
  "ar_result_id": null                   // 開始填判定時才建 result
}

(→ OscalSnapshotService.snapshot_ssp + AssessmentPlanService.create_ap + generate_draft_from_ssp + AssessmentResultService.add_ar

key errorGRC_ROUND_INVALID_STATUS_TRANSITION(GRC_412031)、GRC_ROUND_SSP_NOT_FROZEN(GRC_412033,理論上 launch 才凍結,反向誤呼叫防呆)、GRC_NOT_MANAGER(GRC_403002)。

4.4 發起覆核 POST /audit-round/{round_uid}/launch-reverify

前置:母輪 status = pending_reverify副作用:開新 close-out round(parent_round_id=母輪、round_type='close-out'、沿用母輪 AP/AR);重新 snapshot 當下 living SSP(PM 教材:沿用舊快照是 bug);新 round status = audit_planning;narrowed reviewed-controls 從母輪 not_met findings 推導。 request{ "name": "複驗-第2輪" }(選填) response data:新 round 物件(同 §4.2 shape,round_type:"close-out"parent_round_uid 指母輪、ssp_uid 為新快照、ap_uid 沿用母輪 AP)。 (→ 新增一筆 ar_results(自帶 narrowed scope);AssessmentResultService.add_result

key errorGRC_REVERIFY_PARENT_NOT_PENDING(GRC_412032,母輪不在 pending_reverify)、GRC_NOT_AUDITOR(GRC_403001)。


5. AP 稽核計畫 Assessment Plan([新] — 全新畫面,重點全寫

sub-project BE-B4 / FE-C1。requirement §4.5。對應 assessment_plans + ap_reviewed_controls + ap_assessment_subjects(D-3) + ap_tasks。 套件對應:AssessmentPlanService。AP 回答四件事,只①必填(②③④稽核員臨場補)。

端點 標記 權限 說明
GET /audit-round/{round_uid}/ap [新] 參與者 取本輪 engagement 的 AP(草稿/詳情)
POST /audit-round/{round_uid}/ap/generate-draft [新] auditor 重新從 SSP 快照生草稿(launch-audit 已自動生一次;此為手動重生)
PUT /ap/{ap_uid}/reviewed-controls [新] auditor ①設定查哪些控制(必填)
GET /ap/{ap_uid}/assessment-subjects [新] 參與者 抽查名單(含 SSP 快照可選資產)
PUT /ap/{ap_uid}/assessment-subjects [新] auditor ②設定抽查名單(subject_type/subject_uuid/include)
PUT /ap/{ap_uid}/tasks [新] auditor ③④行程 + 方法

5.1 AP 詳情 GET /audit-round/{round_uid}/ap

response data

{
  "uid": "<str>", "title": "稽核計畫-第1輪",
  "import_ssp_uid": "<frozen-snapshot-uid>",   // 對著哪份凍結快照(AP 鐵則)
  "reviewed_controls": [                         // ① 自動草稿來自 SoA 適用集
    { "control_id": "AC.L2-3.1.1", "title": "...", "included": true }
  ],
  "assessment_subjects": [                       // ② 見 §5.3
    { "uid":"...", "subject_type":"inventory-item", "subject_uuid":"...", "title":"DC-01 網域控制器", "include": true }
  ],
  "tasks": [                                     // ③④ 行程 + 方法
    { "uid":"...", "title":"文件審查", "timing":"2026-07-10", "methods":["EXAMINE","INTERVIEW","TEST"] }
  ],
  "created_user": "auditor1", "created_user_name": "陳稽核"
}

5.2 設定 reviewed-controls PUT /ap/{ap_uid}/reviewed-controls

request{ "control_ids": ["AC.L2-3.1.1", "AC.L2-3.1.2", "..."] } response data{ "ap_uid":"...", "reviewed_control_count": 95 }(→ set_reviewed_controlskey errorGRC_AP_REVIEWED_CONTROLS_REQUIRED(GRC_412034,空清單)、GRC_NOT_AUDITOR(GRC_403001)。

5.3 設定抽查名單 PUT /ap/{ap_uid}/assessment-subjects

全量覆寫(FE 送整份名單)。subject_typecomponent / inventory-item / location / party / user(design §7)。 request

{
  "subjects": [
    { "subject_type":"inventory-item", "subject_uuid":"<ssp-snapshot-obj-uuid>", "include": true,  "title":"DC-01" },
    { "subject_type":"party",          "subject_uuid":"<party-uuid>",            "include": true,  "title":"資安長" },
    { "subject_type":"user",           "subject_uuid":"<...>",                   "include": false }
  ]
}

response data{ "ap_uid":"...", "subjects":[ {uid, subject_type, subject_uuid, include, title, sort_order}, ... ] }(→ set_assessment_subjects

5.4 設定 tasks(行程 + 方法)PUT /ap/{ap_uid}/tasks

request

{ "tasks": [ { "title":"現場訪談", "timing":"2026-07-12", "methods":["INTERVIEW","EXAMINE"], "description":"..." } ] }

response data{ "ap_uid":"...", "tasks":[ {uid, title, timing, methods, sort_order}, ... ] }(→ set_tasks


6. AR 稽核結果 Assessment Result([改] — 中改:AO 全量矩陣 + 風險總結,重點全寫

sub-project BE-B5 / FE-C2。requirement §4.6。判定下沉到 AO 層(Q3):每個 in-scope AO 一筆 finding(met/not_met/pending)。 套件對應:AssessmentResultService(finding 全量矩陣)+ AssessmentRiskService(風險總結,Q2 多對多)。

6.0 值域

允許值
finding state(target_status_state) met(satisfied) / not_met(not-satisfied) / pending(未判定)
risk severity(等級,risk 層判定) low / medium / high / critical
risk status(CMMC 允許集,design §7) open / investigating / remediating / closed(ISO 的 deviation/risk_accepted 本期不 wire)

6.1 端點清單

端點 標記 權限 說明
GET /audit-round/{round_uid}/ar/findings [改] 參與者 AO 全量判定矩陣(以控制項為導覽單位、展開到 AO)+ stats 分母
PUT /ar-finding/{finding_uid} [改] auditor 單 AO upsert 判定(met/not_met/pending)
POST /ar-result/{ar_result_uid}/observations [新] auditor 建 observation(看到什麼,好壞都記;引用證據不複製)
POST /audit-round/{round_uid}/ar/risks [新] auditor 建系統風險(風險等級在 risk 層)
GET /audit-round/{round_uid}/ar/risks [新] 參與者 風險總結列表(含關聯 finding 數)
PUT /ar-risk/{risk_uid}/findings [新] auditor 組風險:勾選哪幾條 finding(多對多)
POST /audit-round/{round_uid}/ar/finalize [改] auditor AR 定版:判定完整性檢查 → 追加一筆 ar_results → 無 not_met 結案 / 有 not_met 轉 remediation

6.2 AO 全量矩陣 GET /audit-round/{round_uid}/ar/findings

response data(以控制項為導覽單位,每控制項展開其 AO findings)

{
  "ar_result_uid": "<str>",
  "stats": {                                  // 分母 = in-scope AO 總數
    "total_ao": 320, "met": 280, "not_met": 25, "pending": 15,
    "controls_total": 95, "controls_with_not_met": 18
  },
  "controls": [
    {
      "control_id": "AC.L2-3.1.1", "title": "...",
      "ao_findings": [
        {
          "finding_uid": "<str>",
          "ao_id": "AC.L2-3.1.1[a]",          // catalog_control_parts (AO) 的識別
          "ao_statement": "determine if ...",
          "state": "met",                      // met / not_met / pending
          "observation_uid": null,
          "updated_user": "auditor1", "updated_user_name": "陳稽核"
        }
      ]
    }
  ]
}

(→ AssessmentResultService.list_findings;finding 掛 catalog_control_parts(AO))

6.3 單 AO 判定 PUT /ar-finding/{finding_uid}

request{ "state": "not_met", "description": "未發現密碼複雜度政策", "observation_uid": "<opt>" } response data{ finding_uid, ao_id, state, description, observation_uid, updated_user, updated_user_name }(→ upsert_findingkey errorGRC_AR_RESULT_NOT_LATEST(GRC_412008 既有 GRC_AR_NOT_LATEST_ROUND)、GRC_NOT_AUDITOR(GRC_403001)。

6.4 建風險 POST /audit-round/{round_uid}/ar/risks

request

{ "title":"存取控制缺口", "description":"...", "severity":"high", "status":"open" }

response data{ uid, title, description, severity, status, linked_finding_count:0, created_user, created_user_name }(→ AssessmentRiskService.add_risk

6.5 組風險(多對多)PUT /ar-risk/{risk_uid}/findings

全量覆寫該 risk 的 finding 關聯(oscal.assessment_finding_risks)。 request{ "finding_uids": ["<not_met-finding-1>", "<not_met-finding-2>"] } response data{ risk_uid, linked_findings: [ {finding_uid, ao_id, state}, ... ] }(→ AssessmentRiskService.link_findingskey errorGRC_RISK_NO_FINDING_LINKED(GRC_412037,定版前 risk 必須至少關聯一條 finding)、GRC_NOT_AUDITOR(GRC_403001)。

6.6 AR 定版 POST /audit-round/{round_uid}/ar/finalize

前置:所有 in-scope AO 有判定(無 pending);每個 not_met finding 有 finding 紀錄;每個 risk 至少關聯一條 finding。 副作用:追加 ar_results(不改舊的);無 not_met → round 轉 closed;有 not_met → round 轉 remediation + 自動生 POA&M(§7)。 request{} response data

{
  "ar_result_uid": "<str>", "round_status": "remediation",  // 或 "closed"
  "not_met_count": 25, "poam_generated": true, "poam_item_count": 18
}

(→ AssessmentResultService.add_result + PoamService.generate_from_findings

key errorGRC_VERDICT_INCOMPLETE(GRC_412003,有 pending)、GRC_FINDING_REQUIRED_FOR_FAILED(GRC_412011)、GRC_RISK_NO_FINDING_LINKED(GRC_412037)、GRC_AR_RESULT_NOT_LATEST(GRC_412008)、GRC_NOT_AUDITOR(GRC_403001)。


7. POA&M 改善計劃([改] — 小改:milestone+assignee / 180 天 / 三層整改

sub-project BE-B5 / FE-C3。requirement §4.7。FE 100% 現成(小改)。 三層忠實落地:risk → assessment_remediations(response) → poam_milestones(task)(D-4b)。POA&M item 用 FK 指回 AR,不複製(PM1)。 套件對應:PoamService

端點 標記 權限 說明
POST /audit-round/{round_uid}/poam-items/list [改] 參與者 POA&M item 分頁列表(含 180 天告警欄)
GET /poam-item/{item_uid} [改] 參與者 POA&M item 詳情(封面 + 連著的 risk/remediation/milestone)
POST /poam-item/{item_uid}/remediations [新] manager 建整改計畫(response;CMMC 可自動帶預設一筆)
POST /remediation/{remediation_uid}/milestones [新] manager 加里程碑(per-milestone assignee)
PUT /poam-milestone/{milestone_uid} [新] manager 改里程碑(狀態 / assignee / target_date)
POST /audit-round/{round_uid}/close-round [新] manager 結案(→ §8)

7.1 POA&M item 列表 POST /audit-round/{round_uid}/poam-items/list

response data(陣列)

[
  {
    "uid": "<str>", "title": "存取控制缺口", "status": "open",
    "risk_uid": "<str>", "severity": "high",
    "target_date": "2026-09-15",
    "days_remaining": 93,          // 180 天追蹤
    "deadline_warning": false,     // CMMC 180 天逼近告警(PM4)
    "milestone_count": 3, "milestone_done": 1
  }
]

deadline_warning 判斷(§11-D)days_remaining <= 30(或最早 target_date 逾期)為 true;確切門檻 Wave 2 定,FE 只讀此布林。

7.2 整改計畫 POST /poam-item/{item_uid}/remediations

request{ "title":"整改計畫", "description":"...", "lifecycle":"planned" } response data{ uid, risk_uid, title, description, lifecycle, milestone_count:0 }(→ upsert_remediation

7.3 加里程碑 POST /remediation/{remediation_uid}/milestones

request

{
  "title": "M1 部署密碼政策",
  "description": "...",
  "assignee_user_id": 456,        // per-milestone 負責人(跨部門可拆)
  "assignee_org_unit_id": 12,
  "target_date": "2026-08-20",
  "task_type": "milestone"
}

response data

{
  "uid": "<str>", "title": "M1 部署密碼政策", "status": "open",
  "assignee_user_id": 456, "assignee_user_name": "李工程師", "assignee_login_name": "lee",
  "assignee_org_unit_id": 12, "assignee_org_unit_name": "資訊部",
  "target_date": "2026-08-20", "task_type": "milestone"
}

(→ add_milestoneoscal.poam_milestones

7.4 改里程碑 PUT /poam-milestone/{milestone_uid}

request{ "status":"done", "assignee_user_id":789, "target_date":"2026-08-25" }(部分欄位) response data:同 §7.3 shape(含 completed_at 當 status=done)。 值域:milestone statusopen / in_progress / donekey errorGRC_POAM_NOT_FOUND(GRC_404019)、GRC_POAM_INVALID_STATUS_TRANSITION(GRC_412006)、GRC_NOT_MANAGER(GRC_403002)。


8. 結案 / 覆核連動 Close Round([新]

sub-project BE-B5。requirement §4.8。

POST /audit-round/{round_uid}/close-round 前置:本輪所有 POA&M item closed(GRC_POAM_NOT_ALL_CLOSED(GRC_412005));本輪 status = remediationpending_reverify行為差異

  • 母輪(initial/surveillance):整改完成 → 轉 pending_reverify(不能自宣告通過,等覆核)。
  • close-out 輪定版:確認母輪 not_met AO 過了 → 沿 parent_round_id 回頭關閉母輪 POA&M → 母輪轉 closed(跨 round 連動,design §3)。

request{} response data

{
  "round_uid": "<str>", "status": "pending_reverify",   // 或 "closed"(close-out 連動)
  "parent_round_closed": false,                          // close-out 連動關閉母輪時 true
  "parent_round_uid": null
}

key errorGRC_POAM_NOT_ALL_CLOSED(GRC_412005)、GRC_ROUND_INVALID_STATUS_TRANSITION(GRC_412031)、GRC_NOT_MANAGER(GRC_403002)。


9. SSP 維護(不大改 — OSCAL 落點對齊 + 新增「啟動稽核」動作)

sub-project BE-B3。FE 100% 現成(7 tabs)。requirement §4.4 + §5。 SSP 維護端點 [沿用] 現有 contract(system-characteristics / parties / components / inventory / implemented-requirements / by-components / SoA props),僅落地資料對齊 v2 schema + UI→OSCAL 對應(§5 表)。 套件對應:SspService 子物件 CRUD。

端點群 標記 說明
GET/PUT /ssp/{uid}/system-characteristics [沿用] 受評標的 → system-characteristics
GET/PUT /ssp/{uid}/parties [沿用] 人員/單位 → parties+roles
GET/POST/PUT/DELETE /ssp/{uid}/components [沿用] 元件
GET/POST/PUT/DELETE /ssp/{uid}/inventory-items [沿用] 資產清冊
GET/PUT /ssp/{uid}/implemented-requirements/{control_id} [沿用] 控制實作 + SoA(props.applicability / inclusion-justification / implementation-status;statements[].by-components[])
SSP docx/excel 匯入 [沿用] ssp-docx-import-v2 流程

「啟動稽核 → snapshot」動作不在 SSP 模組,而在輪次 §4.3 launch-audit(snapshot 後 living SSP 仍可改)。FE 在 SSP 頁 header 放「啟動稽核」按鈕,呼叫 §4.3。


10. 任務執行 My Jobs(不大改 — 綁定點後端改,FE 不動)

requirement §4.3b / §6(Q1)。FE 100% 現成、畫面不動。 變更:workflow/job 綁定點後端從「AP task」改為「專案 SSP 控制項」,且在專案成立(§3.2)就建。稽核員 Phase 3 走 AR 判定(§6),走 job 引擎。

端點 標記 說明
POST /my-jobs/list(或既有 SP get_user_task_queue [沿用] 我的任務佇列
GET /job-execution/{uid} + 留言 / 證據子端點 [沿用] 任務執行詳情

11. OSCAL 匯出 Export([新]

sub-project BE-B5。套件對應:OscalIoService.export_oscal(doc_type, uid, fmt)

GET /oscal/export/{doc_type}/{uid}?fmt=json pathdoc_typecatalog / ssp / assessment-results / poamuid = 對應 OSCAL 物件 uid。 queryfmtjson(本期主力)/ xml / yaml權限:參與者(manager/auditor 皆可匯出本專案文件)。 response:標準 OSCAL v1.2.2 文件(Content-Type: application/json 直接回 OSCAL JSON body,不包 envelope —— 因為要餵外部工具)。

判斷(§11-E):匯出端點回裸 OSCAL JSON 不包 {code,data},與其他端點不同;FE 下載時直接存檔。各 sub-project 落地時於 api-spec.md 標明此例外。 key error:對應 doc not found(沿用各模組 not-found 碼,包在 envelope 回 404)。


12. 新增 Error Code 規劃(待 Wave 2 開工前複查最大序號續編)

命名 GRC_<HTTP><3位序號>現有最大值(2026-06-14 grep):404→404036、412→412030、403→403053、409→409033。 下表為保留意圖;Wave 2 實作前再 grep common/code/grc_error_code.py 確認無衝突後定序號(§11-F:本契約引用的序號為建議值,可能在 Wave 2 微調)。

Error Code(建議) HTTP 訊息 對應
GRC_ROUND_NOT_FOUND = GRC_404037 404 稽核輪次不存在 §4
GRC_AP_SUBJECT_NOT_FOUND = GRC_404038 404 抽查名單項目不存在 §5.3
GRC_AR_RISK_NOT_FOUND = GRC_404039 404 系統風險不存在 §6.4
GRC_REMEDIATION_NOT_FOUND = GRC_404040 404 整改計畫不存在 §7.2
GRC_POAM_MILESTONE_NOT_FOUND = GRC_404041 404 整改里程碑不存在 §7.3
GRC_ROUND_INVALID_STATUS_TRANSITION = GRC_412031 412 輪次狀態轉換不合法 §4.3 / §8
GRC_REVERIFY_PARENT_NOT_PENDING = GRC_412032 412 母輪不在待複驗狀態,無法發起覆核 §4.4
GRC_ROUND_SSP_NOT_FROZEN = GRC_412033 412 SSP 快照尚未凍結 §4.3
GRC_AP_REVIEWED_CONTROLS_REQUIRED = GRC_412034 412 AP 必須至少設定一個查核控制項 §5.2
GRC_ROUND_INVALID_ROUND_TYPE = GRC_412035 412 輪次類型不合法(close-out 須走 launch-reverify) §4.2
GRC_AR_FINDING_TARGET_REQUIRED = GRC_412036 412 finding 缺少目標 AO §6.3
GRC_RISK_NO_FINDING_LINKED = GRC_412037 412 風險至少需關聯一條 finding §6.5 / §6.6

沿用既有碼:GRC_VERDICT_INCOMPLETE(412003)、GRC_FINDING_REQUIRED_FOR_FAILED(412011)、GRC_AR_NOT_LATEST_ROUND(412008)、GRC_POAM_NOT_ALL_CLOSED(412005)、GRC_POAM_INVALID_STATUS_TRANSITION(412006)、GRC_NOT_MANAGER(403002)、GRC_NOT_AUDITOR(403001)、GRC_POAM_NOT_FOUND(404019)、GRC_FRAMEWORK_VERSION_NOT_FOUND(404032)。


附錄 A:契約判斷(ambiguity 註記 — 供 user verify)

# 端點 / 議題 我做的判斷 反悔成本
§11-A 專案成立路徑 /oscal-project/start(對齊現有 route),不改 /projects/{uid}/start,避免破壞既有 FE 呼叫點 低(改路徑常數)
§11-B 輪次狀態轉換 用獨立動詞端點(launch-audit / launch-reverify / start-auditing / close-round),非單一 PUT status;每轉換有不同前置 + 副作用
§11-C response id vs uid 同時列 *_id(FK)與 *_uid(對外);FE 以 uid 操作。各 sub-project 可只回 uid
§11-D POA&M 180 天告警 response 給布林 deadline_warning + days_remaining,門檻(建議 ≤30 天)Wave 2 定,FE 只讀布林
§11-E OSCAL 匯出 envelope 匯出端點回裸 OSCAL JSON 不包 {code,data}(餵外部工具),與全站慣例不同 中(FE 下載處理)
§11-F error code 序號 表列序號為建議續編值;Wave 2 開工前須 re-grep 確認無衝突再定
§11-G reviewed-controls 粒度 AP reviewed-controlscontrol 層設定(§5.2),AO 全量判定(§6.2)由 in-scope control 自動展開其 AO;非在 AP 逐 AO 勾選
§11-H assessment-subjects 寫入 全量覆寫(PUT 整份名單)而非逐筆 POST/DELETE,對齊 FE「勾選名單一次送出」UX
§11-I finding/subject 多對多寫入 PUT /ar-risk/{uid}/findings 與 subjects 同採全量覆寫語意

附錄 B:套件 service method ↔︎ 端點 traceability

套件 method(design §4.2) 端點
FrameworkService.add_framework_version / publish_version §1 import / publish
CatalogService.import_catalog_from_pdf §1 framework-versions/import
ProfileService.resolve_profile(邊界①) §2 resource-libraries(建立)
OscalSnapshotService.clone_resource_library(邊界②) §2 建立資源庫 / §3.2 專案成立
OscalSnapshotService.snapshot_ssp(邊界③) §4.3 launch-audit / §4.4 launch-reverify
AssessmentPlanService.create_ap / generate_draft_from_ssp §4.3 launch-audit / §5.1 / §5 generate-draft
AssessmentPlanService.set_reviewed_controls / set_assessment_subjects / set_tasks §5.2 / §5.3 / §5.4
AssessmentResultService.add_ar / add_result / upsert_finding / list_findings §4.3 / §6.6 / §6.3 / §6.2
AssessmentRiskService.add_risk / link_findings / list_risks §6.4 / §6.5 / §6.5(list)
PoamService.generate_from_findings / upsert_remediation / add_milestone / list_* §6.6 / §7.2 / §7.3 / §7.1
OscalIoService.export_oscal §11
OscalIoService.import_ssp_docx/excel §2 import-ssp / §9 SSP 匯入