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-26(FR-067 驗收回饋,v1.16.0 後續補項,歸下版):分派列摘要與執行確認框的目標顯示改「原始寫法+真實台數」(
192.168.50.1-123(123 台))——台數由 BE 算好供應(scan_target_spec.display_counts_of()唯一入口,FE 不複製展開規則;CIDR 依欄位分流:展開型算可用主機/24=254、透傳型算整段 256;range 無歧義一律算;逐台寫法不給值);FE 以 BE 回的scan_target_spec字串比對畫面現值做過期防護(改了目標未存檔退回段數顯示)。詳見 §5(CM-1395) - 2026-08-25(FR-067.8):檢測任務改「每列自足」模型——原本「任務層共用掃描參數+分派列覆寫」的兩層形狀取消,改為每列=一份完整獨立的工作設定:任務層掃描參數區整塊移除(只留完成模式),每個 Agent 分派列展開後直接渲染該工具的整份掃描參數(目標/連線方式/掃描基準/SonarQube 模式各列自填),「留空沿用任務層」語意全數消失。連帶:分派列改摘要列+點開展開卡片(設定項太多,攤平認不出哪列在做什麼);每列可「複製此列」、新增列預設帶第一列的值(密碼欄刻意不抄);最少保留一列,agent 留空=執行時自動挑一台在線的(原「零列=自動分派」的載體改變);SonarQube 同一任務可一列 pull、兩列 upload 各掃各的源碼包(上傳槽逐列,見我的任務 §11.1)。新增驗證:逐列必填(依該列當下模式判斷該不該驗,GRC_400113)+project_key_suffix跨列唯一(兩列落同一 SonarQube 專案會互相覆蓋結果,GRC_400114)。舊資料相容走讀取端合併,不做 backfill。詳見 §5「Agent 分派區塊」 - 2026-08-25(FR-067,驗收第二輪):分派列新增連線方式(transport)覆寫——混合 OS 場景下同一任務的不同列可各走各的通道(Linux 走 SSH、Windows 走 WinRM),不必為每種 OS 各開一個任務;只對 param_schema 真有transport欄位的工具(CINC Auditor/GCB)出現,工具不支援GRC_400109、值不在選項內GRC_400110。掃描目標欄支援 CIDR 與末段範圍(192.168.50.0/24、192.168.50.1-50可與單一 IP 混用)——⚠️ 展開與否依「欄位」而非「工具」:逐台 SSH 登入型的欄位(CINC/GCB/OpenSCAP 的hosts,以及 Nmap 的hosts——那是執行主機不是掃描目標)BE 展開成逐台清單且有台數上限(預設 256,DETECTION_SCAN_TARGET_MAX_HOSTS);原生解析型(Nmap 的targets、OpenVAS 的hosts)原樣透傳且不套上限。格式錯GRC_400111(訊息帶出錯的那一段原文)、超上限GRC_400112。工具參數區的 hosts/targets 欄說明由param_schema.hint承載(migrationf10e45eb,措辭依上述分流分三段) - 2026-08-25(FR-067):檢測任務多 Agent 分派——檢測工具下拉正下方新增「Agent 分派」區塊(每列=agent+掃描目標+可選 profile 覆寫+⏱ 排程 chip;零列=自動分派 fallback),hosts 型工具有分派列時參數區hosts欄隱藏(掃描目標單一入口);單一目標型工具(ZAP/SonarQube)分派列不渲染目標欄(依 param_schema 有無hosts判斷);同一 agent 可開多列各掃各基準(CM-1379,BE 擋完全相同的重複列);openscap/inspec/gcb 的profile欄位改純下拉(select_or_text→select,手填實務上只產生錯誤輸入);測試連線可指定 agent(CM-1377)。詳見 §5「Agent 分派區塊」 - 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)+完成模式(manual/auto)。⚠️ FR-067.8 起掃描參數不在任務層——整份參數搬進每個 Agent 分派列(見 8a),任務層只剩這兩項 |
任務卡(jobType==='detection_tool' 條件層) |
§5 / §6.2 |
| 8a | Agent 工作指派(FR-067/FR-067.8) | 每列=一份完整獨立的工作設定:agent+該工具整份掃描參數(目標/連線方式/掃描基準/SonarQube 模式各列自填)+⏱ 排程時間。收合為摘要列、點開展開卡片;可複製列;最少一列(agent 留空=執行時自動挑)。同 agent 可多列(混合 OS 各掃各基準);secret 參數加密存放、必填驗證逐列且 condition-aware(FR-058 機制對到列上) | 任務卡「Agent 工作指派」區塊(檢測工具下拉正下方) | §5「Agent 分派區塊」 |
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;FR-067.8 起改逐列驗、condition 依該列參數求值) | GET /detection-tools/{uid}/param-schema |
FR-067.8 起:tool.agent_assignments[].scan_targets(每列一份完整參數);tool.params 僅存量資料的讀取合併底與內部 sticky 欄位承載,FE 存檔時刻意整個不送 |
!canEdit |
⚠️ 不在任務層渲染——整份參數在每個 Agent 分派列的展開卡片內(見下方「Agent 分派區塊」)。選定工具當下欄位 default 值自動帶入的行為(FR-057)仍在,作用於列;各工具參數見下方「四款新工具的掃描參數」與 檢測工具管理 §6.2 |
| 9b | 完成模式 | SelectButton | 否(預設 manual) |
manual / auto |
tool.completion_mode |
!canEdit |
manual=掃完留 PROCESSING 等人工;auto=掃完自動完成 |
| 9c | Agent 工作指派(FR-067/FR-067.8) | 列表(每列收合=摘要列:agent 名/目標/transport・profile chip/⏱ 排程 chip/複製列/刪除;展開=整份 param_schema 表單)+「新增一組」 | 最少一列(剩一列時不可刪);列內必填逐列驗(GRC_400113)、project_key_suffix 跨列唯一(GRC_400114)。agent 留空合法=執行時自動挑一台在線的 |
GET /remote-agents?capability=detection_scan + GET /detection-tools/{uid}/param-schema |
tool.agent_assignments[]({uid?, agent_uid, scan_targets, sort_order, scheduled_at?},scan_targets 承載該列完整參數) |
!canEdit |
僅 detection_tool 型顯示,位於檢測工具下拉正下方;詳見下方「Agent 分派區塊」 |
select_or_text型態(FR-057 新增):PrimeVue editable Dropdown——下拉列常用選項,也可直接輸入清單外的自由字串。FR-058 把 CINC Auditor 的profile由text改為select_or_text。渲染元件與檢測工具管理 §5 設定表單共用同一DetectionConfigField.vue。 ⚠️ FR-067(2026-08-24 拍板)起 openscap/inspec/gcb 三支的profile欄位改回純select:手填實務上只產生錯誤輸入——PrimeVue editable Dropdown 打字不會過濾選項(清單不動、看起來像壞掉);openscap 的值是 XCCDF profile id,必須存在於目標主機的 SSG content 內,手打幾乎必錯;庫(含自行上傳)已是主要來源。migration 用 jsonb_agg 只改type保住欄位順序,FE 零改(select與select_or_text共用同一份 selectOptions)。select_or_text型態本身保留供其他欄位使用。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 零改。設定檔的維護見 掃描設定檔管理。
Agent 分派區塊(FR-067/FR-067.8,2026-08-25 現況)¶
檢測任務以「Agent 工作指派」清單設定「哪台 agent 用什麼參數掃哪段目標」,支援多台 agent 各管一個網段、混合 OS 各掃各基準、SonarQube 多包混掃。區塊位於「檢測工具」下拉 正下方,其下只剩「完成模式」——任務層的共用掃描參數區已於 FR-067.8 整塊移除。
🔴 每列=一份完整獨立的工作設定(FR-067.8「每列自足」):列上帶該工具整份掃描參數, 沒有「留空沿用任務層」這回事。原本兩層合併(任務層墊底+列覆寫)的心智模型已取消—— 使用者看一列就是那一列實際會用的全部設定。
列的形狀:收合摘要 + 點開展開卡片
| 狀態 | 內容 |
|---|---|
| 收合(摘要列) | agent 名稱/目標(含 CIDR 或範圍時顯示原始寫法+BE 供的真實台數如「192.168.50.1-123(123 台)」,CM-1395;逐台寫法顯示「N 台」)/連線方式與掃描基準 chip/⏱ 排程 chip(× 可移除)/「複製此列」鈕/刪除鈕 |
| 展開(卡片) | 以 DetectionConfigField 渲染該工具整份 param_schema——condition 依本列參數求值(SonarQube 選 upload 就出現上傳相關欄位、選 pull 就出現 project_key)、secret 欄 per 列顯示「已設定」、profile 庫動態選項、advanced 分區照舊 |
- 新增一組=新列直接展開;存檔驗證沒過的列強制展開+整列紅邊(錯誤欄位藏在收合區 裡的話,toast 說「第 2 組有問題」但畫面上那組看起來一切正常)。
- 最少一列:剩一列時刪除鈕 disabled。agent 留空是合法值=執行時自動挑一台在線的 (原「零列=自動分派」的 fallback 載體改變,便利性保留)。
- 複製此列+新增列預設帶第一列的參數值(非空白表單);密碼等 secret 欄刻意不抄—— 抄 key 名會讓新列誤顯示「已設定」,使用者不填就存進空密碼。
- 摘要列的台數由 BE 供、FE 不算(CM-1395):FE 刻意不算展開後的總台數,展開規則
(是否排除 network/broadcast、哪些欄位不展開)是 BE 的分流邏輯,在 FE 複製一份就是第二套
真相——初版因此只顯示「N 段」,CM-1395 改由 BE
display_counts_of()算好台數隨分派列回傳 (含scan_target_spec原始寫法字串),FE 只負責印。只在「一段代表多台」時給台數 (CIDR/range);CIDR 依欄位分流:展開型算可用主機(/24=254)、透傳型算整段(256, 工具實際處理的量);逐台寫法段數即台數不另給。過期防護:FE 以scan_target_spec字串 與畫面現值比對,使用者改了目標未存檔時退回段數顯示,不顯示過期台數。執行前確認框同一原則 (agent(192.168.50.1-123(123 台))+ 立即/⏱ 時間開始)。
欄位重點(存 config.job_execution_detection_tool_agents 子表,PUT 走
tool.agent_assignments[] diff-sync;列 params 存該表的 scan_targets JSONB——欄位名沿用
未改,承載內容已從「覆寫子集」擴張為「完整列參數」):
| 欄 | 說明 |
|---|---|
| Agent 下拉 | 資料源 GET /remote-agents?capability=detection_scan;離線機照列標「(離線)」不可選。同一 agent 可開多列(混合 OS 場景);BE 擋的是「(agent, 列參數內容) 完全相同」的重複列(GRC_400107)。留空=執行時自動挑一台在線的 |
| 掃描目標 | 支援單一 IP/CIDR/末段範圍混用(192.168.50.0/28, 192.168.60.1-20, 192.168.70.5),分隔符吃逗號/分號/空白/換行。欄位下方給語法說明(不塞 placeholder——一輸入就消失) |
| 連線方式(transport) | SSH/WinRM,per 列(同一任務 Linux 列走 SSH、Windows 列走 WinRM)。只對 param_schema 真有此欄位的工具出現(CINC Auditor/GCB) |
| 掃描基準(profile) | 選項源同工具參數區(FR-059 設定檔庫動態餵);content_path 收在該列 advanced 分區 |
SonarQube scan_mode 等 |
各列自選模式與對應欄位——同一任務可一列 pull、兩列 upload,兩個 upload 列各掃各的源碼包(上傳槽在我的任務抽屜逐列提供) |
| ⏱ 排程 chip | per-列排程時間(scheduled_at)——預設無時間欄位,按 icon 彈 datetime picker,選定顯示實心 chip 帶 ×。時間已過附註「(已過,將立即開始)」 |
掃描目標的展開規則(BE,⚠️ 判準是「欄位」不是「工具」):同一份語法對不同欄位意義不同, BE 依「這個欄位的值最後被 agent 拿去做什麼」分流——
| 工具 | 欄位 | BE 行為 | 為什麼 |
|---|---|---|---|
| CINC Auditor/GCB/OpenSCAP | hosts |
展開成逐台清單 | agent connector 逐台建 SSH 連線 |
| Nmap | hosts |
展開 | 這是 agent 登入去跑 nmap 的執行主機,不是掃描目標 |
| Nmap | targets |
原樣透傳 | nmap 原生解析 CIDR,併發探測遠比幾百個參數高效 |
| OpenVAS | hosts |
原樣透傳 | 丟給 GVM 的目標規格,GVM 原生支援 |
- CIDR 語意刻意與 nmap 不同:
/24展開成 254 台(排除 network/broadcast——用途是逐台 SSH 登入,那兩個位址登入必定失敗、在報告留下兩筆假失敗)。/31、/32例外全數視為主機。 - 台數上限只對展開型套用(預設 256,
DETECTION_SCAN_TARGET_MAX_HOSTS;剛好容納一個 /24。要掃更大範圍的正解是拆成多列平行跑,不是調高)。OpenVAS 的 /16 是正常用法故不套。 - 驗證只在寫入端(存檔時擋):格式錯
GRC_400111(訊息帶出錯的那一段原文)/ 超上限GRC_400112。派工端刻意不擋只展開——擋在那裡使用者會在「按下執行」才看到 幾天前存的設定有問題,且存量資料或上限調小時舊任務會突然執行不了。 - 跨段範圍(
192.168.1.1-192.168.2.5)不支援但明確報錯,不會被誤當成主機名放行。 - 工具參數區那格的說明走
param_schema.hint(措辭依上述分流分三段給:展開型講上限與拆列的 出路/Nmaphosts強調是執行主機/透傳型刻意不提上限),與分派列的說明措辭一致。
逐列驗證(FR-067.8):
- 必填 condition-aware 且逐列判:
GRC_400113,訊息帶「第 N 列(缺哪些欄位)」——SonarQube 的repo_url只在scan模式必填,對 upload 列驗它會讓合法設定存不進去。FE 與 BE 用同一套 判定規則(BEcommon/util/detection_assignment_params.field_visible/FEsrc/utils/detectionFieldValidation.js),避免「畫面沒這個欄位、存檔卻說它必填」。 project_key_suffix跨列唯一:GRC_400114。兩列落到同一 SonarQube 專案代碼,後跑完的 那次會覆蓋前一次的結果,使用者看到的是「有一列的掃描結果莫名其妙不見了」。- 測試連線可指定 agent(CM-1377):測試連線對話框加 agent 下拉(首項「自動選擇」=原
行為;離線機照列 disabled),開窗門檻=可選 agent >1 台;成功訊息帶實際執行 probe 的
agent 名稱。BE 對指定台走可派性診斷,不可派 400 一次列全原因(
DETECTION_TOOLS_400018)。
存量相容(不做 DB backfill):舊資料是「任務層參數+列覆寫(或零列)」,讀取端做一次性
語意合併——每列讀出時任務層 params 墊底;零列綁定合成一列(agent 留空)。合成列沒有 uid,
使用者第一次存檔時才真正落庫(把隱含的 fallback 顯性化)。存檔時 FE 刻意整個不送
tool.params:BE 對未帶欄位是 None-skip,而任務層那份現在只剩兩個作用——存量讀取的合併底、
以及承載 _source_file(upload 源碼包 sticky 參照);送 {} 會把後者清掉,症狀是 upload
任務重掃時找不到源碼包,且要到執行當下才發現。
執行展開:每列直接以該列 params 派工(不再合併);profile:<uid> 庫內參照的展開仍在
派工當下做。執行紀錄的群組化呈現見 我的任務 §11.1。
四款新工具的掃描參數(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++ 目前亦不支援。 - 同一任務可混用三種模式(FR-067.8):
scan_mode是每列各自的參數,一列 pull、兩列 upload 各掃各的源碼包是典型用法(前後端各一包);project_key_suffix因此必須跨列唯一,否則兩列落到同一 SonarQube 專案、後跑完的覆蓋前一次(GRC_400114於存檔時擋)。 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)¶
⚠️ FR-067.8 起載體改為「列」:
secret: true欄位跟著 Agent 分派列走(DB 密文存在該列的scan_targets、API response 剝除、FEsecret_keys_setper 列顯示「已設定」)。下表機制逐項不變,只是作用範圍從「一個任務一份」變成「一列一份」。另複製此列/新列帶預設值刻意不抄 secret 欄——抄 key 名會讓新列誤顯示「已設定」,使用者不填就存進空密碼(與換工具時清空toolSecretKeysSet同一個理由)。
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)¶
⚠️ FR-067.8 起改逐列驗:每個 Agent 分派列各驗一次,
condition依該列的參數求值(SonarQube 的repo_url只在該列scan_mode='scan'時必填,對 upload 列驗它會讓合法設定存不進去)。BE 端同一套判定在common/util/detection_assignment_params.py::field_visible(),與 FE 的isDetectionFieldVisible()規則一致——兩邊漂移會出現「畫面沒有這個欄位、存檔卻說它必填」。BE 保底錯誤碼GRC_400113(訊息帶第幾列缺哪些欄位,FE 列入CODES_KEEPING_BE_DETAIL併陳明細)。
原本 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 功能群