範圍:規劃層 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
| 面向 | 規則 |
|---|---|
| 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,本契約以相對語意路徑表達。
sub-project BE-B1。FE 100% 現成。本期變動:
oscal_frameworks→frameworks、oscal_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 error:GRC_FRAMEWORK_VERSION_NOT_FOUND(GRC_404032)、GRC_IMPORT_INVALID_FILE(GRC_400001)、GRC_IMPORT_FILE_TOO_LARGE(GRC_400002)、GRC_NOT_MANAGER(GRC_403002)。
sub-project BE-B1。FE 100% 現成。新定位:catalog 副本 + profile + SSP 範本 三件組,由稽核顧問維護。 套件對應:
OscalSnapshotService.clone_resource_library(邊界②/①)、OscalIoService.import_ssp_docx/excel、ProfileService.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 error:GRC_FRAMEWORK_VERSION_NOT_FOUND(GRC_404032)、GRC_EXCEL_INVALID_FILE(GRC_400065)、GRC_NOT_MANAGER(GRC_403002)。
sub-project BE-B2。FE 列表/建立/總覽 100% 現成。
| 端點 | 標記 | 權限 | 說明 |
|---|---|---|---|
POST /projects/list |
[沿用] | 全參與者 | 專案分頁列表(加上 OSCAL AP 統計視角) |
GET /project/{uid} |
[沿用] | 參與者 | 專案總覽(基本資訊 + 參與者 + 輪次摘要) |
POST /projects 或既有建立路由 |
[沿用] | manager | 建專案主檔基本資訊(不含 clone;clone 由 §3.2 start 觸發) |
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 error:GRC_PROJECT_NAME_DUPLICATED(GRC_409005)、resource-library not found(沿用資源庫 not-found 碼)、GRC_NOT_MANAGER(GRC_403002)。
sub-project BE-B3。對應
compliance.project_audit_rounds(oscal-v2-deltas D-2)。 狀態機留主專案(design §4.4);snapshot 呼叫套件OscalSnapshotService.snapshot_ssp(邊界③)。 FE:RoundSwitcherBar既有,改接本組端點(requirement §6「輪次切換」)。
| 欄 | 允許值 |
|---|---|
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=本輪)
| 端點 | 標記 | 權限 | 說明 |
|---|---|---|---|
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),語意明確且權限分流。
POST /projects/{project_uid}/audit-roundsrequest
{
"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 error:GRC_ROUND_INVALID_ROUND_TYPE(建 close-out 走錯端點 → 412)、GRC_NOT_MANAGER(GRC_403002)。
POST /audit-round/{round_uid}/launch-audit前置:round.status = planning。 副作用:snapshot 當前 living SSP(邊界③)→ 回填 ssp_id(凍結不可改);engagement 為 initial/surveillance → 新建 AP(import_ssp_id=快照)+ AR;轉 audit_planning。 request:{}(或 { "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 error:GRC_ROUND_INVALID_STATUS_TRANSITION(GRC_412031)、GRC_ROUND_SSP_NOT_FROZEN(GRC_412033,理論上 launch 才凍結,反向誤呼叫防呆)、GRC_NOT_MANAGER(GRC_403002)。
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 error:GRC_REVERIFY_PARENT_NOT_PENDING(GRC_412032,母輪不在 pending_reverify)、GRC_NOT_AUDITOR(GRC_403001)。
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 | ③④行程 + 方法 |
GET /audit-round/{round_uid}/apresponse 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": "陳稽核"
}
PUT /ap/{ap_uid}/reviewed-controlsrequest:{ "control_ids": ["AC.L2-3.1.1", "AC.L2-3.1.2", "..."] } response data:{ "ap_uid":"...", "reviewed_control_count": 95 }(→ set_reviewed_controls) key error:GRC_AP_REVIEWED_CONTROLS_REQUIRED(GRC_412034,空清單)、GRC_NOT_AUDITOR(GRC_403001)。
PUT /ap/{ap_uid}/assessment-subjects全量覆寫(FE 送整份名單)。
subject_type∈component / 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)
PUT /ap/{ap_uid}/tasksrequest
{ "tasks": [ { "title":"現場訪談", "timing":"2026-07-12", "methods":["INTERVIEW","EXAMINE"], "description":"..." } ] }
response data:{ "ap_uid":"...", "tasks":[ {uid, title, timing, methods, sort_order}, ... ] }(→ set_tasks)
sub-project BE-B5 / FE-C2。requirement §4.6。判定下沉到 AO 層(Q3):每個 in-scope AO 一筆 finding(
met/not_met/pending)。 套件對應:AssessmentResultService(finding 全量矩陣)+AssessmentRiskService(風險總結,Q2 多對多)。
| 欄 | 允許值 |
|---|---|
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) |
| 端點 | 標記 | 權限 | 說明 |
|---|---|---|---|
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 |
GET /audit-round/{round_uid}/ar/findingsresponse 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))
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_finding) key error:GRC_AR_RESULT_NOT_LATEST(GRC_412008 既有 GRC_AR_NOT_LATEST_ROUND)、GRC_NOT_AUDITOR(GRC_403001)。
POST /audit-round/{round_uid}/ar/risksrequest
{ "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)
PUT /ar-risk/{risk_uid}/findings全量覆寫該 risk 的 finding 關聯(
oscal.assessment_finding_risks)。 request:{ "finding_uids": ["<not_met-finding-1>", "<not_met-finding-2>"] }responsedata:{ risk_uid, linked_findings: [ {finding_uid, ao_id, state}, ... ] }(→AssessmentRiskService.link_findings) key error:GRC_RISK_NO_FINDING_LINKED(GRC_412037,定版前 risk 必須至少關聯一條 finding)、GRC_NOT_AUDITOR(GRC_403001)。
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 error:GRC_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)。
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) |
POST /audit-round/{round_uid}/poam-items/listresponse 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 只讀此布林。
POST /poam-item/{item_uid}/remediationsrequest:{ "title":"整改計畫", "description":"...", "lifecycle":"planned" } response data:{ uid, risk_uid, title, description, lifecycle, milestone_count:0 }(→ upsert_remediation)
POST /remediation/{remediation_uid}/milestonesrequest
{
"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_milestone → oscal.poam_milestones)
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 status ∈ open / in_progress / done。 key error:GRC_POAM_NOT_FOUND(GRC_404019)、GRC_POAM_INVALID_STATUS_TRANSITION(GRC_412006)、GRC_NOT_MANAGER(GRC_403002)。
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 = remediation 或 pending_reverify。 行為差異:
pending_reverify(不能自宣告通過,等覆核)。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 error:GRC_POAM_NOT_ALL_CLOSED(GRC_412005)、GRC_ROUND_INVALID_STATUS_TRANSITION(GRC_412031)、GRC_NOT_MANAGER(GRC_403002)。
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。
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} + 留言 / 證據子端點 |
[沿用] | 任務執行詳情 |
sub-project BE-B5。套件對應:
OscalIoService.export_oscal(doc_type, uid, fmt)。
GET /oscal/export/{doc_type}/{uid}?fmt=json path:doc_type ∈ catalog / ssp / assessment-results / poam;uid = 對應 OSCAL 物件 uid。 query:fmt ∈ json(本期主力)/ 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)。
命名
GRC_<HTTP><3位序號>。現有最大值(2026-06-14 grep):404→404036、412→412030、403→403053、409→409033。 下表為保留意圖;Wave 2 實作前再 grepcommon/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)。
| # | 端點 / 議題 | 我做的判斷 | 反悔成本 |
|---|---|---|---|
| §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-controls 以 control 層設定(§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 同採全量覆寫語意 |
低 |
| 套件 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 匯入 |