稽核執行 功能群總覽
本檔放「本功能群多個頁面共用的知識」:頁面地圖、稽核階段與狀態機、角色權限基調、AP→AR→POA&M 核心資料模型。
各頁面 spec 引用本檔,只寫自己頁面的差異——改共用邏輯時改這裡一份。稽核輪次狀態機本體與轉換圖在 專案管理群 _overview §2(同一個
compliance.project_audit_rounds實體),本檔不重畫,只補本群相關的階段轉換與資料流。
1. 頁面地圖
稽核執行群承接啟動稽核之後的三個階段主場頁面,加上兩個跨階段的個人任務入口。
| 頁面 | FE route | 用途一句話 | spec |
|---|---|---|---|
| 稽核計畫填寫頁 | /project/projects/:id/round/:roundUid/ap-authoring |
audit_planning 階段:定義 AP(受評控制項 / 行程與查核方法 / 受評對象 / 稽核人員) | ✅ 稽核計畫填寫頁 |
| 稽核執行頁(AR 判定) | /project/projects/:id/round/:roundUid/audit |
auditing 階段:逐控制項判定 met/not_met/pending、記觀察、建系統風險、finalize | ✅ 稽核執行頁 |
| POA&M 矯正頁 | /project/projects/:id/round/:roundUid/poam |
remediation 階段:整改計畫與里程碑追蹤 | ⬜ poam.md |
| 我的任務(受稽方執行) | /project/task-manage |
跨專案的證據蒐集任務收件匣 | ⬜ my-tasks.md |
| 我的稽核 | /project/audit-manage |
稽核員視角的待辦稽核清單 | ⬜ my-audits.md |
頁面 × 階段對應(每個稽核輪次階段有一個「主場頁面」):
planning → 專案規劃頁(專案管理群) living SSP 現況 + 證據蒐集任務準備
audit_planning → 稽核計畫填寫頁(本群) 凍結 SSP 快照 → 定義 AP
auditing → 稽核執行頁 / AR 判定(本群) 逐控制項判定 → 產出 AR
remediation → POA&M 矯正頁(本群) not_met 缺失 → 整改追蹤
closed → 各頁唯讀
進入本群頁面前,輪次已
launch-audit過(living SSP 已凍結成project_audit_rounds.ssp_id快照)。本群所有頁面讀寫的控制項母體都來自凍結快照,不是 living SSP——這是與專案規劃頁最大的差異。
2. 稽核階段與狀態機(本群關心的轉換)
輪次 7 態狀態機與完整轉換圖見 專案管理群 _overview
§2。本群關心的是「啟動稽核之後」的三段推進,enforcement 在
app/grc/service/audit_round_app_service.py:
| 轉換 | API | 誰能按 | 前置條件 | 失敗 error code |
|---|---|---|---|---|
| 啟動稽核 launch-audit → audit_planning | POST /audit-round/{uid}/launch-audit |
manager | status=planning、living SSP 非空(凍結快照) | — |
| 開始稽核 start-auditing → auditing | FE 實走
POST /project/{uid}/audit-round/{uid}/stage/advance(precondition
registry);底層直接 API
POST /audit-round/{uid}/start-auditing |
auditor(manager 可代行) | stage/advance
路徑:ApReviewedControlsSetCheck 查
reviewed-controls
非空(oscal_stage_preconditions.py:40,precondition 失敗回
i18n reason key
flow_engine.precondition.ap_reviewed_controls_required,非
error code);直接 API start_auditing() 只驗 AP
存在(audit_round_app_service.py,可繞過
reviewed-controls 檢查) |
直接 API 無 AP →
GRC_AP_NOT_FOUND。(GRC_AP_REVIEWED_CONTROLS_REQUIRED(412)實為
AP 編輯期 set_reviewed_controls
擋空清單,assessment_plan_app_service.py:470,非本轉換的關卡) |
| 完成判定 finalize → remediation | closed | POST /audit-round/{uid}/ar/finalize |
auditor | 無 pending 判定 + 每條 risk 至少關聯 1 finding | GRC_VERDICT_INCOMPLETE /
GRC_RISK_NO_FINDING_LINKED(412) |
| 結束本輪 close-round → closed | POST /audit-round/{uid}/close-round |
manager | POA&M 全部 closed | GRC_POAM_NOT_ALL_CLOSED(412) |
| 啟動複驗 launch-reverify(開覆核輪) | POST /audit-round/{uid}/launch-reverify |
auditor(manager 可代行) | status=pending_reverify | — |
finalize
的分歧(本群最重要的轉換):ar/finalize
依判定結果決定去向——
- not_met = 0 → 輪次直接
closed(無缺失,不進整改) - not_met > 0 → 自動從 not_met findings 生成
POA&M(
PoamService.generate_from_findings),輪次轉remediation、回填project_audit_rounds.poam_id
通用 phase
guard(本群與專案規劃頁的關鍵差異):本群每個寫入 API 都用
common/util/round_guard.py 的
assert_round_phase(round, {允許階段})
硬擋——AP 寫入只在 audit_planning、AR
寫入只在 auditing,過了階段回
GRC_ROUND_PHASE_READONLY(412)。這與專案規劃頁「唯讀只是
FE 鎖、BE upsert 照寫」不同(見 project-planning §12 坑
3);本群的唯讀是 BE 強制的。
3. 角色權限基調
角色是誰:
- auditor(稽核員,本群主角)——填 AP 行程與查核方法、逐控制項判定 met/not_met、記觀察與風險、finalize。「身為 auditor,我要依稽核計畫逐控制項判定合規狀態,記錄觀察與風險,完成後送出這一輪的結果。」
- manager(專案負責人)——可代行 auditor 的所有判定操作,且獨掌輪次收尾。「身為 manager,我要在 auditor 判定完成、整改都結束後,把這一輪正式結案。」
- viewer(一般參與者)——檢視稽核進度,無編輯權。
專案層角色(compliance.project_participants.role):manager
/ auditor / viewer。角色定義與 manager
全域代行慣例見 專案管理群
_overview §3。本群的基調轉向 auditor
為主角(專案規劃頁以 manager 為主角):
| 動作類型 | manager | auditor | viewer |
|---|---|---|---|
| AP 編輯(受評控制項 / 行程 / 對象 / 稽核人員) | ✓(代行) | ✓ | ✗ |
| AR 判定(verdict / 觀察 / 風險 / finalize) | ✓(代行) | ✓ | ✗ |
| 輪次生命週期(launch / close-round) | ✓ | ✗ | ✗ |
| 檢視 | ✓ | ✓ | ✓ |
BE 檢查點:AP / AR 寫入走
_check_auditor(project_id, user_id)(role in ("auditor","manager")
才過,否則 GRC_NOT_AUDITOR);close-round 走
_check_role(..., "manager")。FE
判定:兩頁都以
roundStatus === '<本頁階段>' && (isAuditor || isProjectManager)
決定可編輯(RoundApAuthoringView.vue /
RoundAuditReviewView.vue),與 BE 對齊。
4. 核心資料模型(AP → AR → POA&M)
本群走的是 OSCAL
資訊流的後半段:Catalog → Profile → SSP →
AP → AR → POA&M。三份文件是獨立的
OSCAL root,靠 import-* FK 串接。
三份文件的職責:
| 文件 | root 表 | 承接自 | 內容 | 本群哪頁編輯 |
|---|---|---|---|---|
| AP(評估計畫) | oscal.assessment_plans |
import_ssp_id → 凍結 SSP |
reviewed-controls(查核範圍)/ tasks(行程+方法)/ assessment-subjects(抽查對象)/ parties(稽核人員) | 稽核計畫填寫頁 |
| AR(評估結果) | oscal.assessment_results →
oscal.ar_results(每輪一筆 result) |
import_ap_id → AP |
findings(逐 AO 判定)/ observations(觀察)/ risks(系統風險) | 稽核執行頁 |
| POA&M(矯正計畫) | oscal.poams → oscal.poam_items |
poam_id(round 回填),內容源自 AR 的 not_met
findings |
poam-items / remediations(response)/ milestones | POA&M 矯正頁 |
engagement 模型(本群最重要的概念):一個 engagement
= 1 輪主稽核(initial/surveillance)+ N
輪覆核(close-out),共用同一份 AP(OSCAL
import_ssp_id 單一)與同一份 AR
root(import_ap_id 單一),但每輪各 append 一筆
oscal.ar_results;覆核輪 launch-reverify 時
clone 母輪 AP(clone_ap_for_round,party
uuid 依 name remap)成獨立副本。
AO(assessment objective)判定粒度:
- 判定矩陣是 AO 層:每個在範圍控制項的每個 AO = 一筆
finding(
target_id = ao_id,_obj.N格式);控制項無 AO part 時 fallback 成 control 層(target_id = control_id) - UI 與 POA&M 是控制項層:稽核執行頁面板以控制項為單位,POA&M 一控制項一 item
- 控制項 verdict roll-up:控制項下所有 AO 都 met → 控制項 met;任一 AO not_met → 控制項 not_met
- AO 衍生邏輯
app/grc/service/ao_derivation.py::derive_ao_pairs(B5 AR 與規劃期 prep-job 共用),AO 識別碼 canonical =_obj.N(見 專案管理群 _overview §5)
5. 本群共用的已知坑
- 凍結快照,不是 living SSP:本群讀控制項 /
資產一律走
project_audit_rounds.ssp_id(launch-audit 當下拍的快照)。專案規劃頁的 living SSP 之後繼續演進,不影響本輪稽核 - phase guard 是 BE 硬擋:AP 只能在 audit_planning 改、AR 只能在 auditing 改,過階段 BE 回 412(不是只有 FE 鎖)——與專案規劃頁相反,別把規劃頁「BE 照寫」的心智模型套過來
- finding 判定 AO 層 / 顯示與 POA&M
控制項層:改判定相關邏輯先確認你在哪一層;
stats有met/not_met/pending(AO 計數)與controls_with_not_met(控制項計數)兩套 - verdict 值 API ≠ DB,但
target_status_state直存 4 種 token:API 用FindingStateenum(met/not_met/pending,另有第 4 值not_applicable);DBoscal.assessment_findings.target_status_state直接存 4 種 product token——satisfied(met)/not-satisfied(not_met)/not-applicable(na,OSCAL 規格無此值、比照 pending 直存 product token)/pending(未判定)。target_statusJSONB 是全狀態的冗餘鏡射(非pending專屬),比對以target_status_state為準(jedi-oscal-v2 .../domain/service/ar/ar_finding_matrix_service.py) - risk severity 存
JSONB:
oscal.assessment_risks無 severity 欄,等級存在characterizationsJSONB(稽核員判定),不是列舉欄位 - finding ↔︎ risk
多對多:
oscal.assessment_finding_risks關聯表 + 雙寫finding.related_risks[](OSCAL 匯出保真);finalize 前每條 risk 必須至少關聯一條 finding - 一 engagement 一 AP/AR:不要為覆核輪另開全新 AP/AR
root;覆核走 clone_ap_for_round + append ar_results。輪次反查走
task_assignees.project_id,不要比對 workflow_execution_uid(那是死路) - 輪次狀態 BPMN 驅動:直接 UPDATE
project_audit_rounds.status會跟 workflow 脫鉤,一律走 API(start-auditing/ar/finalize/close-round)