跳轉到

第三方元件風險:掃描結果頁(Source Scan Result)

功能群:第三方元件風險|三套工具、summary 形狀、呈現原則先讀 功能群總覽

事實基準:2026-10-11 從 FE src/views/source-scan/SourceScanResult.vue、src/components/source-scan/*、BE api/source_scan/routes/scan_route.py、jedi-source-scan source_scan_app_service.py/report//ai_analysis_service.py、DEV compliance.source_scans/source_scan_findings 實查掃出

變更紀錄

日期 FR 變更
2026-10-11 FR-134 新頁:風險總覽/弱點明細/元件清單三分頁、三家工具動態欄位與「顯示工具」勾選、各家編號與分數差異標示、單一完整報表匯出(PDF/HTML/Word)、AI 摘要分析

1. 功能描述

單次掃描的結果頁。兩個入口共用:清單的「檢視結果」、任務抽屜的「查看完整結果」都開這一頁。掃描還沒完成時只顯示四段進度並自動更新;完成後顯示三個分頁。

主要使用者:顧問、稽核員——看懂一套系統的第三方元件有哪些已知弱點、多嚴重、有沒有修正版,並匯出報表交付。

角色速覽

角色 一句話
持 detection.source-scan.read 看結果、下載 SBOM 與原始結果、按 AI 摘要分析
另持 detection.source-scan.export 多一顆「匯出報表」

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

# 功能 說明 位置 詳述
1 頁首資訊 系統名稱(任務掃描沒有受評系統時顯示「第三方元件風險結果」)、掃描時間、輸入來源、上傳檔名 頁首 §5
2 弱點資料日期 Tag「弱點資料日期 YYYY-MM-DD」,依資料年齡上色 頁首右 UC-SSR-01
3 匯出報表 選格式(PDF/HTML/Word)、委託單位名稱(選填)下載 頁首右 UC-SSR-03
4 掃描中進度 未完成時只顯示四段進度+「掃描完成後將於此顯示結果,頁面會自動更新」,每 3 秒輪詢 主區 §10
5 風險總覽 嚴重度分布、KEV、EPSS、修補可行性、分析涵蓋度、前次比較、元件授權分布 第一分頁 §5.1
6 AI 摘要分析 按了才呼叫 AI 把統計整理成文字;可重新分析 風險總覽下方 UC-SSR-04
7 弱點明細 每弱點一列、各工具一欄;篩選、排序、分組、展開看描述與來源頁 第二分頁 UC-SSR-02
8 顯示工具 勾選要顯示哪幾家工具的欄;沒跑的工具灰掉 弱點明細篩選列 §5.2
9 元件清單 原始 SBOM 檔攤成表:可切格式、搜尋、每元件弱點數可點 第三分頁 §5.3
10 原始檔案與來源 下載各格式 SBOM、各工具原始結果;複製 SHA-256;工具版本與資料來源 元件清單下方 §5.3

2. Use Case

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

UC-SSR-01 判讀一次掃描

項目 內容
角色 持 read
前置條件 掃描 done
產出 / 後置條件 無寫入

建議閱讀順序(也是畫面的排列順序):

  1. 先看弱點資料日期:超過 30 天黃、超過 90 天紅(以掃描完成時間算,舊掃描看的是「當時用了多舊的資料」)。資料太舊會漏掉新公布的弱點。
  2. 再看分析涵蓋度:有生態系缺鎖定檔時,紅色警示「此專案 npm 部分缺少 package-lock.json,弱點明細僅涵蓋直接宣告的元件、版本為估算值,且無法分析傳遞相依,實際風險可能較高」。這行出現時,後面的數字都可能低估。
  3. KEV 與 EPSS:已被實際利用、或預估近期被利用機率高的弱點,列出前幾筆(超過列「僅列前 N 筆,共 M 筆」)。
  4. 嚴重度分布:三家工具各一欄、各自計數。
  5. 弱點明細:逐列看、比對各家判讀差異。

UC-SSR-02 在弱點明細找出要先處理的

項目 內容
角色 持 read
前置條件 掃描 done
產出 / 後置條件 無寫入;篩選與排序每次重新向後端查詢

UC-SSR-02 弱點明細

  • 嚴重度 chip:點了就篩,篩的是列層等級(各家中較高者),可多選。
  • 排序:四種,表頭顯示「目前排序:KEV → EPSS → CVSS」。選中的鍵提到最前,其餘依 KEV→EPSS→CVSS 接在後面。
  • 檢視:依套件分組(預設,每頁 100 筆減少一個套件被切兩頁;組頭顯示最高嚴重度、弱點數、KEV 數,被切頁時標「續上頁」「續下頁」)或依弱點逐列。
  • 只看 KEV、只看有修正版:KEV 資料未載入時「只看 KEV」停用。
  • 元件篩選:從元件清單點弱點數進來時,篩選列多一顆「元件:」,可清除。
  • 展開列:弱點描述(工具沒給時「工具未提供描述」)、NVD 連結(非 CVE 編號時說明 NVD 無對應頁面)、各家「<工具> 來源頁」連結與編號、其他編號(別名)、套件識別(purl)。各家分級不一致時多一段說明「各家原始分級均保留於上方表格,請比對各工具來源頁內容」。

UC-SSR-03 匯出報表

項目 內容
角色 持 export
前置條件 掃描 done(未完成 412 SOURCE_SCAN_412001)
產出 / 後置條件 下載一個檔;稽核事件 6192

UC-SSR-03 匯出報表

  • 報表只有一種:完整技術版,含全部弱點與元件清單。顧問會自行消化再交付,不另提供摘要版或隱藏內部備註的版本。
  • 對話框兩個欄位:檔案格式(PDF/HTML/Word)、委託單位名稱(選填,顯示於報表抬頭;預設帶系統名稱)。
  • 檔名見總覽 §8 第 1 條,例如 PrimeVue_弱點掃描報告_20261011-1341.pdf。
  • 報表不含 AI 摘要。

報表章節(HTML 與 PDF 同一份模板,Word 用 docx 範本同章節):

章節 內容
封面 標題、委託單位、受評系統、掃描時間、產生時間
統計 嚴重度分布(這次跑了的每家工具一欄;Word 改為一家一列)、元件數、弱點數
與上次掃描比較 只在回訪報告出現(見歷次比較)
已知遭利用弱點(CISA KEV)/高利用機率弱點(EPSS 達門檻) 清單
有修正版的弱點 清單
授權類型統計 Copyleft/寬鬆/未標示
全部弱點列表 每筆:各工具判讀、編號連 NVD、來源頁連結、修正版
元件清單 全部元件
附錄:工具版本、資料日期與原始報告指紋 各工具版本、弱點資料日期、各原始檔 SHA-256
資料來源說明 NVD/CVSS、CISA KEV、FIRST EPSS、各工具弱點資料庫

報表只寫事實句,不出現「屬高風險」「建議立即」這類結論。PDF 由同一份 HTML 經 WeasyPrint 產出,字型用主機的 Noto Sans CJK TC(缺字型時中文會整段消失,見 CLAUDE.md「主機層依賴」)。

UC-SSR-04 AI 摘要分析

項目 內容
角色 持 read
前置條件 本租戶已設定 AI(沒設定時整張卡不顯示)
產出 / 後置條件 source_scans.ai_analysis 寫入或覆蓋;扣 AI 額度、留呼叫紀錄
  • 卡片說明「按下後才會呼叫 AI 並扣用 AI 額度,將上方統計彙整為文字說明;報表不含此段」。執行中顯示「AI 彙整中,約需十餘秒…」。
  • 結果固定標「AI 生成,以上方統計為準」,下方註記產生者、時間、模型。再按「重新分析」覆蓋前一次。
  • 送給 AI 的只有統計與前 20 筆弱點的事實欄位(編號、purl、等級、CVSS、KEV、EPSS、修正版),不送原始報告與弱點描述全文——描述來自外部弱點庫,是不可信文字。提示詞要求只整理資料段列出的事實、不下結論、不給燈號。
  • 提示詞以 AI 守門規則目錄 prompts.yaml 的 source-scan.analysis 為準,套件內建一份逐字相同的退路。
  • 錯誤:未設定 AI 412 SOURCE_SCAN_412002;額度用完 429 SOURCE_SCAN_429001;AI 失敗 502 SOURCE_SCAN_502001。

3. 權限矩陣

操作 FE 判定 BE 強制 BE 檢查位置
進頁、看三分頁 路由守衛(source-scan-result 子路由) require_license("plugin")+read SourceScanDetailRoute.get、SourceScanFindingsRoute.get、SourceScanComponentsRoute.get
下載 SBOM/原始結果 — read SourceScanFileRoute.get
匯出報表 v-can=export;且掃描 done export;未完成 412 SourceScanExportRoute.post
AI 摘要分析 後端回 ai_available=false 時整張卡不顯示 read;未設定 AI 412 SourceScanAiAnalysisRoute.post
租戶歸屬 — RLS;別租戶的掃描 404 SOURCE_SCAN_404002 source_scan_app_service.py::_get

任務掃描不另判專案成員:任務發起的掃描結果頁同樣只看 detection.source-scan.read。要加專案層守門須連「任務發起的掃描」頁籤一起改,另案處理。

4. 狀態機與前置條件

條件 效果 出處
status 為 queued/running 只顯示四段進度並輪詢;匯出鈕不出現 SourceScanResult.vue isDone
status 為 failed 顯示四段進度停在失敗段與錯誤訊息 ScanStageProgress.vue
summary.kev.available=false/epss.available=false 對應區塊顯示「未載入」與原因,不顯示 0 FactThreatList.vue
summary.coverage.missing_lock 非空 分析涵蓋度紅色警示 FactCoverage.vue
本次沒跑某工具 該工具不出現在任何欄位;「顯示工具」勾選框灰掉 useScanToolsRan.js
掃描完成時資料年齡 > 30/> 90 天 弱點資料日期 Tag 黃/紅 SourceScanResult.vue::vulndbAge

5. UI 設計

掃描結果頁版面

5.1 風險總覽

區塊 內容 資料
嚴重度分布 等級 × 工具的計數表(Critical/High/Medium/Low/Negligible/未分級/合計),只列這次跑了的工具;各家分級不一致時底下一行「有 N 筆工具間分級不一致,詳見『弱點明細』」 summary.severity、tools_disagree
已知遭利用弱點(CISA KEV) 編號、套件、收錄日 summary.kev+findings in_kev=true
高利用機率弱點(EPSS ≥ 10%) 編號、套件、EPSS 機率 summary.epss(門檻 threshold,預設 0.1)
修補可行性 已有修正版 N、尚無修正版 N、最早修正版發布日 summary.fixable
分析涵蓋度 缺鎖定檔的生態系(點名檔名,如 npm → package-lock.json、pypi → poetry.lock);NTIA 最低要素 n/7 與缺哪幾項 summary.coverage
前次比較 已修補/新增/仍存在;首次掃描顯示「首次掃描」 summary.delta
元件授權分布 元件總數;Copyleft 類(GPL/LGPL/AGPL)、寬鬆類(MIT/Apache/BSD/ISC)、未標示或無法辨識 summary.components+components license_risk

5.2 弱點明細

欄 內容
套件/版本 從 purl 解出名稱與版本
弱點編號 合併後的編號(CVE 優先),連 NVD
每家工具一欄「<工具>(等級/CVSS)」 等級徽章(依工具分級上色)+分數;該家編號與合併編號不同時,下方小字列該家自己的編號(可點來源頁,一行截斷、滑過看全文);該家沒報顯示「未報」
不一致標示 跑了的工具之間等級不同(提示「工具間分級不一致」)或分數差 ≥ 0.1(提示「工具間分數不一致」);只比有跑的工具,不受「顯示工具」勾選影響
KEV/EPSS 「已列入」標記、EPSS 百分比
修正版 工具列出的修正版本

顯示工具:篩選列的「顯示工具」三個勾選框。純畫面篩選——收掉某家只是隱藏那欄,不重查、不影響筆數與風險總覽。本次沒跑的工具灰掉並提示「本次掃描未執行」;只剩一個勾時不能取消;換一筆掃描時重設為全勾。

各家差異為什麼要露出:實測三家對同一批弱點,等級幾乎一致,但編號寫法常不同(Trivy 多寫 CVE、Grype 多寫 GHSA)、分數偶有 0.1 的差。只秀等級時看起來三家一模一樣,顧問無從判斷各家是否真的看到同一件事。

5.3 元件清單

  • 元件清單=原始 SBOM 檔攤成表,欄位照原檔,不另加工:元件名稱、版本、生態系、授權、PURL(點擊複製)、CPE、bom-ref(CycloneDX)或 SPDX ID(SPDX)。
  • 格式切換:這次產出了哪幾種 SBOM 就有幾顆(SPDX 固定排前)。
  • 搜尋:名稱或 PURL。
  • 多一欄「弱點數」:依 purl 對上後端元件清單算好的數字,點了切到弱點明細並篩該元件。元件沒有 purl 時標「此元件不在弱點比對範圍」。
  • 單檔超過 5,000 個元件時只列前 5,000 筆並提示,完整內容請下載。檔案解析不了時提示下載後以工具開啟。

原始檔案與來源(元件清單下方):

區 內容
軟體物料清單(SBOM) 每種格式一列:下載、複製 SHA-256
工具原始報告 這次跑了的每家工具一份 JSON:下載、複製 SHA-256;沒產出顯示「此次掃描未產出此類檔案」
工具與資料來源 各工具版本、弱點資料日期、上傳檔名、上傳檔 SHA-256

6. API 規格

6.1 總清單(本頁呼叫)

Method + Path 說明 能力點
GET /source-scan/scans/{uid} 掃描本體(狀態、進度、summary、產物清單、AI 摘要) read
GET /source-scan/targets/{uid} 取受評系統名稱當標題(任務掃描沒有) read
GET /source-scan/scans/{uid}/findings 弱點明細分頁 read
GET /source-scan/scans/{uid}/components 元件分頁(給弱點數、授權統計、KEV/EPSS 清單補套件名) read
GET /source-scan/scans/{uid}/files/{kind} 下載 SBOM 或原始結果 read
POST /source-scan/scans/{uid}/export 匯出報表 export
POST /source-scan/scans/{uid}/ai-analysis AI 摘要分析,回整筆掃描 read
GET /source-scan/targets/{uid}/scans 判斷是否首次掃描(前次比較區塊) read

6.2 掃描本體(SourceScanResponseSchema)

欄位 型別 說明
uid / target_uid / detection_execution_uid string|null 兩個入口二擇一非空
source_type / source_ref string / dict source_ref.filename 是上傳檔名
status string queued/running/done/failed
progress dict|null 四段進度
tools dict {"vuln": [...], "sbom_formats": [...]}——這次勾選
tool_versions dict 各工具版本
vulndb_date date|null 當次弱點資料日期
summary dict|null 見總覽 §6
sbom_quality dict|null NTIA 要素與缺鎖定檔明細
sbom_formats / raw_outputs / artifacts 計算欄 產物清單(種類、格式鍵、檔名、SHA-256)
ai_analysis dict|null 文字、模型、時間、產生者暱稱
ai_available bool|null 本租戶是否設定 AI
error / started_at / finished_at
審計欄+暱稱

出處:api/source_scan/serializers/source_scan.py:88-131。

6.3 弱點明細查詢(SourceScanFindingQuerySchema)

參數 說明
page / page_size 分頁;page_size 上限見 serializer
sort kev|epss|cvss|severity|package;選中的鍵在前,其餘依 KEV→EPSS→CVSS
severity 可重複,列層等級
tool 只看某家有報的
in_kev / has_fix 布林
component_purl 元件篩選

回應每列(SourceScanFindingSchema):

欄位 型別 說明
uid / component_purl / vuln_id string
aliases list 其他編號
severity / cvss_score string / float 列層:各家最高
trivy / grype / osv dict|null 各家判讀 {reported, id, severity, score, advisory_url};沒跑或沒報為 null
fixed_versions / oldest_fix_date list / date
in_kev / kev_date_added / epss_score
description / nvd_url string
package_group dict|null 分組模式的組頭資訊

6.4 下載(GET /files/{kind})

kind=sbom|trivy|grype|osv;sbom 另帶 ?format=cyclonedx|spdx|cyclonedx-json…&version=1.6。檔案這次沒產出 404 SOURCE_SCAN_404003。標頭規則見總覽 §8 第 2 條。

6.5 匯出(SourceScanExportRequestSchema)

{
  "format": "pdf",
  "client_name": "委託單位 A",
  "diff_with": null
}
欄位 說明
format 必填,html|pdf|docx
client_name 選填,≤255,封面委託單位
diff_with 選填,同系統另一次已完成掃描的 uid;帶了就是回訪報告(多「與上次掃描比較」一節)

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

檔案 角色
src/views/source-scan/SourceScanResult.vue 主頁:頁首、三分頁、輪詢
src/components/source-scan/FactDashboard.vue 風險總覽七區塊
src/components/source-scan/FactSeverity.vue/FactThreatList.vue/FactCoverage.vue/FactCard.vue 風險總覽子區塊
src/components/source-scan/AiAnalysisCard.vue AI 摘要分析
src/components/source-scan/FindingsTable.vue/FindingsFilterBar.vue 弱點明細表與篩選列
src/components/source-scan/FindingToolCell.vue/FindingDetail.vue/FindingGroupHeader.vue 工具格子、展開列、分組組頭
src/components/source-scan/SbomViewer.vue/RawDownloads.vue 元件清單、原始檔案與來源
src/components/source-scan/ExportReportDialog.vue 匯出對話框
src/composables/useScanToolsRan.js 這次跑了哪幾家(勾選 ∩ summary 非 null,依 SCAN_TOOLS 順序)
src/composables/useSourceScanFacts.js/useSourceScanFindings.js/useSbomViewer.js/useScanFileDownload.js 資料取用

8. 後端檔案地圖

鏈路 Route App Service 底層
掃描本體/明細/元件 api/source_scan/routes/scan_route.py jedi-source-scan source_scan_app_service.py domain/source_scan、infra/source_scan
下載 scan_route.py::SourceScanFileRoute、_file_response scan_file_download.py::pick_file 宿主檔案庫 adapter
匯出 scan_route.py::SourceScanExportRoute report/report_export_service.py report_model.py、report_render.py(HTML/PDF)、report_docx*.py(Word)、templates/report/
AI 摘要 scan_route.py::SourceScanAiAnalysisRoute ai_analysis_service.py 宿主 AI 閘道(FR-122)
檔名 — artifact_names.py —

9. DB

9.1 資料表總清單

表 讀/寫 說明
compliance.source_scans 讀;AI 摘要寫 ai_analysis 掃描本體
compliance.source_scan_findings 讀 弱點明細
compliance.source_scan_components 讀 元件、授權
compliance.source_scan_targets 讀 標題
public.upload_files 讀 SBOM、原始結果、報表檔

9.2 compliance.source_scans

欄位 型別 說明
id / uid bigint / uuid NOT NULL
tenant_id / org_unit_id bigint
target_uid uuid 可空:從檢測工具派工進來的掃描不掛受評系統
detection_execution_uid varchar(36) soft-ref → detection_executions.uid;非空表示跑完要回寫檢測結果
source_type varchar(16) NOT NULL
source_ref jsonb
input_file_id bigint soft-ref → upload_files.id(上傳的源碼包或 SBOM 檔)
status varchar(16) NOT NULL
background_job_uid varchar(36)
tools / tool_versions jsonb
vulndb_date date 當次使用的弱點資料庫日期(結果頁顯示新舊)
summary / sbom_quality jsonb
raw_outputs / sbom_files jsonb 產物檔指標與 SHA-256
report_file_id bigint soft-ref → upload_files.id
ai_analysis jsonb
error text
started_at / finished_at timestamptz
審計欄

9.3 compliance.source_scan_findings

欄位 型別 說明
scan_uid uuid NOT NULL → source_scans.uid cascade
component_purl text
vuln_id varchar(128) NOT NULL 合併編號,CVE 優先
aliases jsonb
severity / cvss_score varchar(16) / numeric(4,1) 列層
trivy / grype / osv jsonb 各家判讀;沒跑或沒報為 NULL
fixed_versions / oldest_fix_date jsonb / date
in_kev / kev_date_added / epss_score boolean NOT NULL / date / numeric(6,5)
match_key text ecosystem+name+version+正規化弱點編號(跨工具配對)
fingerprint text ecosystem+name+正規化弱點編號,不含版本——升版仍受影響要算「仍存在」
description / nvd_url text

9.4 compliance.source_scan_components

欄位 型別 說明
scan_uid uuid NOT NULL
purl text
name varchar(512) NOT NULL
version / ecosystem varchar
licenses jsonb
license_risk varchar(16) copyleft|permissive|unknown

10. 頁面邏輯與資料對應

  • 載入:GET /scans/{uid};有 target_uid 再 GET /targets/{uid} 取標題(取不到退回預設標題)。未完成時每 3 秒重取,完成才載分頁——未完成的摘要是空的,顯示會誤導。
  • 跑了哪幾家:useScanToolsRan = tools.vuln 與 summary.severity 非 null 的交集,依 ['trivy','grype','osv'] 排序;所有欄位與統計都吃它。
  • 元件清單點弱點數:切到弱點明細分頁,呼叫 FindingsTable.showComponent(purl) 加元件篩選。
  • 下載:前端讀 X-Filename 並 decodeURIComponent 當存檔名;讀不到才用 fallback。
  • 錯誤:全域 interceptor 依 error_code 顯示。

11. 背景行為與外部依賴

類型 內容
AI 閘道 FR-122:provider 設定、額度、呼叫紀錄、Prompt Guard
PDF WeasyPrint,主機字型 Noto Sans CJK TC
Word docxtpl 範本 templates/report/report.docx,由 scripts/generate_source_scan_docx_template.py 產生
稽核事件 6192 匯出
socket 無;未完成時輪詢

12. 邊界情況與已知坑

  1. 只跑兩家的舊掃描:osv 欄為 NULL、tools.vuln 只有兩家,畫面照樣兩欄,不可因欄位缺就壞。
  2. 只勾部分工具的掃描:沒勾的那家 summary.severity 為 null、原始結果下載 404。任務掃描沿用建任務時的工具設定,可能因此沒有 OSV。
  3. 分級一致不代表三家看到同一件事:編號寫法、來源頁、分數都可能不同,工具格子會把差異顯示出來。
  4. 任務掃描結果頁沒有歷次比較:任務掃描不掛受評系統。
  5. PDF 中文消失:主機缺 fonts-noto-cjk 時發生,非程式問題。

13. 開發與驗證

  • 導航:第三方元件風險 → 列上 📊;或任務抽屜「查看完整結果」
  • 驗收:三家全勾掃一包 → 弱點明細三欄、嚴重度三欄、展開列三個來源頁、原始檔多一份 OSV;取消「顯示工具」的 Grype → 欄位收掉、筆數不變;匯出三種格式檔名中文正確;開一筆只跑兩家的舊掃描 → 兩欄不報錯
  • 測試資料:DEV 弱點資料在 /opt/vulndb;測試帳號密碼見 .env/部署文件