手冊首頁 / 專案與任務 / 稽核風險管理

稽核風險管理(Audit Risk)

功能群:稽核執行|階段轉換、AP→AR→POA&M 資料模型、AO 判定粒度先讀 功能群總覽|上層導航頁見 稽核執行頁

事實基準:2026-07-05 從 FE src/views/project/RoundAuditReviewView.vue(Tab1 風險)+ BE assessment_result_app_service.py / serializer api/project/serializers/audit_round.py 掃出(FR-038 v2 B5 AR,Q2 finding↔︎risk 多對多)

變更紀錄

日期 FR 變更
2026-07-06 FR-047 audit-review.md 拆出:Tab1 風險管理(系統風險 CRUD + finding 多對多關聯,UC-AR-02)獨立成子頁

1. 功能描述

稽核風險管理是 稽核執行頁Tab1「風險」:稽核員把逐控制項判定出的不符合項(not_met finding)歸納成系統風險(system risk)。每個風險有標題 / 描述 / 嚴重度(severity)/ 狀態,並勾選由哪幾條 not_met finding 組成(多對多、全量覆寫)。這批風險是 finalize 前置檢查的一部分——每條風險 finalize 前必須至少關聯 1 條 finding(見 _overview §5 坑 6)。風險本體與 Tab0 判定右欄的 not_met 內嵌風險是同一批資料(同 assessment_risks),Tab1 提供全風險的集中管理視圖。

主要使用者:稽核員(auditor)、專案經理(manager,可代行)。

角色速覽(完整定義見 _overview §3;本群主角是 auditor):

角色 一句話
auditor 稽核員 — 建立 / 編輯風險與關聯判定的執行者
manager 專案經理 — 可代行 auditor
viewer 一般參與者 — 只能檢視

1.1 功能總覽(本頁全部功能)

# 功能 說明 位置 詳述
1 系統風險列表 Tab1 呈現本輪全部系統風險(標題 / 描述 / 嚴重度 / 狀態 / 關聯 finding 數) Tab1 清單 §6.2
2 建立系統風險 標題 / 描述 / 等級 / 來源 not_met 控制項 Tab1 建立表單 UC-AR-02
3 編輯 / 刪除風險 編輯欄位或刪除(刪除解除 finding 連結) Tab1 風險卡 UC-AR-02
4 風險關聯判定(finding) 一個風險勾選由哪幾條 not_met finding 組成(多對多,全量覆寫) Tab1 風險卡 sources UC-AR-02

展開成 UC:UC-AR-02(建立系統風險並關聯判定)。判定 / 觀察 / not_met 內嵌風險的輸入端稽核判定工作區;頁級 finalize(風險關聯完整性檢查在此把關)見 稽核執行頁 hub。

2. Use Case

角色速覽(完整定義見 _overview §3;本群主角是 auditor):

角色 一句話
auditor 稽核員 — 建立 / 編輯風險與關聯判定的執行者
manager 專案經理 — 可代行 auditor
viewer 一般參與者 — 只能檢視

流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。

UC-AR-02 建立系統風險並關聯判定

項目 內容
角色 auditor(manager 可代行)
前置條件 輪次 = auditing;已有 not_met 判定可供關聯
產出 / 後置條件 oscal.assessment_risks(severity 存 characterizations JSONB)+ assessment_finding_risks 關聯;finalize 前每風險至少關聯 1 finding
ar_uc_02 start tab 風險 tab(或判定內嵌) 填標題/描述/等級 start->tab src 勾來源:由哪幾條 not_met finding 組成 (母體 = notMetControls) tab->src save 建立風險 POST /ar/risks src->save link 關聯 finding(全量覆寫) PUT /ar/risk/{riskUid}/findings save->link ok assessment_risks(severity 存 characterizations) + assessment_finding_risks 關聯 雙寫 finding.related_risks 保真 link->ok note finalize 前置: 每風險至少關聯 1 finding (否則 412 GRC_RISK_NO_FINDING_LINKED) ok->note
UC-AR-02 流程:建風險(等級/來源)→ 關聯 not_met findings(全量覆寫)→ 雙寫 finding.related_risks 保真

3. 權限矩陣

專案層角色基調見 _overview §3。本頁(Tab1 風險)檢查點(FE / BE 對齊,phase guard BE 硬擋):

操作 FE 判定 BE 強制 BE 檢查位置
讀 risks 參與者即可(JWT) 讀走 writable=False(finalize 後仍可讀) assessment_result_app_service.py::list_risks
風險寫入(增 / 改 / 刪 / 關聯) canEdit = roundStatus==='auditing'(RoundAuditReviewView.vue:49) _check_auditor + assert_round_phase({auditing}) assessment_result_app_service.py:111-118round_guard.py:31

判定 / 觀察 寫入的權限見 稽核判定工作區 §3;finalize(風險關聯完整性把關)見 稽核執行頁 §3

本頁 FE / BE 權限對齊良好:兩端都以 auditing 階段 + auditor/manager 判定;BE assert_round_phase 硬擋——過階段直接打 API 仍回 412(見 _overview §5 坑 2)。

4. 狀態機與前置條件

輪次狀態機見 專案管理群 _overview §2;本群階段轉換見 _overview §2。本頁相關 gate:

條件 效果 出處
roundStatus === 'auditing' canEdit=true:風險可增 / 改 / 刪 / 關聯 RoundAuditReviewView.vue:49
其餘狀態 canEdit=false:唯讀 banner + 全表單禁用 同上 + Message(:686-688)
BE 寫入 phase guard AR 寫入一律 assert_round_phase({auditing}),過階段回 412 round_guard.py:31
Finalize 前置:風險皆關聯 有風險未關聯 finding → GRC_RISK_NO_FINDING_LINKED(412) assessment_result_app_service.py:809-811

finalize 完整前置(含無 pending 判定)與頁級 gate 見 稽核執行頁 §4

5. UI 設計

Tab [1] 風險 — 系統風險集中管理(not_met finding 歸納成風險) 開啟:UC-AR-02 建立系統風險並關聯判定 建立系統風險 標題 * / 描述 * / 風險陳述 嚴重度(low/medium/high/critical) 存 characterizations JSONB,非獨立欄 狀態(open/investigating/…) 來源(not_met 控制項)多選 = notMetControls(state==='not_met' finding) 依賴判定工作區 refreshStats 重算 POST …/ar/risks → UC-AR-02(點我) 開啟:§6.2 風險類(risk CRUD / risk-findings 關聯) 系統風險清單(風險卡) 標題 / 嚴重度 / 狀態 / 關聯 finding 數 編輯(PUT)/刪除(DELETE,解除連結) 關聯判定(finding)多對多 全量覆寫;雙寫 finding.related_risks 保真 finalize 前每風險至少關聯 1 finding GET/PUT/DELETE …/ar/risk/{uid} PUT …/ar/risk/{uid}/findings → §6.2(點我) 彩色=可點跳本頁小節錨點(coral=UC-AR-02 建立風險/teal=§6.2 風險類 CRUD 關聯/purple=來源 not_met finding)|灰=結構描述 not_met 內嵌風險輸入端見 audit-verdict.md;finalize 完整性檢查見 audit-review.md hub|API 僅列代表性,完整規格見 §6
稽核風險管理版面 wireframe:Tab1 風險建立表單(標題/描述/嚴重度/來源 not_met 控制項)+風險卡清單(編輯/刪除/關聯 finding 全量覆寫);標代表性 API POST/PUT risk / PUT risk/findings

實機截圖(STG,亮色模式,2026-07-05,帳號 blsadmin,專案「亞○ CMMC L1 合規」/ auditing 輪次):

風險 tab:風險標題 / 描述 / 嚴重度 / 來源(不符合控制)建立表單(此輪尚無風險,範例文字未送出)

狀態呈現:風險 per-按鈕 spinner(savingRisk);風險來源多選對應 notMetControls(state==='not_met' 的 finding),依賴 refreshStats 重算——改完 verdict 未刷新前來源清單可能過時。無 socket,風險增改刪 / 關聯後重取風險清單。

6. API 規格

Envelope:成功 {"code": 1, "data": …}、失敗 {"code": 0, "msg": "…"}

6.1 總清單(本頁呼叫的 endpoint)

分類 Method + Path 說明 完整規格
載入 GET /audit-round/{round_uid}/ar/risks 系統風險清單(含關聯 finding) §6.2
風險 POST /audit-round/{round_uid}/ar/risks 建立風險 §6.2
風險 PUT/DELETE /ar/risk/{risk_uid} 編輯 / 刪除風險 §6.2
風險關聯 PUT /ar/risk/{risk_uid}/findings 關聯 finding(全量覆寫) §6.2

判定 / 觀察端點見 稽核判定工作區 §6;finalize / 匯入 / 專案載入端點見 稽核執行頁 §6

以下 §6.2 為本頁核心 endpoint 完整規格(欄位逐條對照 serializer / service 組裝碼,出處標在各段末)。

6.2 風險類

[GET] /audit-round/{round_uid}/ar/risks

Response data.risks[](陣列,每項):

欄位 型別 說明
uid string 風險識別
title string 標題
severity string 嚴重度
description string 描述
status string 狀態
linked_findings array 關聯 finding,每項 {finding_uid, control_id}
linked_finding_count int 關聯 finding 數

[POST] /audit-round/{round_uid}/ar/risks

Request:

欄位 型別 必填 說明
title / description string 風險標題 / 描述
statement string 風險陳述
severity string OneOf low/medium(預設)/high/critical——存 characterizations JSONB
status string OneOf open(預設)/investigating/remediating/closed
remediation string 矯正建議(落 risk.remediations,lifecycle=recommendation)

PUT /ar/risk/{risk_uid}UpdateRiskRequest,全欄選填)/ DELETE(解除 finding 連結)同檔。出處:serializer CreateRiskRequestaudit_round.py:147-162)、service add_riskassessment_result_app_service.py:672-686)。

[PUT] /audit-round/{round_uid}/ar/risk/{risk_uid}/findings

風險 ↔︎ finding 關聯(全量覆寫)。Request:

{
  "finding_uids": ["string"]
}

finding_uids 為 required。雙寫 assessment_finding_risks 關聯表 + finding.related_risks[](OSCAL 匯出保真)。出處:serializer LinkRiskFindingsRequestaudit_round.py:182-188)、service link_risk_findingsassessment_result_app_service.py:763-789)。

7. 前端檔案地圖(compliance-manager-fe/)

檔案 角色
src/views/project/RoundAuditReviewView.vue 主檢視(Tab1 風險 CRUD、關聯 finding、風險來源多選皆在此檔;TabView 容器見 hub §7
src/service/BaseService.js axios 封裝(AR risks / risk-findings 直接呼叫)
src/config/api/api.js 端點常數(AUDIT_ROUND …)
src/config/locales/i18n/{zh-tw,en}/audit-review.jsonround-audit-poam.json 頁面 i18n(namespace 見 hub §12 坑 6

Tab0 判定工作區元件(AuditTreeNav / AuditControlRef / EvidencePreviewDialog)見 稽核判定工作區 §7

8. 後端檔案地圖

鏈路 Route App Service 底層
風險列 / 增 / 改 / 刪 api/project/routes/audit_round_route.py::AuditRoundRisksRoute / AuditRoundRiskRoute add_risk / update_risk / delete_risk assessment_risks(severity 存 characterizations JSONB)
風險關聯 finding 同檔 AuditRoundRiskFindingsRoute link_risk_findings(雙寫 finding.related_risks) assessment_risks + assessment_finding_risks

判定 / 觀察鏈路見 稽核判定工作區 §8;finalize(讀本頁風險做完整性檢查 + 生 POA&M)鏈路見 稽核執行頁 §8

DDD 提醒:風險寫入 API 都照層級走(route 不碰 DB、app service @transaction_check_auditor + assert_round_phase 雙檢查)——與 AP 頁一致的 phase-guard 樣板。

9. DB

9.1 資料表總清單(本頁讀寫的表)

讀/寫 說明 欄位詳述
oscal.assessment_risks 讀/ 系統風險(severity 在 characterizations) §9.3
oscal.assessment_finding_risks 讀/ finding ↔︎ risk 多對多 §9.3
oscal.assessment_findings 關聯來源(not_met finding);雙寫 related_risks 保真 稽核判定工作區 §9.3
oscal.assessment_remediations 矯正建議(response,remediation 欄落此) 見 poam.md

9.2 ER 圖(本頁讀寫範圍)

ar_er cluster_ar AR(本頁寫入) cluster_poam POA&M(finalize 生成) ap oscal.assessment_plans AP(import_ap_id 溯源) arroot oscal.assessment_results AR root(import_ap_id) arroot->ap import_ap_id arres oscal.ar_results 每輪一筆 result arroot->arres 1:N(每輪) find oscal.assessment_findings 逐 AO 判定(target_status_state) arres->find 1:N obs oscal.assessment_observations 觀察(methods / evidence) arres->obs 1:N risk oscal.assessment_risks 系統風險(severity 在 characterizations) arres->risk 1:N find->obs related_observations fr oscal.assessment_finding_risks finding ↔ risk 多對多 find->fr finding_id poam oscal.poams / poam_items not_met → 一控制項一 item find->poam not_met → generate risk->fr risk_id
AR 寫入鏈:AR root(綠)承接 AP;每輪 ar_results 掛 findings / observations / risks;finding↔risk 多對多;not_met → poam_items(橘)

9.3 核心表欄位(取自 DEV DB dump,含 DB comment)

oscal.assessment_risks — 系統風險

assessment-common risk。owner = ar_result_id / poam_id。required=[description, statement, status, title, uuid]。

欄位 型別 說明
uuid uuid 風險身分(= risk_uid)
title / description / statement text 標題 / 描述 / 風險陳述
status varchar(40) open / investigating / remediating / closed
characterizations jsonb 特徵 facets——風險等級(severity)存這裡,非獨立欄
remediations jsonb OSCAL 兜底;抽出真表 assessment_remediations(response,milestone 父層)

oscal.assessment_finding_risks — finding ↔︎ risk 多對多(FR-038 Q2)

稽核員手動建風險、勾由哪幾條 not_met finding 組成;一風險多 finding、一 finding 多風險。

欄位 型別 說明
finding_id bigint → assessment_findings(通常 not-satisfied)
risk_id bigint → assessment_risks

10. 頁面邏輯與資料對應

關鍵欄位對應(畫面 ↔︎ API):

畫面元素 FE state API 欄位
風險列表 risks GET risks data.risks[]
風險嚴重度 editRisk.severity POST/PUT risk severity(存 characterizations)
風險狀態 editRisk.status POST/PUT risk status
風險來源多選 notMetControls(state==='not_met' 的 finding) PUT risk/findings finding_uids
關聯 finding 數 risk.linked_finding_count GET risks linked_finding_count

儲存流程:風險新建走 POST risks、編輯走 PUT risk/{uid}、刪除走 DELETE risk/{uid};關聯 finding 走 PUT risk/{uid}/findings(全量覆寫,雙寫 finding.related_risks)。增改刪 / 關聯後重取風險清單。風險來源多選 notMetControls 依賴 稽核判定工作區refreshStats 重算。

錯誤對應:BE error envelope → toast;本頁常見碼 GRC_NOT_AUDITOR(403)、GRC_ROUND_PHASE_READONLY(412,過階段);finalize 時的 GRC_RISK_NO_FINDING_LINKED(412)見 稽核執行頁 §10。412 由 axios interceptor → event bus → App.vue toast 統一處理。

11. 背景行為與外部依賴

類型 內容
通知 本頁風險增 / 改 / 刪站內通知
Socket 本頁 socket / 輪詢;各動作後手動重取
jedi-* 套件 jedi-oscal-v2(AR risk repo + AssessmentResultService)、jedi-auth(JWT)
系統參數 無 feature flag

12. 邊界情況與已知坑

  1. phase guard BE 硬擋:風險寫入只在 auditing,過階段直接打 API 也回 412(見 _overview §5 坑 2
  2. risk severity 存 JSONBassessment_risks 無 severity 欄,等級在 characterizations——比對 / 查詢 severity 時要看 JSONB,不是列舉欄位(_overview §5 坑 5
  3. finding ↔︎ risk 多對多、全量覆寫PUT risk/{uid}/findings 是全量覆寫(非增量),漏帶等於解除既有關聯;雙寫 assessment_finding_risks 關聯表 + finding.related_risks[](OSCAL 匯出保真)
  4. finalize 前置:每風險至少 1 finding:有風險未關聯 finding → finalize 回 GRC_RISK_NO_FINDING_LINKED(412);改風險判定時 notMetControls 依賴 稽核判定工作區refreshStats 重算,改完 verdict 未刷新前來源清單可能過時
  5. 內嵌風險 = 同一批資料:Tab0 判定右欄的 not_met 內嵌風險與 Tab1 風險列表是同 assessment_risks,兩處編輯任一都影響另一(見 稽核判定工作區 §12
  6. engagement 共用 AR:覆核輪不另開 AR root,append 一筆 ar_results,風險亦掛在該輪 result(_overview §5 坑 7

13. 開發與驗證

  • 跑起來:BE python main_socketio.py(port 8000,log 在 log/app.log);FE 在 compliance-manager-fe/ 起 Vite dev server。BE 改 service code 後必須重啟(無 hot reload)
  • 測試帳號:dev 環境稽核員角色帳號(密碼見 .env / 部署文件)
  • 導航路徑:登入 → 專案 → 進入 auditing 狀態的輪次 → 稽核執行 → Tab「風險」
  • 前置資料:需先在 Tab0 判定出至少一條 not_met finding,才有可關聯的來源
  • E2Ecompliance-manager-test/ repo(Cucumber + Playwright);以 稽核執行 / audit-review / 風險 / risk 搜尋
  • 相關文件:上層導航 稽核執行頁稽核判定工作區(Tab0 判定 / not_met 內嵌風險輸入端);FR-038(v2 遷移 B5 AR,Q2 finding↔︎risk 多對多);DB / API 全量見 GAI-SD-02/03