AO 任務配置與啟動(Task Config)
功能群:專案管理|上層見 專案規劃頁(本頁=其 Tab0「控制項實作」的 AO 任務配置,規劃頁功能總覽第 4/5/6 項)
事實基準:2026-07-06 從 FE / BE code 掃出;表結構對
db_schema.json、endpoint 對routes.json、欄位對 marshmallow serializer + service 組裝碼逐條核變更紀錄:
- 2026-08-01(FR-059):profile 下拉動態化——CINC Auditor / GCB 的
profile欄位改由「掃描設定檔庫」動態餵選項:param_schema 升 v2 加"options_source": "profile_library"宣告並拿掉靜態 options,FE 偵測宣告改呼 profile menu API(帶工具 uid 過濾,GCB 池與 CINC 池分開);型別維持select_or_text、手填能力保留。取值來源三層並存:庫內檔案型(profile:<uid>參照)/ 庫內連結型 / 現場手填(既有任務存的舊靜態選項值照常可用)。設定檔的上傳與維護見新頁 掃描設定檔管理- 2026-08-01(FR-058.5/.6/.7):檢測工具下拉新增 SonarQube(原始碼安全檢測 SAST),三種取源模式以
scan_mode參數分流——pull讀取既有結果/scan掃描公開 Git repo/upload上傳源碼壓縮包(各自參數與適用情境見 §5「SonarQube 的掃描參數與三種執行模式」);scan/upload為寫入行為(會在客戶 SonarQube 建立/更新Guidant-AI-前綴專案),任務卡加橘色警語區塊事前告知(D24 配套);upload 模式的壓縮包不在本頁上傳(於任務抽屜發起執行時提供,D26);upload 任務不進「開始執行任務」自動派發(D33,批次發佈 confirm 文案加註提示);condition.value語法擴充支援陣列形式(一欄可同時綁多個模式值)。另 GCB 的profile下拉由 1 筆示範擴為 8 支 TWGCB 作業系統類基準(fr058-18;後續已由 FR-059 改為設定檔庫動態餵)- 2026-07-31(FR-058):檢測工具下拉新增四款(ZAP / CINC Auditor / Nmap / 政府組態基準 GCB),各自的掃描參數見 §5「四款新工具的掃描參數」;任務層敏感參數加密(
param_schema的secret:true欄位如 ZAP 登入密碼,DB 存密文、API response 整個 key 剝除、FE 靠secret_keys_set顯示「已設定」);掃描參數必填驗證(原本完全不驗、缺參數的任務照樣存檔到 agent 執行期才失敗,改為存檔前擋下且 condition-aware);CINC Auditor 的profile參數由text改select_or_text- 2026-07-29(FR-057):檢測工具任務新增
select_or_text掃描參數型態(下拉常用選項 + 自由輸入,OpenSCAPprofile欄位用);選定工具後有default值的參數欄位自動帶入預設值(onSelectDetectionTool新行為,見 §5 / §10)- 2026-07-27(FR-056):任務類型新增
detection_tool(檢測工具執行)——選定工具後動態渲染掃描參數 + 完成模式(manual/auto);「開始執行任務」批次動作新增自動派 detection 任務首次掃描(見 §6.3)- 2026-07-06 FR-047 從 project-planning.md 拆出並納入快速配置 / 開始執行
1. 功能描述
AO 任務(prep-job)是「為每個控制項評估目標(AO)蒐集證據」的工作項。本頁涵蓋任務域的三件事:①單一任務逐欄配置(inline 編輯)、②快速配置(一鍵把多位人員批次併入所有可編輯任務)、③開始執行(PM 把已指派任務批次轉 PROCESSING 並通知)。
規劃頁 Tab0 的左樹選到某個 AO 後,右面板下方列出該 AO
的任務卡;每張卡片直接 inline 編輯(非獨立
Dialog):任務名稱 / 描述 / 指南、任務類型(一般 general /
問卷
survey)、指派人員(可標審核人)、部門、設備、問卷(可標審批要求)。儲存以
PUT job 一次寫回,關聯欄位採
full-replace 語意(送 [] 清空、省略 =
不動)。快速配置與開始執行是 Header
的批次動作:前者 FE 端逐一 PUT 合併、後者呼叫專屬
start-task-execution endpoint。
任務真相來自範本 BPMN 的
userTask(見規劃頁背景);儲存時 BE 會在同一 transaction
同步回寫 BPMN userTask,讓 BPMN 編輯器與規劃頁兩端一致。與 SSP
編輯不同,任務指派不受 SSP 凍結影響,只要輪次在
planning 階段、使用者對該節點有三層遞進的 manager
權即可編。
主要使用者:對該控制節點有 manager 權者(專案 / 群組 / 控制項任一層 manager)。
角色速覽(完整定義見 _overview §3):
| 角色 | 一句話 |
|---|---|
| manager(三層任一) | 專案 / 群組 / 控制項 manager —
可編此節點下的任務(canEdit 遞進,見規劃頁 §3) |
| 參與者 | auditor / reviewer / viewer — 可讀不可寫 |
1.1 功能總覽(本頁全部功能)
| # | 功能 | 說明 | 位置 | 詳述 |
|---|---|---|---|---|
| 1 | 編輯任務基本文案 | 名稱 / 描述 / 指南 + 類型(general / survey) | 任務卡 inline | UC-TE-01 |
| 2 | 指派人員(含審核人) | MultiSelect 指派;survey
型可標審核人(is_approver) |
任務卡 | UC-TE-01 |
| 3 | 部門 / 設備關聯 | MultiSelect 綁部門(org_unit)/ 設備(device) | 任務卡 | UC-TE-01 |
| 4 | 問卷關聯(含審批) | 僅 survey 型;MultiSelect 選問卷,每張可標「審批要求」 | 任務卡 | UC-TE-01 / §12 |
| 5 | 儲存任務 | 一次 PUT,關聯 full-replace,同步 BPMN | 每卡「儲存」 | §6.2 |
| 6 | 快速配置 | 多選人員一鍵 additive 併入所有可編輯 AO 的任務(FE 逐一 PUT) | Header 按鈕 → dialog | UC-PP-02 / §12 |
| 7 | 開始執行任務 | PM 把「已指派且 TODO」的任務批次轉 PROCESSING
並通知(idempotent);FR-056:detection_tool
型任務一併自動派第一次掃描(scan_mode=upload
者除外——自動路徑不帶檔案,D33,見 §6.3) |
Header 按鈕(限 PM) | UC-PP-02 / §6.3 |
| 8 | 檢測工具綁定與參數(FR-056) | 任務類型選「檢測工具執行」後:選定工具(config.detection_tools)+
依工具 param_schema 動態渲染掃描參數 +
完成模式(manual/auto);FR-058:secret 參數加密存放 +
存檔前必填驗證 |
任務卡(jobType==='detection_tool' 條件層) |
§5 / §6.2 |
UC(§2)展開兩條核心路徑:UC-TE-01(單一任務逐欄 inline 編輯,含 M2M / 審核人坑)與 UC-PP-02(快速配置 → 開始執行的批次流)。快速配置 ⚠️ 有清空關聯的坑(見 §12)。
2. Use Case
角色速覽(同 §1)。流程圖視覺慣例:菱形 = 判斷、橘底 = 例外/擋下、紅框 = 錯誤(標 error code)、綠框 = 成功終點、虛線 = 可選路徑。
UC-TE-01 編輯 AO 任務
| 項目 | 內容 |
|---|---|
| 角色 | 對該節點有 manager 權者 |
| 前置條件 | 輪次 AP=planning;已載入
control-tree(任務內嵌於樹);canEdit 為真 |
| 產出 / 後置條件 | 寫 job_executions(本體)+ reconcile 四關聯表(指派 /
部門 / 設備 / 問卷);同步 BPMN userTask;名稱變更 best-effort 觸發
Drive 改名 |
UC-PP-02 快速配置與開始執行
| 項目 | 內容 |
|---|---|
| 角色 | 快速配置:有編輯權者;開始執行:專案 manager |
| 前置條件 | 輪次 = planning;AO 下已有任務(BPMN 範本衍生的 prep-job) |
| 產出 / 後置條件 | 快速配置:對每個可編輯任務 additive 併入指派人員後逐一
PUT job;開始執行:compliance.task_assignees
等已備妥後 job_executions.status
TODO→PROCESSING、站內通知送出 |
3. 權限矩陣
| 操作 | FE 判定 | BE 強制 | BE 檢查位置 |
|---|---|---|---|
| 讀任務 | 參與者即可(隨 control-tree 一起載) | require_participant(讀樹) |
規劃頁 control-tree 路徑 |
| 編輯 / 儲存任務 | 三層遞進 canEdit(專案 → 群組 → 控制項
manager,ProjectPlanningView.vue:663-679) |
⚠️ 無角色守門(route 僅
@jwt_required()) |
api/grc/routes/job_route.py:98(route);service
app/grc/service/job_service.py |
| 快速配置 | 同編輯(逐一 PUT job) | 同上(每筆 PUT 無角色守門) | FE
executeQuickConfig(ProjectPlanningView.vue:1192) |
| 開始執行任務 | isProjectManager 才顯示按鈕 |
_check_manager(須專案 manager,否則
GRC_403002) |
app/grc/service/task_execution_service.py(_check_manager:8-17) |
⚠️ FE / BE 權限不對齊:FE 用三層遞進的
canEdit擋按鈕,但PUT jobroute 層只有 JWT 登入、無任何角色檢查——任一登入者繞過 FE 直接打 API 都能改任務指派。與規劃頁 §3 / §12 記載的同一 finding。唯有「開始執行」有 BE manager 守門(GRC_403002)。
4. 狀態機與前置條件
承接輪次階段 gate(見 規劃頁 §4):
| 條件 | 效果 | 出處 |
|---|---|---|
apStatus === 'planning' |
任務可編(不受 SSP 編輯凍結影響) | 規劃頁 FE 行為 |
!canEdit(非三層 manager) |
任務卡全部欄位 disabled | ProjectPlanningView.vue(canEdit 綁
:disabled,約 :1953-2029) |
類型 = survey |
才顯示問卷 MultiSelect + 審批 / 審核人 switch | 同檔(jobType==='survey' gate,約 :1985/:2015) |
5. UI 設計
版面骨架(Header 批次動作 + AO 任務卡 inline 編輯,含 survey 型條件層 + 代表性 API):
📸 截圖待補(亮色模式):AO 面板 + 任務卡展開(general 型)
taskedit-general.png、survey 型(含問卷 + 審批 + 審核人 switch)taskedit-survey.png。(規劃頁 §5 已有planning-ao-tasks.png概覽可參考)
任務卡 inline 表單(ProjectPlanningView.vue Panel
卡片,無獨立 Dialog)。欄位:
| # | i18n label | 型別 | 必填 | 列舉 / 來源 | payload 欄位 | disabled | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | task_name_label 任務名稱 |
InputText | 否 | — | name |
!canEdit |
|
| 2 | task_desc_label 描述 |
Textarea(autoResize) | 否 | — | description |
!canEdit |
|
| 3 | task_guide_label 指南 |
Textarea(autoResize) | 否 | — | guide |
!canEdit |
|
| 4 | 任務類型 | SelectButton | 否 | general / survey |
job_type |
!canEdit |
切 survey 才顯示問卷區 |
| 5 | assignees_label 指派人員 |
MultiSelect(chip) | 否 | /users/menu({id,name}) |
assignees[{uid,is_approver}] |
!canEdit |
dataKey=id |
| 5a | is_approver_label 審核人 |
InputSwitch(每位人員) | 否 | — | assignees[].is_approver |
!canEdit |
僅 survey 型且有問卷開審批時顯示 |
| 6 | departments_label 部門 |
MultiSelect | 否 | /org-units/menu({id,name}) |
department_uids[] |
!canEdit |
|
| 7 | devices_label 設備 |
MultiSelect | 否 | POST /devices({id,name}) |
device_uids[] |
!canEdit |
|
| 8 | surveys_label 問卷 |
MultiSelect | 否 | /surveys/menu({id,name}) |
surveys[{uid,approval_required}] |
!canEdit |
僅 survey 型顯示 |
| 8a | approval_required_label 審批 |
InputSwitch(每張問卷) | 否 | — | surveys[].approval_required |
!canEdit |
與問卷名並列 |
| 9 | 檢測工具(FR-056) | Dropdown | 是(detection_tool 型必選) |
工具目錄選單(menuStore.detectionToolMenu,來源
GET /detection-tools) |
tool.uid |
!canEdit |
僅 job_type==='detection_tool'
顯示;未選不可儲存(toast warn_tool_required) |
| 9a | 掃描參數 | 依 param_schema
動態渲染(DetectionConfigField,型態含
text/number/select/select_or_text/password) |
依欄位定義,FR-058 起存檔前實際驗證 | GET /detection-tools/{uid}/param-schema |
tool.params(Raw,不硬編欄位) |
!canEdit |
選定工具後才出現;選定工具當下若欄位有 default
值會自動帶入(onSelectDetectionTool,FR-057,見
§10),非空表單;各工具參數見下方「四款新工具的掃描參數」與 檢測工具管理 §6.2 |
| 9b | 完成模式 | SelectButton | 否(預設 manual) |
manual / auto |
tool.completion_mode |
!canEdit |
manual=掃完留 PROCESSING 等人工;auto=掃完自動完成 |
select_or_text型態(FR-057 新增):PrimeVue editable Dropdown——下拉列常用選項,也可直接輸入清單外的自由字串。用於各工具的profile欄位(OpenSCAP 的 xccdf profile id 會因目標主機自帶的 SSG 版本不同而有自訂值;CINC Auditor / GCB 的 profile 可以是 Git 網址、Agent 主機上的本機路徑或 Supermarket 名稱)。FR-058 把 CINC Auditor 的profile由text改為select_or_text(FE 元件早已支援,FE 與 agent 零改動)。渲染元件與檢測工具管理 §5 設定表單共用同一DetectionConfigField.vue。profile 下拉動態化(FR-059):欄位定義帶
"options_source": "profile_library"宣告時(CINC Auditor / GCB 的profile欄位,param_schema v2 起),DetectionConfigField不再讀欄位內的靜態options陣列,改呼GET /detection-tool-profiles/menu?detection_tool_uid=(帶當前工具 uid——GCB 抽屜只見 gcb 池、CINC 只見 inspec 池)動態組選項;選項的value是 BE 組好的profile:<uid>庫內參照,派工時展開。選項載入走src/composables/useDetectionProfileOptions.js的模組層級快取(規劃頁 N 個任務列共用、同工具只打一次 API);menu 拉不到時顯示警示但欄位照常可手填(不擋畫面)。取值來源三層並存:庫內檔案型 / 庫內連結型 / 現場手填(URL、Agent 本機路徑、Supermarket 名稱——不進庫、單次執行用;既有任務存的舊靜態選項值屬手填路徑,升級後照常可用)。回顯相容:值不在選項內時 editable Dropdown 原樣顯示該字串;已停用的profile:<uid>參照補一個唯讀 fallback 選項標示「庫裡已沒有的項目」,不露裸 UUID(DetectionConfigField.vue::profileOptions:82-92)。沒有宣告的欄位維持靜態 options 行為零影響;機制 tool-agnostic,新工具 seed 宣告即接庫、FE 零改。設定檔的維護見 掃描設定檔管理。
四款新工具的掃描參數(FR-058,DEV
實查 config.detection_tool_param_schemas 當前生效版)
| 工具(code) | 參數欄位 |
|---|---|
ZAP(zap) |
target_url(text,必填)/scan_mode(select,必填,預設
passive:被動掃描 / 主動掃描 /
登入後主動掃描)/login_url・login_username(text,必填,condition: scan_mode=authenticated)/login_password(password,secret: true,必填,同
condition)/logged_in_indicator(text,必填,同
condition)/timeout_sec(number,預設 3600) |
CINC Auditor(inspec) |
hosts(text,必填,多台逗號分隔、逐台各產一份報告、單台失敗不影響其餘)/transport(select,必填,預設
ssh:SSH(Linux 目標)/WinRM(Windows
目標))/profile(select_or_text,必填,FR-059
起帶 options_source: "profile_library"
宣告、選項改由掃描設定檔庫動態餵——原靜態預置的 dev-sec
Linux/Windows
基準已搬入庫成公用版連結型,下拉內容零回歸)/timeout_sec(number,預設
3600,逐台各自計時非總時間) |
Nmap(nmap) |
hosts(text,必填,「執行主機」=Agent
登入並執行 nmap
的機器)/targets(text,必填,「掃描目標」=IP/網段/主機名稱)/scan_type(select,必填,預設
connect:TCP 連線掃描(不需提權)/SYN 掃描(較快,需
sudo)/服務版本偵測(探測流量較多,需確認已獲授權))/port_range(text,預設
1-1024)/timeout_sec(number,預設
3600) |
GCB(gcb) |
與 CINC Auditor
同構(hosts/transport/profile/timeout_sec)。profile
同樣帶 options_source 宣告(FR-059)——下拉列掃描設定檔庫
gcb 池內容:8 支 TWGCB 公用版(Windows Server 2016/2019/2022、Windows
11、RHEL 8/9、Ubuntu 22.04 等,原 FR-058
靜態容器路徑選項搬遷而來)+租戶自行上傳的自有版 |
Nmap 的
hosts≠targets:其餘工具「登進去掃自己」,hosts同時是登入對象與掃描對象;Nmap 是 Agent 以 SSH 登入hosts這台執行主機,再由該主機對targets(可能是整個網段)送出探測封包。兩個欄位語意不同,任務執行抽屜 的摘要卡與執行詳情視窗因此分開顯示兩者。ZAP
scan_mode='authenticated'對單頁式應用(SPA)不可使用:不只是掃不到——會引發登入無限重試,累積到 ZAP 保護門檻後 daemon 主動停止,連帶中斷其他租戶正在執行的掃描。詳見 檢測工具管理 §12 坑 14;ZAP 卡片的部署前提說明已明白標示。
profile下拉一律用 tarball 形式(.../archive/refs/heads/master.tar.gz)而非裸 GitHub 網址:裸網址走 CINC 的 git fetcher,需 shell out 呼叫git查 default branch;實測把 agent 容器內的git移走後裸網址直接失敗、tarball 照常可用。下拉是平台預置的建議值,選少一個故障點的形式。GCB 的
profile選項演進:fr058-13 上架時僅 1 筆 Windows 最小示範(2 項);fr058-14 加入 TWGCB-01-011 Server 2022 完整基準後,fr058-16 把示範筆移出下拉(select_or_text手填仍可用);fr058-18 擴為 8 支 TWGCB 作業系統類基準——Windows 組 4 支(007 Server 2016/009 Server 2019/010 Windows 11/011 Server 2022,各 540/539/108/558 項自動檢查)+ Linux 組 4 支(008 RHEL 8/012 RHEL 9/013 RHEL 9/014 Ubuntu 22.04 LTS,各 53/74/60/35 項)。排序先 Windows 後 Linux、組內照編號;012 與 013 是 NICS 官方兩份不同的 RHEL 9 基準(非重複),label 一律帶 TWGCB 編號區分。來源集合共 23 支,其餘 14 支(macOS 15、browser/application/network/cloud 類)可自動檢查項數皆 0 或標的形態接不上 SSH/WinRM transport,不上架。2026-08-01 起選項來源再改為掃描設定檔庫動態餵(FR-059options_source,見變更紀錄),上述 8 支已搬遷為庫內 SYSTEM 公版。
SonarQube 的掃描參數與三種執行模式(FR-058.5/.6/.7,DEV 實查 param_schema v4 當前生效版)
SonarQube
是平台第一個拉取快照(pull-snapshot)與平台發動掃描並存的工具,以
scan_mode 參數在同一張工具卡內分流三種取源模式(D17 ZAP
式單卡多模式;憑證兩模式共用同一組 base_url+User
Token):
模式(scan_mode) |
誰發動掃描 | 源碼來源 | 對客戶 SonarQube | 適用情境 |
|---|---|---|---|---|
scan 原始碼掃描(預設,D24) |
平台(Agent 執行 sonar-scanner) | 公開 Git repo(D19) | 寫入——推分析結果,專案不存在自動建立 | 沒有 CI 整合、要掃「稽核當下」的源碼狀態 |
upload 上傳原始碼壓縮包 |
平台(同上) | 使用者上傳的壓縮包(FR-058.7) | 寫入(同上) | 源碼在私有 repo 或不在 git(委外交付源碼包、特定版本快照)——客戶打包上傳即可,不必把 git 憑證交給平台 |
pull 讀取既有結果 |
客戶自己的 CI / 開發者機器 | 平台不碰源碼 | 唯讀——只取回最近一次分析結果 | 已有成熟 CI、SonarQube 天天在跑,只要把結果收進證據池 |
各模式的參數欄位(condition 綁
scan_mode,不可見欄位不必填也不送出):
| key | 型別 | 顯示條件 | 說明 |
|---|---|---|---|
scan_mode |
select・必填・預設 scan |
永遠顯示 | 三選項如上表;hint 註明「上傳模式的檔案於發起執行時提供,設定頁不需填寫」 |
repo_url |
text・必填 | 僅 scan |
公開 Git repo 的 HTTPS 網址;不支援私有 repo
認證(要掃私有源碼改走 upload) |
project_key_suffix |
text・必填 | scan 與
upload(condition.value 陣列形式
["scan","upload"],FR-058.7 語法擴充) |
只填後半段識別字;實際推上客戶 server 的專案代碼是
Guidant-AI-<此欄>,前綴由平台組上(D21——防撞號、可辨識來源) |
project_key |
text・必填 | 僅 pull |
客戶 SonarQube 上的既有專案代碼 |
branch |
text・選填 | 僅 pull |
多分支為付費版(Developer Edition 起)功能;Community Build 留空即可,填了而 server 不支援回明確錯誤(D14) |
三個必知限制與配套:
scan/upload是寫入行為且沒有 dry-run——每次掃描都把結果推上客戶 SonarQube,專案不存在會自動建立。FE 於scan_mode為寫入模式時(含舊 v1 任務scan_mode未存值、實際生效預設scan的情況)在參數區塊顯示橘色警語(D24 配套①,ProjectPlanningView.vue::isWritingScanMode);setup_guide另載收緊做法(預先建好專案+移除 token 帳號的 Create Projects 全域權限)。- 支援語言僅七種 A
類純源碼語言(Python/JS/TS/Go/PHP/Ruby/Kotlin,D18)——不需編譯即可分析。C#/VB.NET
永遠只能走
pull:這不是取捨而是架構限制——.NET 的分析發生在編譯器內部(Roslyn analyzer 隨 MSBuild 編譯執行),SonarScanner CLI 明文不支援,且送進去不會報錯、只會靜默淺掃(產出一份看似正常實際幾乎沒分析到的報告),故setup_guide以醒目警語要求改用pull。Java/C/C++ 目前亦不支援。 upload的壓縮包不在本頁提供(D26)——任務設定只選模式;檔案於任務抽屜發起執行時上傳(壓縮包是「這一次要掃的源碼快照」,不是「任務怎麼掃」的設定),已上傳的檔案 sticky 保留供重掃沿用(D29)。格式與限額見任務抽屜頁 §11.1。
出處:param_schema v4 seed
scripts/sql/2026-08-01-fr058-21-sonarqube-upload-scan-mode.sql(v1→v4
演進:2026-07-31-fr058-17(pull 兩欄)→
fr058-19(scan 模式+setup_guide 改寫)→
2026-08-01-fr058-20(標籤精簡)→
fr058-21(upload 選項));FE 警語
ProjectPlanningView.vue:1008-1023;condition.value
陣列支援
src/utils/detectionFieldValidation.js+DetectionConfigField.vue(純擴充,字串形式行為不變)。
任務層敏感參數加密(FR-058.0)
param_schema 的 secret: true 欄位(現況只有
ZAP 的
login_password)屬某一次掃描任務的機密,不是工具連線設定,只能落在
config.job_execution_detection_tools.tool_params(JSONB,與非機敏欄位混雜)。租戶層那套「整欄加密」無法直接套用,故另做「同一個
JSONB
內挑欄位加密」的機制(common/util/detection_secret_params.py):
| 環節 | 行為 |
|---|---|
| 寫入 | job_service._encrypt_tool_secret_params() 把 secret
欄位包成自我描述 envelope
{"__enc__": "fernet", "value": "<密文>"};欄位留空時從既有綁定沿用舊密文(否則使用者改任何其他參數都會清空密碼,且症狀要到掃描時才出現) |
| 讀取(API response) | JobToolResponseSchema.params 整個 key
不出現(非遮罩成 ****);另回
secret_keys_set(已設定值的 secret 欄位 key 名清單,只回
key 不回值) |
| FE 顯示 | DetectionConfigField 以
hasExistingValue(secret_keys_set.includes(field.key))顯示「已設定,留空=沿用原值」,而非空白必填紅框——逼使用者重打密碼才是真正的洩漏誘因 |
| 派工 | decrypt_secret_params() 還原明文下發給 agent;agent
用完即丟。解密失敗(金鑰輪替、資料損毀)剝除該 key
而非落密文——送密文過去 agent
會拿它當密碼登入,症狀是「掃描跑完但沒進到登入後頁面」,比缺欄位更難查 |
| 稽核 log | 同樣走 strip_secret_params() 剝除 |
為何用 envelope 而非裸密文:密文存進 JSONB 後與明文同為 string,無法從值本身判斷是否已加密。若靠「查 schema 哪些 key 是 secret」決定要不要解密,正確性就綁在 schema 上——而
param_schema是版本化的(is_current切換),改版後舊任務會解錯。改存 envelope 後值本身就說明自己加密與否,schema 只用來決定「新寫入的值要不要加密」,不參與解密判定。換工具時 FE 會一併清空toolSecretKeysSet(否則新工具的同名 secret 欄位會誤顯示「已設定」,使用者不填就存 → 存進空密碼)。
掃描參數必填驗證(FR-058.2)
原本 FE 對掃描參數完全不驗,缺參數的任務照樣存檔 →
派工 → 到 agent 執行期才失敗(ZAP 缺 logged_in_indicator
實測踩過兩次)。改為 saveTask 前檢查,缺漏時 toast
列出欄位名稱並不送出。判斷是
condition-aware 的:因 condition
不成立而隱藏的欄位不算必填(被動掃描時 ZAP
的登入四欄不該被誤擋);secret 欄位已設定過(在
toolSecretKeysSet 內)留空=沿用原值、不算缺。與
DetectionConfigField 的 visible /
showError 同源——三處都呼叫
src/utils/detectionFieldValidation.js 的
isDetectionFieldVisible() /
findMissingRequiredDetectionFields(),不各判一套。出處:ProjectPlanningView.vue::saveTask:1037-1057。
FE 別名坑:指派人員的審核旗標,FE 早期送
is_admin,serializerAssigneeInputSchema以@post_loadcoalesce 進is_approver(api/grc/serializers/job.py:105-114)。新資料一律is_approver。
狀態呈現:無 socket;儲存中 per-任務
spinner(taskSavingSet Set);儲存後前端 state
更新(不整頁重載)。
6. API 規格
Envelope:成功 {"status": true, "data": …}、失敗
{"error_code": "…", "msg": "…"}。
6.1 總清單
| 分類 | Method + Path | 說明 | 完整規格 |
|---|---|---|---|
| 任務 | PUT /grc/project/{project_uid}/job/{job_uid} |
任務更新(關聯 full-replace) | §6.2 |
| picker | GET
/users/menu・/org-units/menu・/surveys/menu、POST
/devices |
指派 / 部門 / 設備 / 問卷下拉 | GAI-SD-02 |
批次啟動
POST /grc/project/{uid}/start-task-execution屬規劃頁功能 6,見 規劃頁 §6.4。
6.2 核心 endpoint
[PUT] /grc/project/{project_uid}/job/{job_uid}
任務更新。全部欄位選填(load_default=None);關聯陣列
full-replace(送 [] 清空、null/省略 =
不動)。Request(JobUpdateRequestSchema,Meta.unknown=EXCLUDE):
{
"name": "訪談紀錄蒐集",
"description": "…",
"guide": "…",
"job_type": "survey",
"control_uid": null,
"assignees": [{ "uid": "<user uid>", "is_approver": true }],
"department_uids": ["<org_unit uid>"],
"device_uids": ["<device uid>"],
"surveys": [{ "uid": "<survey uid>", "approval_required": true }],
"tool": { "uid": "<detection_tool uid>", "params": {"hosts": "<target host>"}, "completion_mode": "manual" }
}| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
| name / description / guide | string|null | 否 | 任務文案 → job_executions |
| job_type | string|null | 否 | OneOf GrcJobType(general /
survey /
detection_tool) |
| control_uid | string|null | 否 | 控制層 workflow_execution.uid;可省略,BE 由 job 的 workflow 階層自動解析 |
| assignees | array|null | 否 | [{uid(必填), is_approver(預設 false;相容 is_admin 別名)}]
→ task_assignees |
| department_uids | array[string]|null | 否 | 部門 org_unit uid → job_execution_org_units |
| device_uids | array[string]|null | 否 | 設備 uid → job_execution_devices |
| surveys | array|null | 否 | [{uid(必填), approval_required(預設 null)}] →
survey.task_surveys;送 null
保留既有審批旗標(BPMN
編輯器同步不誤蓋,SurveyInputSchema:117-125) |
| tool | object|null | 否(detection_tool 型必填才可正常掃描) |
{uid(必填,detection_tools uid), params(Raw,依 param_schema), completion_mode("auto"|"manual",未帶=partial-update 不動既有值,新建綁定時 app service 補預設 "manual")}
→
config.job_execution_detection_tools(DetectionToolBindingSchema:138-153) |
出處:request api/grc/serializers/job.py:128-153(含
DetectionToolBindingSchema:138-153)、route
api/grc/routes/job_route.py:100-136、service
app/grc/service/job_service.py(update_job +
_reconcile_task_surveys:228 +
_replace_detection_tool_binding)
Response data(JobResponseSchema,形狀同
control-tree 的 Job 節點):
| 欄位 | 型別 | 說明 |
|---|---|---|
| id / name / description / guide | string | 任務基本資料(id=job uid) |
| job_type / status | string | 類型 / 狀態 |
| template_job_id | string|null | 範本任務來源 |
| can_revert_job | bool | 可否退回 |
| assignees / departments / devices / surveys | array | 關聯子物件(形狀同 control-tree Job 節點) |
| tool | object|null | detection_tool
型才有值:{uid, code, name, params, completion_mode, secret_keys_set}(JobToolResponseSchema,供
FE 讀取端 prefill)。params 已剝除所有加密 envelope
欄位(整個 key
不出現);secret_keys_set 是已設定值的
secret 欄位 key 名清單(只回 key 不回值),供 FE
顯示「已設定,留空=沿用原值」——剝除後 FE
無從分辨「沒設過」與「設過但看不到」,會把已設定的必填欄位標成缺漏(FR-058.0
T-0.3) |
出處:api/grc/serializers/job.py:71-98(JobToolResponseSchema,含
secret_keys_set)/:86-(JobResponseSchema);加解密與剝除
util common/util/detection_secret_params.py;寫入端
app/grc/service/job_service.py::_encrypt_tool_secret_params:338-370
6.3 批次動作
快速配置(無專屬 endpoint,FE 逐一 PUT)
FE-only 編排:多選人員(來源 = 專案參與人員)→
迴圈所有可編輯(canEditCtrl)控制項底下的任務
→ 每個 job 把選取人員 additive
併入既有指派(已存在者跳過)→ 逐一
PUT job。⚠️ 每筆 PUT 送
department_uids: [] / device_uids: [] /
surveys: []——因 full-replace
語意,會清空該任務既有的部門 / 設備 / 問卷關聯(見 §12
坑)。出處:ProjectPlanningView.vue:executeQuickConfig:1192-1230。
[POST] /grc/project/{project_uid}/start-task-execution
無 body。把該專案「已指派且 TODO」的 prep-job 批次轉
PROCESSING 並發站內通知。限專案
manager(_check_manager,否則
GRC_403002);idempotent 可重按。Response
data:{"activated_count": int, "detection_dispatched_count": int}(分別為實際轉
PROCESSING 的任務數、自動派出首次掃描的 detection_tool 任務數)。
FR-056:自動派 detection_tool 首次掃描——批次轉
PROCESSING 的同時,對其中 job_type='detection_tool'
且查無任何 detection_executions
紀錄(NOT EXISTS,冪等防重)的任務逐筆呼叫
DetectionOrchestrationService.start_execution()
派出第一次掃描;單筆派工失敗(如未設定憑證)用 SAVEPOINT
隔離、不擋整批發佈與其餘任務,失敗的任務留給執行者從 任務執行抽屜
手動補派。出處:service
app/grc/service/task_execution_service.py(_check_manager:49-56、start_task_execution:58-96、_auto_dispatch_detection_scans:98-134)、query
infra/grc/repository/task_execution_query.py::get_detection_jobs_without_execution:134-、serializer
api/grc/serializers/task_execution.py。
scan_mode=upload的任務不進自動派發(FR-058.7,D33):自動路徑不帶檔案,upload 任務必然打中「首次無檔」400(DETECTION_TOOLS_400005),且該錯誤被批次派發的 try/except 吞掉只留 warning log——PM 看到成功 toast、執行者無感,又因失敗不產生執行紀錄、冪等條件永遠成立,每按一次「開始執行任務」就再失敗一次。故在挑選階段排除:get_detection_jobs_without_execution的查詢排除tool_params->>'scan_mode'='upload'的綁定(task_execution_query.py:142/:169),upload 任務一律由執行者在任務抽屜上傳檔案後手動執行(抽屜有無檔不給按執行的前置擋門);pull/scan 自動派發行為不變。FE 配套:批次發佈 confirm 文案加註「上傳型掃描任務需由執行者上傳檔案後手動執行」(i18nstart_task_execution_confirm_message)。已執行過的 upload 任務本來就不在冪等清單內,不受影響。
7. 前端檔案地圖(compliance-manager-fe/)
| 檔案 | 角色 |
|---|---|
src/views/project/ProjectPlanningView.vue |
任務卡 inline 編輯(Panel 卡片、四關聯 MultiSelect、審核人 / 審批
switch、saveTask
PUT);檢測工具區(onSelectDetectionTool 帶 default + 清舊
secret 狀態、detectionParamSchemaCache 依工具快取
schema、FR-058 掃描參數必填驗證) |
src/components/detection-tools/DetectionConfigField.vue |
掃描參數的動態欄位渲染,與檢測工具管理設定表單共用同一元件(hasExistingValue
在本頁綁 toolSecretKeysSet,在該頁綁
config.has_credentials) |
src/utils/detectionFieldValidation.js |
(FR-058)isDetectionFieldVisible() /
findMissingRequiredDetectionFields()——condition
可見性與必填檢查的共用實作,本頁
saveTask、DetectionConfigField、檢測工具管理頁三處同源 |
src/service/BaseService.js |
put(...) 打
/grc/project/{uid}/job/{jobId} |
8. 後端檔案地圖(本頁核心鏈路)
| 鏈路 | Route | App Service | 底層 |
|---|---|---|---|
| 任務更新 | api/grc/routes/job_route.py(PUT,僅 JWT) |
JobService.update_job(full-replace reconcile + BPMN
同步,同一 transaction) |
compliance.job_executions + reconcile
task_assignees / job_execution_org_units /
job_execution_devices +
survey.task_surveys(_reconcile_task_surveys)+
config.job_execution_detection_tools(_replace_detection_tool_binding,FR-056) |
| 開始執行任務(含自動派掃描) | api/grc/routes/(start-task-execution) |
app/grc/service/task_execution_service.py::start_task_execution(_check_manager
→ activate_jobs → 通知 →
_auto_dispatch_detection_scans) |
job_executions(狀態轉換)+
DetectionOrchestrationService.start_execution()(見 檢測工具管理) |
⚠️ DDD 提醒:
PUT jobroute 層無角色守門(別比照為權限典範);問卷關聯跨到surveyschema 的task_surveys(非compliance.job_execution_surveys),維護留意 schema 邊界;detection_tool 綁定表config.job_execution_detection_tools屬configschema(非compliance),跨 schema 命名慣例注意。
9. DB
9.1 資料表總清單(本頁讀寫的全部表)
| 表 | 讀寫 | 說明 | 詳述 |
|---|---|---|---|
| compliance.job_executions | 讀 / 寫 | 任務本體(name / description / guide / job_type) | §9.3 |
| compliance.task_assignees | 讀 / 寫(reconcile) | 指派人員(含 is_approver) |
§9.3 |
| compliance.job_execution_org_units | 讀 / 寫(全刪重建) | 任務↔︎部門 | §9.3 |
| compliance.job_execution_devices | 讀 / 寫(全刪重建) | 任務↔︎設備 | §9.3 |
| survey.task_surveys | 讀 / 寫(reconcile) | 任務↔︎問卷(含 approval_required) |
§9.3 |
| config.job_execution_detection_tools | 讀 / 寫(FR-056) | 任務↔︎檢測工具綁定(工具 uid / 掃描參數 /
完成模式,is_delete 軟刪) |
§9.3 |
9.2 ER 圖
9.3 核心表欄位(取自 DEV DB dump,含 DB comment)
compliance.job_executions — 任務本體
| 欄位 | 型別 | 說明 |
|---|---|---|
| uid | varchar(50) | 任務 UUID(對外 job id) |
| name | varchar(500) | 任務名稱 ← name |
| description / guide | text | 描述 / 指南 |
| job_type | varchar(50) | general / survey ←
job_type(另有 type jobtype enum 欄) |
| status | jobstatus | 任務狀態(TODO / PROCESSING…) |
| org_unit_id / tenant_id | integer | 租戶 / 組織(RLS) |
compliance.task_assignees — 指派人員
| 欄位 | 型別 | 說明 |
|---|---|---|
| project_id / control_id / task_id | integer | 複合定位 |
| task_uid / task_template_id | varchar | 任務 uid / 範本任務 id |
| user_id | integer | 指派人 |
| role | varchar(50) | 角色 |
| is_approver | integer | 是否審核者(0/1)← assignees[].is_approver |
compliance.job_execution_org_units / job_execution_devices — 部門 / 設備關聯
| 欄位 | 型別 | 說明 |
|---|---|---|
| job_execution_id | integer | → job_executions |
| org_unit_id / device_id | integer | 部門 / 設備(全刪重建 reconcile) |
survey.task_surveys — 問卷關聯(跨 schema)
| 欄位 | 型別 | 說明 |
|---|---|---|
| task_id / survey_id | integer | 任務 / 問卷 |
| approval_required | boolean | 是否需審核 ← surveys[].approval_required |
| device_id / department_id | integer | 問卷施測的設備 / 部門 |
| job_evidence_id | integer | soft ref → job_evidences.id(標記 GRC 建立) |
| is_delete | boolean | 軟刪旗標 |
⚠️ schema 註:問卷關聯在
surveyschema 的task_surveys(job_service._reconcile_task_surveys管理),不是compliance.job_execution_surveys(那是另一張簡單映射表,本更新路徑不寫)。欄位一律取自db_schema.json。
config.job_execution_detection_tools — 任務↔︎檢測工具綁定(FR-056)
| 欄位 | 型別 | 說明 |
|---|---|---|
| job_execution_id | integer | →
compliance.job_executions(uq_jedt_job_active
唯一約束:is_delete=false 下每任務僅一筆有效綁定) |
| detection_tool_id | bigint | → config.detection_tools(工具目錄) |
| tenant_config_id | bigint|null | →
tenant_detection_tool_configs.id;建表時預留,寫入端從未寫過此欄(一律
NULL,見 檢測工具管理
§12 已知坑),憑證解析改走
(tenant_id, detection_tool_id) fallback 反查 |
| tool_params | jsonb | 掃描參數(動態欄位,依 detection_tool_param_schemas
定義)。secret: true 欄位以加密 envelope
{"__enc__":"fernet","value":"<密文>"}
存放(FR-058.0),與明文欄位混在同一個
JSONB;金鑰同租戶層憑證的
DETECTION_TOOL_ENCRYPTION_KEY |
| completion_mode | varchar(20) | manual(預設)/ auto |
| is_delete | boolean | 軟刪(重新綁定其他工具=標舊列刪除 + 建新列,非 update) |
10. 頁面邏輯與資料對應
任務隨 control-tree 一次載入(內嵌於 AO 節點)。欄位對應:
| 畫面元素 | FE state | payload 欄位 → DB |
|---|---|---|
| 名稱 / 描述 / 指南 | task.name / .description /
.guide |
同名 → job_executions |
| 類型 | task.jobType |
job_type → job_executions.job_type |
| 指派人員(含審核人) | task.assignees[{id,name,is_approver}] |
assignees[{uid,is_approver}] →
task_assignees |
| 部門 / 設備 | task.departments / .devices |
department_uids / device_uids → org_units
/ devices 關聯 |
| 問卷(含審批) | task.surveys[{id,name,approval_required}] |
surveys[{uid,approval_required}] →
survey.task_surveys |
| 檢測工具 / 參數 / 完成模式(FR-056) | task.tool.{uid,params,completionMode} |
tool{uid,params,completion_mode} →
config.job_execution_detection_tools |
| secret 參數的「已設定」狀態(FR-058.0) | task.toolSecretKeysSet(string[]) |
response
tool.secret_keys_set(唯讀,不回送) |
選定檢測工具的預設值帶入(FR-057,FR-058 補清 secret
狀態):切換 task.toolUid 時先把
toolParams 清成
{}、toolSecretKeysSet 清空(換工具=換一套
schema,舊工具的 secret 已設定狀態不再適用),取回該工具的
param_schema 後,遍歷欄位定義找出有 default
值(非
undefined/null)的欄位組成預設物件,一次性寫回
toolParams——避免使用者看到全空表單以為沒有預設值可用(如
timeout_sec 預設 3600、Nmap
port_range 預設 1-1024、CINC Auditor
transport 預設
ssh)。出處:ProjectPlanningView.vue::onSelectDetectionTool:1011-1023。
儲存流程:saveTask 依序做 client
檢查——① detection_tool
型必須選定工具(warn_tool_required)→ ②
掃描參數必填檢查(FR-058,condition-aware,缺漏列出欄位名,見
§5)→ ③「survey 有審批要求但無審核人」→ 任一不過皆 toast warn 不送 → PUT
→ 前端 state
更新。錯誤對應:GRC_404005(任務不存在)/
GRC_409033(問卷已有填答無法移除)→ toast。
11. 背景行為與外部依賴
| 類型 | 內容 |
|---|---|
| socket | 無(儲存後前端 state 更新) |
| BPMN 同步 | update_job 在同一 transaction 回寫 BPMN userTask(name
/ description / guide / job_type / 問卷 actionInfo),維持 BPMN
編輯器一致 |
| Drive | 任務名稱變更 → best-effort 觸發 Drive 資料夾改名(無 Drive 整合則靜默跳過) |
| 依賴 | /users/menu(指派)/
/org-units/menu(部門)/
/surveys/menu(問卷)/ POST
/devices(設備) |
12. 邊界情況與已知坑
PUT job無 BE 角色守門:route 僅@jwt_required(),FE 靠三層canEdit擋;繞過 FE 直打 API 任一登入者都能改任務指派(與規劃頁 §12 同一 finding)。- 審核人檢查只在 FE:「survey
有審批要求則至少一審核人」只在
saveTask前 client 檢查(toast 不送),BE 無此驗證——直打 API 可存出「有審批要求卻無審核人」的任務。 - 問卷關聯跨 schema、非直覺:任務問卷走
survey.task_surveys(_reconcile_task_surveys),不是compliance.job_execution_surveys;後者是另一映射表,本路徑不動。 surveys[].approval_required送 null 保留既有值:SurveyInputSchema的approval_requiredload_default=None——BPMN 編輯器同步問卷時不帶此欄(無此 UI),送 null →_reconcile_task_surveys保留既有審批旗標,不誤蓋規劃頁設定;規劃頁顯式送 True/False 行為不變。is_admin→is_approver別名:FE 早期送is_admin,serializer@post_loadcoalesce 進is_approver;下游只讀is_approver。- 關聯 full-replace
語意:
assignees/department_uids/device_uids/surveys送[]清空、null/省略不動;部門 / 設備關聯是全刪重建(reconcile)。 - 問卷已有填答不可移除:移除已有答案的問卷 evidence →
GRC_409033(GRC_SURVEY_HAS_ANSWERS)。 - ⚠️ 快速配置會清空部門 / 設備 /
問卷關聯:
executeQuickConfig對每個 job 只 additive 合併指派人員,但送department_uids: []/device_uids: []/surveys: []——因 full-replace 語意([]=清空),會把該任務既有的部門 / 設備 / 問卷關聯一併清掉。若已配置這些關聯,用快速配置補人員前要留意會被清空(ProjectPlanningView.vue:1218)。 - survey × device 笛卡爾積:任務掛多問卷多設備時
survey.task_surveys列數會乘開(每問卷 × 每設備一列),指派前想清楚資料量。 - date-only 顯示 08:00 陷阱:任務時間相關欄位 BE
慣例吐
YYYY-MM-DDTHH:MM:00無 tz,前端 date-only 顯示要留意時區偏移(見 _overview 時區慣例)。 job_execution_detection_tools.tenant_config_id恆為 NULL(FR-056 已知坑):欄位建表時預留、讀取端(start_execution)會讀,但寫入端_replace_detection_tool_binding從未寫過此值——實務上憑證解析一律 fallback 走(tenant_id, detection_tool_id)反查(該組合有 UNIQUE 約束可唯一定位)。若使用者「先建任務、後設工具憑證」,查詢時解析比綁定時寫死更穩,故此設計非遺漏;該欄位是否退役留待未來討論。- secret
掃描參數留空=沿用、不是清空(FR-058.0):
encrypt_secret_params()對空值會從既有綁定沿用舊密文——因為 FE 對已設定的 secret 欄位承諾「留空=沿用原值」,不沿用則使用者每次改別的參數(如timeout_sec)都會把密碼清空,且症狀要到掃描時才浮現。代價是「清空單一 secret 參數」沒有 UI 入口;真要清空得換工具(onSelectDetectionTool會清toolParams與toolSecretKeysSet)或直接改 DB。 - 換檢測工具會清空全部掃描參數:
onSelectDetectionTool切換toolUid時先把toolParams清成{}、toolSecretKeysSet清空,再依新工具param_schema帶入default。這是刻意的——不清會讓新工具的同名 secret 欄位誤顯示「已設定」,使用者不填就存 → 存進空密碼。誤按下拉再切回原工具,參數要重填。 tool_params的加密判定看值不看 schema:is_encrypted()只認 envelope 的__enc__標記,不查param_schema哪些 key 是 secret——因為param_schema版本化(is_current可切換),若靠 schema 判定,改版後舊任務會把明文當密文解(拋例外)或把密文當明文送給 agent(帳密以密文形式送出,掃描失敗且難查)。改動加解密路徑時勿引入 schema 依賴。- 檢測工具任務不走 flow-engine complete
觸發掃描:
detection_tool型任務的「開始執行」走獨立的DetectionOrchestrationService(見 任務執行抽屜 §11.1),與本頁「儲存任務」/「開始執行任務」的批次轉 PROCESSING 是兩件事——後者只是把任務從 TODO 轉 PROCESSING 讓任務「可以」被執行,掃描本身另外觸發(規劃頁批次轉 PROCESSING 時會自動觸發首次掃描,見 §6.3)。
13. 開發與驗證
- 跑起來:起 BE + FE;登入見
.env/ 部署文件測試帳號(需該節點 manager;開始執行需專案 manager) - 導航:規劃頁 Tab0 → 左樹選 AO → 右面板任務卡展開編輯;快速配置 / 開始執行在 Header
- 前置資料:需 AP=planning 的專案 + 至少一個控制項有 AO 任務;備 user / org_unit / device / survey master 測四關聯;切 survey 型測問卷 + 審批 + 審核人 switch 連動;測快速配置後檢查部門/設備是否被清空(坑 8)
- 相關:現況編輯 / 程序書見 現況編輯;批次匯入見 批次維護;任務真相=範本 BPMN userTask;問卷設計見 survey 功能群