跳轉到

第三方元件風險:受評系統清單(Source Scan Target List)

功能群:第三方元件風險|兩個入口、掃描流程、資料模型、權限先讀 功能群總覽

事實基準:2026-10-11 從 FE src/views/source-scan/SourceScanTargetList.vue、SourceScanWizard.vue、TaskScanList.vue、BE api/source_scan/routes/target_route.py、jedi-source-scan source_scan_target_app_service.py、DEV compliance.source_scan_targets 實查掃出

變更紀錄

日期 FR 變更
2026-10-11 FR-134 新頁:受評系統清單、三步精靈(新增/重新掃描)、任務發起的掃描頁籤、批次刪除受評系統、清理失敗紀錄

1. 功能描述

第三方元件風險模組的入口頁。一列=一套受評系統(被掃的軟體),顯示最近一次掃描的重點數字與前次比較;從這裡新增受評系統並發起首次掃描、重新掃描、進結果頁、進歷次比較、刪除。第二個頁籤列出從專案任務發起的掃描(不掛受評系統)。

主要使用者:顧問、稽核員(持 detection.source-scan.*)。

角色速覽

角色 一句話
稽核主管(detection.*.*) 全部操作,含刪除
稽核員(detection.*.read+create/execute/export) 新增、掃描、看結果、匯出;不能刪
一般成員/唯讀(detection.*.read) 只能看清單與結果

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

# 功能 說明 位置 詳述
1 受評系統清單 分頁列出受評系統;欄位:系統名稱/委託單位說明/輸入來源/最近掃描時間/關鍵指標/前次比較/功能 「受評系統」頁籤 §5
2 搜尋 依系統名稱關鍵字,停止輸入 300ms 後查詢 Header 搜尋框 §10
3 新增受評系統 三步精靈:基本資料 → 掃描來源 → 掃描設定,送出即建系統並發起首掃 Header「新增受評系統」 UC-SSL-01
4 重新掃描 同一精靈,跳過基本資料、來源類型鎖定 列操作 ↻ UC-SSL-02
5 掃描進度 掃描中的列,「關鍵指標」欄改顯示四段進度並每 3 秒更新;失敗時顯示失敗提示 列「關鍵指標」欄 §10
6 檢視結果 開最近一次完成的掃描結果頁 列操作 📊 掃描結果頁
7 歷次比較 有兩次以上完成的掃描才出現 列操作 🕘 歷次比較
8 刪除單一受評系統 確認框列出將一併刪除的掃描次數;有掃描進行中則按鈕停用 列操作 🗑 UC-SSL-03
9 批次刪除 勾選多列後「刪除所選(N)」;所選含掃描進行中的列則按鈕停用 勾選欄+Header UC-SSL-03
10 清理失敗紀錄 該系統有失敗的掃描時出現;對話框只列失敗的那幾筆讓人勾選刪除 列操作(橡皮擦) UC-SSL-04
11 任務發起的掃描 第二頁籤:從專案任務發起的掃描,一列一次;可開結果頁 「任務發起的掃描」頁籤 §5.3
12 授權唯讀 授權到期唯讀時所有寫入按鈕 disabled,tooltip 說明原因 全部寫入按鈕 總覽 §7.2

頁籤切換寫在網址 ?tab=targets|task,重新整理停在原頁籤。

2. Use Case

視覺慣例:判斷框=分支條件;紅框=擋下並標 error code;綠框=完成。

UC-SSL-01 新增受評系統並首次掃描

項目 內容
角色 持 detection.source-scan.create+execute
前置條件 授權含檢測工具包;未進唯讀
產出 / 後置條件 source_scan_targets 一列;source_scans 一列(queued)+背景工作;稽核事件 6190、6191

UC-SSL-01 新增並首掃

精靈三步:

步驟 欄位 規則
基本資料 系統名稱(必填,≤255)、委託單位/系統說明(選填,≤2000)、技術聯絡窗口(選填,≤2000,提示「僅供紀錄,系統不會主動聯繫」) 名稱空白不能下一步
掃描來源 二選一:「上傳原始碼封存檔」(.zip/.tar/.tar.gz/.tgz,≤100MB)或「上傳 SBOM 文件」(.json/.xml/.spdx,≤50MB);拖曳或點選檔案 選原始碼時提示要含鎖定檔;副檔名與大小前端先擋,後端再擋
掃描設定 「標準掃描」卡片=三套工具全勾+CycloneDX 1.6、SPDX 2.3;「進階設定」可改弱點比對工具(Trivy/Grype/OSV-Scanner)與 SBOM 輸出格式(CycloneDX 1.4/1.5/1.6、SPDX 2.2/2.3) 工具、格式各至少一個;有改過時卡片標「已調整」

送出順序:先 POST /source-scan/targets 建系統,再 POST /source-scan/targets/{uid}/scans 上傳並入列。上傳中顯示百分比進度條,對話框不可關。

陷阱:系統建好但上傳失敗時,系統已存在。精靈記住剛建好的 uid,使用者換檔重送只補上傳,來源類型同時鎖定——否則每按一次就多一個重複的受評系統。

UC-SSL-02 重新掃描

項目 內容
角色 持 detection.source-scan.execute
前置條件 該系統沒有掃描進行中
產出 / 後置條件 source_scans 新一列;完成後 summary.delta 自動對前一次完成的掃描算出修補/新增/仍存在

精靈只有「掃描來源」「掃描設定」兩步,標題「重新掃描:<系統名稱>」。來源類型鎖定為該系統建立時選的那一種(提示「同一受評系統須固定使用同一種來源,歷次結果方可比較」)——原始碼與 SBOM 兩種來源的元件辨識範圍不同,混用會讓歷次比較失真。

UC-SSL-03 刪除受評系統(單一/批次)

項目 內容
角色 持 detection.source-scan.delete
前置條件 所選系統都沒有掃描進行中
產出 / 後置條件 受評系統與其全部掃描、元件、弱點刪除(DB 外鍵 cascade);每套系統一筆稽核事件 6193

UC-SSL-03 刪除

  • 確認框文字:「將刪除 N 套受評系統及其全部掃描紀錄(共 M 次),無法復原。」沒有任何掃描時省略次數。
  • 整批全有或全無:後端先檢查所有選取的系統,任一套有掃描排隊中或執行中就整批 409 SOURCE_SCAN_409001,訊息點名是哪幾套;不會刪掉一半。前端同時把「勾到掃描中的列」時的刪除鈕停用,避免送出後才被擋回。
  • 上傳檔與產物檔留在檔案庫,不在本交易刪。
  • 換頁或重新查詢時勾選清空,避免刪到畫面上看不到的列。

UC-SSL-04 清理失敗紀錄

項目 內容
角色 持 detection.source-scan.delete
前置條件 該系統至少有一次失敗的掃描
產出 / 後置條件 所選失敗掃描刪除;每筆一個稽核事件 6195

對話框說明「僅失敗的掃描可刪除;成功的掃描保留作為歷次比較依據,不可刪除」,列出該系統所有失敗掃描(時間、檔名、錯誤摘要),可全選。後端另擋一道,下列任一種混在選取裡就整批 409 SOURCE_SCAN_409002 並點名:

拒絕理由 為什麼
不是失敗的(完成或進行中) 完成的是歷次比較的基準;進行中的要等它結束
任務發起的掃描 跟著任務走,不從模組刪
該系統最近一次完成的掃描 防禦性檢查:系統指向的那筆被刪會變孤兒指標

3. 權限矩陣

操作 FE 判定 BE 強制 BE 檢查位置
選單可見、進頁 ui_routes source-scan 綁 detection.source-scan.read(ALL) — web-menu
清單、任務掃描清單 — require_license("plugin")+detection.source-scan.read SourceScanTargetListRoute.post、SourceScanTaskScanListRoute.post
新增受評系統 v-can="{ all: [create, execute] }" create SourceScanTargetCreateRoute.post
發起掃描(含重新掃描) v-can=execute(精靈「開始掃描」、列 ↻) execute SourceScanTargetScansRoute.post
刪除/批次刪除/清理失敗 v-can=delete(勾選欄、🗑、橡皮擦、Header 刪除鈕) delete SourceScanTargetDetailRoute.delete、SourceScanTargetBatchDeleteRoute.post、SourceScanBatchDeleteRoute.post
租戶歸屬 — RLS;查不到即 404 SOURCE_SCAN_404001/404002 source_scan_target_app_service.py::_get
授權唯讀 useLicenseReadonly().writeDisabled() require_license("plugin") common/authz/license.py

4. 狀態機與前置條件

掃描狀態機見總覽 §4。本頁的 gate:

條件 效果 出處
該列有任一次掃描 queued/running ↻ 與 🗑 停用;勾選後 Header 刪除鈕停用並提示「所選系統有掃描進行中…」 SourceScanTargetList.vue::isRowBusy、selectedBusy
有掃描進行中仍送刪除 409 SOURCE_SCAN_409001,訊息點名系統 source_scan_target_app_service.py::delete_targets
完成次數 ≥ 2 出現「歷次比較」鈕 SourceScanTargetList.vue doneCount >= 2
有失敗掃描 出現「清理失敗紀錄(N)」 failedCount
有完成的掃描 出現「檢視結果」,開最近一次完成的 lastDoneScan
精靈來源類型 ≠ 系統建立時的類型 重新掃描時來源選擇鎖死 SourceScanWizard.vue lockedType

5. UI 設計

受評系統清單版面

5.1 受評系統頁籤欄位

欄 內容
勾選(只對持 delete 顯示) 批次刪除用
系統名稱 粗體
委託單位/說明 最多兩行截斷,空值「—」
輸入來源 Tag:原始碼封存檔/SBOM 文件
最近掃描時間 最近一次掃描的完成時間(沒完成取建立時間),YYYY-MM-DD HH:mm;沒掃過顯示「尚未掃描」
關鍵指標 掃描中或失敗:四段進度(ScanStageProgress);否則最近一次完成的重點數字:「KEV 收錄 N」「嚴重度 High 以上 N」
前次比較 「已修補 N/新增 N」(綠/橘);只完成一次顯示「首次掃描」
功能 純圖示鈕,滑過有 tooltip:📊 檢視結果、🕘 歷次比較、↻ 重新掃描、橡皮擦 清理失敗紀錄(N)、🗑 刪除

關鍵指標的兩個數字:

  • KEV 收錄:本次弱點中被列在美國 CISA「已知遭利用弱點」目錄的筆數。弱點資料沒帶 KEV 目錄時顯示「—」並提示「無法判定,不代表無收錄」——不能顯示 0。
  • 嚴重度 High 以上:Critical+High 的筆數。各工具各算各的、取最多的那家,tooltip 列這次跑了的各家原數(例:「Trivy 58/Grype 57/OSV-Scanner 55」)。

5.2 四段進度

檔案接收 → SBOM 產製(上傳 SBOM 時標「(已提供,略過)」)→ 弱點比對 → 報告彙整。排隊時顯示「排隊中,等候開始」;失敗時顯示「掃描未完成,請確認檔案內容後按『重新掃描』」。

5.3 任務發起的掃描頁籤

欄 內容
所屬專案 反查任務所屬專案名
任務 任務名
掃描時間 發起時間(清單依發起時間排,這欄也顯示發起時間,否則排序看起來是亂的)
關鍵指標 失敗:紅色「掃描失敗」徽章,滑過看錯誤首行;進行中:四段進度;完成:重點數字(沒有數字的指標整顆不顯示)
功能 📊 檢視結果(失敗的也能開,看原因)
  • 所屬任務已刪除的掃描不列。反查專案走 canonical JobProjectOwnershipQuery;反查本身壞掉時不過濾(記 log),清單是找結果用的,不能因此整頁 500。
  • 失敗的提示:「掃描未完成,可開啟結果頁查看原因;如需重新掃描,請至所屬任務重新執行掃描工具。」——本頁籤沒有重新掃描與刪除。
  • 空狀態:「於專案任務中選用『第三方元件弱點掃描』工具執行後,結果將列於此處。」

6. API 規格

Envelope 見 CLAUDE.md「Response Format」。全部端點守門順序 @jwt_required → @require_license("plugin") → @require_capability(...)。

6.1 總清單(本頁呼叫)

Method + Path 說明 能力點
POST /source-scan/targets/list 分頁清單(body 帶 filters.keyword) read
POST /source-scan/targets 建受評系統 create
GET /source-scan/targets/{uid} 單一受評系統 read
PUT /source-scan/targets/{uid} 改名稱/說明(畫面目前未使用) update
DELETE /source-scan/targets/{uid} 刪單一 delete
POST /source-scan/targets/batch-delete 批次刪受評系統,body {"uids": [...]} delete
GET /source-scan/targets/{uid}/scans 該系統歷次掃描(新到舊) read
POST /source-scan/targets/{uid}/scans 上傳並發起掃描(multipart) execute
POST /source-scan/scans/batch-delete 刪失敗掃描,body {"uids": [...]} delete
POST /source-scan/task-scans/list 任務發起的掃描分頁清單 read
GET /source-scan/scans/{uid} 輪詢掃描進度 read

6.2 建受評系統(SourceScanTargetCreateSchema)

欄位 型別 說明
name string,必填,1~255 系統名稱
client_note string|null,≤2000 委託單位/系統說明
contact_note string|null,≤2000 技術聯絡窗口
source_type string,必填 upload|sbom;其他值 400

回應 SourceScanTargetResponseSchema:

欄位 型別 說明
uid string
name / client_note / contact_note / source_type 同上
latest_scan_uid string|null 最近一次完成的掃描;進行中與失敗的不會寫這欄
created_user / created_user_name / updated_user / updated_user_name string|null 審計欄+暱稱
created_at / updated_at datetime

出處:api/source_scan/serializers/source_scan.py:28-60。

6.3 發起掃描(multipart)

欄位 說明
file 原始碼封存檔或 SBOM 文件
source_type upload|sbom,須與受評系統一致
tools 可重複:trivy/grype/osv;不帶=三家全跑
sbom_formats 可重複:cyclonedx-json@1.4|1.5|1.6、spdx-json@2.2|2.3,≤8 個

回應 {"scan_uid": "...", "status": "queued"}。驗不過的檔不進儲存、也不留掃描列(create_scan_from_upload「先驗再存」);掃描列與背景工作同一交易建立,排不進去就不留一筆永遠 queued 的列。

6.4 批次刪除(SourceScanBatchDeleteSchema)

{ "uids": ["<uid>", "<uid>"] }

uids 1~100 筆,每個 ≤64 字。回應 {"deleted": N}。409 的 msg 帶「<原因>: <點名清單>」,前端以 error_code 翻譯當抬頭、msg 當明細。

6.5 任務掃描清單回應(TaskScanResponseSchema)

欄位 型別 說明
uid / source_type / status string
progress dict|null 四段進度
summary dict|null 見總覽 §6
error string|null 失敗原因
started_at / finished_at / created_at datetime
task_context object|null project_uid、project_name、job_uid、job_name

錯誤碼見總覽 §9。

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

檔案 角色
src/views/source-scan/SourceScanTargetList.vue 主頁:兩頁籤、清單、搜尋、刪除、按鈕守門
src/views/source-scan/SourceScanWizard.vue 新增/重新掃描精靈(三步或兩步)
src/views/source-scan/WizardStepBasic.vue/WizardStepSource.vue/WizardStepOptions.vue 精靈三步
src/views/source-scan/TaskScanList.vue 任務發起的掃描頁籤
src/views/source-scan/ScanStageProgress.vue 四段進度
src/views/source-scan/ScanKeyFigures.vue 清單重點數字(兩頁籤共用)
src/components/source-scan/FailedScanCleanupDialog.vue 清理失敗紀錄
src/composables/useSourceScanRows.js 清單列:補最近一次掃描、完成/失敗次數、輪詢
src/composables/useScanPoller.js 掃描中的列每 3 秒輪詢
src/composables/useScanToolsRan.js 這次跑了哪幾家工具
src/service/SourceScanService.js API 包裝
src/config/sourceScan.js 上傳上限、工具清單、狀態、輪詢間隔
src/config/locales/i18n/{zh-tw,en}/source-scan.json 頁面字串

8. 後端檔案地圖

鏈路 Route App Service 底層
受評系統 CRUD/批次刪除 api/source_scan/routes/target_route.py jedi-source-scan app/service/source_scan_target_app_service.py domain/source_scan、infra/source_scan
發起掃描 target_route.py::SourceScanTargetScansRoute.post source_scan_app_service.py::create_scan_from_upload scan_input_service.py(收檔驗證)、app/jobs/sbom_scan_job.py(背景工作)
任務掃描清單 api/source_scan/routes/task_scan_route.py task_scan_app_service.py 宿主 port:JobProjectOwnershipQuery(任務 → 專案反查)
宿主組裝 api/source_scan/__init__.py(路由註冊)、core/plugins/source_scan.py(DI、沙箱 client、檔案庫 adapter)

9. DB

9.1 資料表總清單

表 讀/寫 說明
compliance.source_scan_targets 寫 受評系統
compliance.source_scans 寫 發起掃描建一列;清理失敗刪列
compliance.source_scan_components/source_scan_findings 刪(cascade) 刪系統或掃描時一併刪
public.upload_files 寫 上傳檔
public.background_jobs 寫 sbom_scan 背景工作

9.2 compliance.source_scan_targets

欄位 型別 說明
id bigint NOT NULL PK
uid uuid NOT NULL 對外識別碼
tenant_id bigint NOT NULL 租戶
org_unit_id bigint 組織
name varchar(255) NOT NULL 系統名稱
client_note text 委託單位/說明
contact_note text 技術聯絡窗口
source_type varchar(16) NOT NULL upload|sbom;字串欄,允許值由 API 守門,不用 enum 以免加來源要改表
source_ref jsonb 來源補充資訊
latest_scan_uid uuid soft-ref → source_scans.uid,最近一次完成的掃描
created_user / updated_user varchar(255) 審計欄
created_at / updated_at timestamptz NOT NULL

source_scans 欄位卡見掃描結果頁 §9。

10. 頁面邏輯與資料對應

  • 載入:POST /targets/list 取一頁 → 對每列平行 GET /targets/{uid}/scans,算出 latestScan(第一筆)、lastDoneScan(第一個 done)、doneCount、failedCount、hasActiveScan、scanCount。單列補資料失敗不擋整頁,該列顯示「尚未掃描」。
  • 為什麼要逐列再查歷次掃描:受評系統的 latest_scan_uid 只在掃描完成時寫,進行中與失敗的掃描只能從歷次清單找。
  • 輪詢:最近一次是 queued/running 的列,每 3 秒 GET /scans/{uid} 更新進度,直到 done/failed。任務頁籤同理。
  • 錯誤:一律由全域 interceptor 依 error_code 顯示 i18n 訊息;精靈出錯時停在當頁讓使用者換檔重送。

11. 背景行為與外部依賴

類型 內容
背景工作 sbom_scan(max_attempts=1),worker 撿單執行;沒起 worker 時掃描永遠停在「排隊中」且不報錯
沙箱 guidant-sandbox /v1/scan,單一併發,逾時 20 分鐘
稽核事件 6190 建系統、6191 發起掃描、6193 刪系統、6195 刪掃描
socket 無;進度靠輪詢
jedi-* 套件 jedi-source-scan(本模組全部 domain/service)、jedi-detection(安全解壓、沙箱 client)

12. 邊界情況與已知坑

  1. 刪除檢查與刪除之間不加鎖:檢查完沒有進行中、刪除前剛好有人對同一系統發起掃描,新掃描會隨系統一起被刪。可接受,不處理。
  2. 刪除不清檔案庫:上傳檔與產物檔留在檔案庫;要回收空間需另行處理。
  3. 重新掃描不能換來源類型:要改用 SBOM 文件,請另建一套受評系統。
  4. 畫面沒有編輯受評系統的入口:PUT /targets/{uid} 存在,畫面未接。
  5. 任務掃描的「所屬任務已刪除」判定靠反查:反查失敗時不過濾,可能短暫列出孤兒掃描。
  6. 兩頁籤共用 ScanKeyFigures:改重點數字的顯示會同時影響兩邊。

13. 開發與驗證

  • 導航:合規與稽核 → 第三方元件風險(/source-scan)
  • 跑起來:BE api(python main.py)+worker(RUN_MODE=worker python main.py)+DEV 沙箱(docker/dev/README-source-scan.md);弱點資料放 /opt/vulndb
  • 測試帳號:稽核主管(全部)、稽核員(無刪除)。密碼見 .env/部署文件
  • 驗收:新增系統上傳一包含鎖定檔的原始碼 → 清單看到四段進度 → 完成後出現重點數字 → 再掃一次 → 出現「已修補/新增」與歷次比較鈕 → 勾兩列批次刪
  • 相關:FR-134 設計 docs/features/FR-134-2610-source-code-sbom-scan/design.md