跳轉到

第三方元件風險 功能群總覽

本檔放本功能群多個頁面共用的知識:功能定位、兩個入口、掃描流程、資料模型、權限與授權、共用規則。各頁 spec 引用本檔,只寫自己頁面的差異——改共用邏輯時改這裡一份。

事實基準:2026-10-11 從 FE src/views/source-scan/、src/views/vulndb/、BE api/source_scan/、jedi-source-scan 套件、DEV compliance.source_scan_* 實查掃出

變更紀錄

日期 FR 變更
2026-10-11 FR-134 新功能群:第三方元件風險(軟體組成分析 SCA)——上傳原始碼封存檔或 SBOM 文件,產製 SBOM、以 Trivy/Grype/OSV-Scanner 比對已知弱點;獨立模組與檢測工具兩個入口共用同一個核心

1. 定位

第三方元件風險是軟體組成分析(SCA,Software Composition Analysis):查一套系統用了哪些第三方套件(開源元件)、這些套件有哪些已公開的弱點、多嚴重、有沒有修正版。它只看第三方套件,不檢查程式碼本身——程式碼的安全檢測請用檢測工具裡的 SonarQube。

要解決的問題:顧問輔導客戶時,系統弱點本該由客戶工程師盤點,實務上常被一句「那個不嚴重」帶過,顧問沒有技術背景無從判斷。本功能讓不需要技術背景的顧問自己就能看到系統用了哪些第三方元件、哪些有已知弱點、多嚴重、有沒有修正版,並拿有工具原始輸出背書的報表去對話。

呈現原則(全功能群一致,改動前先讀):

  1. 只列事實,不下結論:畫面與報表只呈現工具輸出與公開來源(NVD、CISA KEV、FIRST EPSS)的事實;不出現「屬高風險」「建議立即修補」這類結論句。顧問依事實自行判斷。
  2. 各工具各自分級、並列不平均:三套工具對同一個弱點的等級或分數可能不同,畫面逐家並列,不取平均、不替使用者選一家。
  3. 資料不足要明講:弱點資料未含 KEV/EPSS、專案缺鎖定檔時,一律標示「無法判定」,不顯示成 0——0 會被讀成「很乾淨」。
  4. 自行設計,不仿競品;未實作的功能不預告——畫面不出現灰掉的入口或「即將推出」字樣。

2. 頁面地圖

頁面 FE route(path) 用途一句話 spec
第三方元件風險(受評系統清單) /source-scan 受評系統清單、新增/重新掃描精靈、任務發起的掃描頁籤、批次刪除與清理失敗紀錄 ✅ 受評系統清單
掃描結果頁 /source-scan/scans/:uid 單次掃描的風險總覽、弱點明細、元件清單三分頁;匯出報表;AI 摘要分析 ✅ 掃描結果頁
歷次比較 /source-scan/targets/:uid/diff 同一受評系統任選兩次掃描比較已修補/新增/仍存在;匯出回訪報告 ✅ 歷次比較
檢測工具入口 無獨立 route(任務配置頁+任務抽屜) 「第三方元件弱點掃描」作為檢測工具:建任務、上傳檔案、執行、結果轉任務證據 ✅ 檢測工具入口
弱點資料庫 /system/vulndb 系統設定:弱點資料日期、上傳更新包、檢查更新、自動更新、更新紀錄 ✅ 弱點資料庫

選單位置:「第三方元件風險」在合規與稽核分組(ui_routes.name='source-scan',sort 40);結果頁與歷次比較是它的子路由(不出現在選單);「弱點資料庫」在系統設定分組(sort 38)。

3. 一個核心、兩個入口

一個核心兩個入口

入口 1:獨立模組 入口 2:檢測工具
從哪進 側選單「第三方元件風險」 任務配置選「第三方元件弱點掃描」工具 → 任務抽屜上傳檔案、執行
被掃的對象 受評系統(source_scan_targets),歷次掃描掛在它底下 不掛受評系統(target_uid 為空),掛在檢測執行列(detection_execution_uid)
結果去哪 結果頁、歷次比較、匯出報表 一樣有結果頁;另外把報表與產物轉成任務證據
清單看得到嗎 「受評系統」頁籤 「任務發起的掃描」頁籤(所屬任務已刪除的不列)
可刪嗎 受評系統可整批刪(連同歷次掃描);單次掃描只能刪失敗的 不能從模組刪,跟著任務走

核心不判斷入口:兩個入口建出同一種掃描列、跑同一支背景工作、產同一種結果。差別只在「誰建掃描列」與「跑完要不要多做一步轉任務證據」(看 detection_execution_uid 是否非空)。

4. 掃描流程(四段進度)

掃描流程四段

段 畫面字樣 做什麼 在哪跑
receive 檔案接收 從檔案庫取出上傳檔;原始碼封存檔做安全檢查(格式、解壓炸彈、路徑穿越、符號連結);SBOM 文件辨識格式 worker
sbom SBOM 產製 Syft 讀鎖定檔產出 SBOM(依選項多格式多版本);上傳的是 SBOM 時顯示「(已提供,略過)」 沙箱
vuln 弱點比對 依勾選跑 Trivy/Grype/OSV-Scanner,全部離線、只讀本機弱點資料 沙箱
report 報告彙整 解析三家結果、跨工具配對、補 KEV/EPSS、算統計與涵蓋度、存 SBOM 與原始結果、寫三張表 worker
  • 沙箱不出網:掃描在 guidant-sandbox 容器內做,容器在 internal 網段、沒有對外路由。弱點資料由系統設定的「弱點資料庫」事先放進唯讀掛載的 volume。這是讓「上傳別人的原始碼」可被接受的前提——原始碼不會被送出去。
  • 只做純靜態解析:讀鎖定檔(package-lock.json、poetry.lock、Gemfile.lock…),不執行建置、不下載相依。缺鎖定檔時版本是估算值、看不到傳遞相依,結果頁「分析涵蓋度」會明講。
  • 沙箱逾時 20 分鐘;任何一段失敗即 failed,錯誤寫進 source_scans.error,不自動重試。
  • 狀態:queued → running → done|failed。前端每 3 秒輪詢進行中的掃描。

5. 三套弱點比對工具

工具 代碼 弱點資料 本系統怎麼處理差異
Trivy trivy Trivy 弱點資料庫 原樣保留等級與 CVSS
Grype grype Grype 弱點資料庫 原樣保留
OSV-Scanner osv OSV 離線庫(每個生態系一包) OSV 只給 CVSS 向量、不給分數,系統用 cvss 套件從向量算出分數與等級;向量缺漏或壞掉時該筆保留、等級標「未分級」。同一 CVE 若 OSV 同時列 GHSA 與 PYSEC 兩筆,留較嚴重那筆
  • 精靈預設三家全勾;可在進階設定取消。這次跑了哪幾家記在 source_scans.tools.vuln,所有畫面(弱點明細欄、嚴重度統計、來源頁列、清單重點數字)與報表都只呈現這次跑了的工具。只跑兩家的舊掃描照樣顯示兩欄。
  • 跨工具配對:match_key=生態系+套件名+版本+正規化弱點編號(GHSA/PYSEC 收斂到 CVE)。同一列裡每家工具各有一格判讀 {reported, id, severity, score, advisory_url};沒報的那家顯示「未報」,沒跑的那家整欄不出現。

6. 資料模型

資料模型

表 一句話 寫入者
compliance.source_scan_targets 受評系統(名稱、委託單位說明、技術窗口、來源類型、最近一次完成的掃描) 獨立模組
compliance.source_scans 一次掃描(狀態、勾選的工具與格式、統計摘要 summary、SBOM 品質、原始結果與 SBOM 檔案指標、AI 摘要) 兩個入口
compliance.source_scan_components SBOM 元件(purl、名稱、版本、生態系、授權、授權類型) 背景工作
compliance.source_scan_findings 弱點列(合併編號、別名、三家判讀、修正版、KEV、EPSS、配對鍵) 背景工作
public.vulndb_updates 弱點資料更新紀錄(整台機器共用,不分租戶) 弱點資料庫頁、安裝程式、自動更新
  • source_scans → source_scan_targets 與 components/findings → source_scans 皆 ON DELETE CASCADE:刪受評系統連同歷次掃描、元件、弱點一起刪。上傳檔與產物檔留在檔案庫,不在同一交易刪。
  • 四張 compliance.* 表都有 tenant_id+org_unit_id,RLS 做租戶隔離;別租戶的資料讀不到,一律 404。
  • 欄位卡見各頁 §9。

summary 形狀(兩個入口共用,結果頁與清單都讀它):

{
  "severity": {"trivy": {"critical": 12, "high": 46, "medium": 50, "low": 12, "negligible": 0, "unknown": 0},
                "grype": {...}, "osv": null},
  "kev":  {"available": true, "count": 1, "items": ["CVE-2025-31125"]},
  "epss": {"available": true, "threshold": 0.1, "count": 3, "items": ["CVE-..."]},
  "fixable": {"with_fix": 118, "without_fix": 2, "oldest_fix_date": null},
  "coverage": {"missing_lock": ["npm"], "ntia": {...}},
  "delta": {"vs_scan_uid": "...", "fixed": 4, "new": 2, "remaining": 8},
  "components": 568, "findings": 121, "vulndb_date": "2026-10-11",
  "tools_disagree": 3
}

severity 裡沒跑的工具是 null;kev/epss 的 available=false 表示弱點資料沒帶那份清單。

7. 權限與授權

7.1 能力點

能力點 給什麼
detection.source-scan.read 進頁、看清單與結果、歷次比較、下載 SBOM/原始結果、按 AI 摘要分析
detection.source-scan.create 新增受評系統(精靈第一頁,與 execute 一起才看得到「新增受評系統」)
detection.source-scan.update 改受評系統基本資料(API 有,畫面目前沒有編輯入口)
detection.source-scan.delete 批次刪受評系統、清理失敗紀錄
detection.source-scan.execute 發起掃描(新增後的首掃、重新掃描)
detection.source-scan.export 匯出報表、匯出回訪報告
system.vulndb.read 看弱點資料庫頁
system.vulndb.update 上傳更新包、檢查更新、自動更新開關

出廠角色(resources/roles/shipped-roles.json):稽核主管 detection.*.*(全部);稽核員 detection.*.read+create/execute/export(不能刪);一般成員與唯讀 detection.*.read。既有租戶的 is_admin 角色由 migration 逐顆補發六顆 detection.source-scan.* 與兩顆 system.vulndb.*。

任務入口不看這組能力點:在任務頁發起掃描是任務操作,守 grc.job.*(見檢測工具入口);任務抽屜的「查看完整結果」鈕才看 detection.source-scan.read。

7.2 授權模組

  • 獨立模組的 API 全部掛 @require_license("plugin")——歸既有「檢測工具包」,沒買的租戶進不來。
  • 弱點資料庫 API 不掛 license:它是基礎設施,不上報價單。
  • 授權到期進唯讀時,寫入按鈕 disabled,tooltip 說明原因(useLicenseReadonly)。

7.3 弱點資料庫是全機共用

弱點資料(/opt/vulndb)整台機器一份,不分租戶。落地版一台一客戶,客戶管理員持 system.vulndb.update 即可更新;SaaS 部署下另要求平台管理員,任一租戶都不准換掉全機共用的資料。

8. 共用規則

  1. 產物檔名:<名稱>_<種類>_<YYYYMMDD-HHMM>.<副檔名>,例如 PrimeVue 前台_弱點掃描報告_20261011-1341.pdf、PrimeVue 前台_SBOM-CycloneDX-1.6_20261011-1341.json、PrimeVue 前台_Trivy原始結果_20261011-1341.json。名稱依序取:受評系統名 → 任務名(沒有就專案名)→ 上傳的壓縮檔名(去副檔名)→ source-scan。名稱去掉 / \ : * ? " < > | 與控制字元、連續空白收成一個、上限 60 字;時間轉 Asia/Taipei。報表、SBOM、原始結果、任務證據四條線共用同一支 artifact_names.py。
  2. 下載標頭:檔名含中文,Content-Disposition 雙寫 filename="<ASCII 退路>"; filename*=UTF-8''<URL 編碼>,X-Filename 放 URL 編碼後的值、前端 decodeURIComponent 還原。檔名直接塞標頭會讓連線在送標頭時中斷、前端永遠轉圈。另帶 nosniff、no-store,HTML 報表一律下載不內嵌。
  3. 外部連結只收 https:弱點來源頁連結來自工具輸出(外部輸入),前端 safeHttpsUrl 過濾。
  4. 弱點資料日期:每次掃描記下當時用的弱點資料日期(vulndb_date)。結果頁以「掃描完成時間 − 資料日期」算資料年齡:超過 30 天黃、超過 90 天紅。
  5. 稽核事件:SOURCE_SCAN_TARGET_CREATED(6190)、SOURCE_SCAN_EXECUTED(6191)、SOURCE_SCAN_EXPORTED(6192)、SOURCE_SCAN_TARGET_DELETED(6193)、VULNDB_UPDATE(6194)、SOURCE_SCAN_DELETED(6195)。

9. 錯誤碼總表

模組前綴 SOURCE_SCAN_;弱點資料庫用 SOURCE_SCAN_4xx1xx 段。定義在 jedi-source-scan common/error_code.py、common/vulndb_error_code.py。

碼 HTTP 情境
SOURCE_SCAN_400001 400 來源類型不是 upload/sbom
SOURCE_SCAN_400002 400 沒附上傳檔
SOURCE_SCAN_400003 400 原始碼封存檔不是 .zip/.tar/.tar.gz/.tgz
SOURCE_SCAN_400004 400 檔案超過上限(封存檔 DETECTION_UPLOAD_MAX_MB,預設 100;SBOM SOURCE_SCAN_SBOM_MAX_MB,預設 50)
SOURCE_SCAN_400005 400 封存檔未通過安全檢查(解壓炸彈、路徑穿越、符號連結等)
SOURCE_SCAN_400006 400 上傳的不是 CycloneDX 或 SPDX
SOURCE_SCAN_400007 400 SBOM 格式或版本不支援
SOURCE_SCAN_400008 400 下載種類不是 sbom/trivy/grype/osv
SOURCE_SCAN_400010 400 報表格式不是 html/pdf/docx
SOURCE_SCAN_400011 400 比較的兩次掃描不屬同一受評系統
SOURCE_SCAN_404001 404 受評系統不存在或不屬本租戶
SOURCE_SCAN_404002 404 掃描不存在或不屬本租戶
SOURCE_SCAN_404003 404 要下載的檔案這次沒產出(例如沒勾 OSV 卻下載 OSV 原始結果)
SOURCE_SCAN_409001 409 受評系統有掃描進行中,不能刪;訊息點名是哪幾套
SOURCE_SCAN_409002 409 要刪的掃描不是失敗的、是任務發起的、或是該系統最近一次完成的;訊息點名
SOURCE_SCAN_412001 412 掃描未完成,不能匯出
SOURCE_SCAN_412002 412 本租戶未設定 AI
SOURCE_SCAN_412003 412 比較的兩次掃描有未完成的
SOURCE_SCAN_429001 429 AI 額度用完
SOURCE_SCAN_502001 502 AI 呼叫失敗
SOURCE_SCAN_400101 400 弱點資料更新包驗證失敗(簽章、逐檔指紋)
SOURCE_SCAN_400102 400 更新包日期沒有比現行資料新
SOURCE_SCAN_400103 400 更新來源值不合法
SOURCE_SCAN_400104 400 分塊上傳有缺段
SOURCE_SCAN_400105 400 上傳完的檔與宣告的 SHA-256 不符
SOURCE_SCAN_400106 400 分塊編號或大小不合法
SOURCE_SCAN_404101 404 上傳工作階段不存在或已過期
SOURCE_SCAN_409101 409 已有另一個弱點資料更新在進行
SOURCE_SCAN_412101 412 未設定弱點資料更新站
SOURCE_SCAN_500101 500 自動更新開關儲存失敗

10. 不在本功能群範圍

Git 來源(含私有倉庫憑證)、建置型相依解析、容器映像掃描、定期重掃與告警、授權條款的法律意見。

11. 相關文件

  • 設計:docs/features/FR-134-2610-source-code-sbom-scan/design.md
  • 使用手冊:第三方元件風險使用指南
  • 安裝與維運:落地部署版手冊(磁碟需求見第一章 1.5、安裝時放入弱點資料見第二章、對外連線見第八章 8.4、弱點資料更新見第十章 10.7)