Skip to content

Commit e2fe403

Browse files
committed
Add OSV dependency vulnerability scanning
The SBOM only inventoried dependencies and SARIF only exported findings, so nothing ever produced a vulnerability finding. Match SBOM components against an injected OSV advisory database (introduced/fixed/last_affected range sweep, PEP-503 name normalization, severity to SARIF level) and bridge results into the SARIF exporter for code scanning. Advisories are injected as data so matching is offline and deterministic; the live osv.dev query stays an optional fetcher seam. Wired through the facade, AC_scan_vulns executor command, ac_scan_vulns MCP tool and Script Builder.
1 parent 9074652 commit e2fe403

15 files changed

Lines changed: 557 additions & 2 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
## Table of Contents
1515

16+
- [What's new (2026-06-21) — Dependency Vulnerability Scanning (OSV)](#whats-new-2026-06-21--dependency-vulnerability-scanning-osv)
1617
- [What's new (2026-06-21) — JSON Schema Validation](#whats-new-2026-06-21--json-schema-validation)
1718
- [What's new (2026-06-20) — SARIF 2.1.0 Findings Export](#whats-new-2026-06-20--sarif-210-findings-export)
1819
- [What's new (2026-06-20) — Text PII Detection & Redaction](#whats-new-2026-06-20--text-pii-detection--redaction)
@@ -110,6 +111,12 @@
110111

111112
---
112113

114+
## What's new (2026-06-21) — Dependency Vulnerability Scanning (OSV)
115+
116+
Match the SBOM against known CVEs. Full reference: [`docs/source/Eng/doc/new_features/v58_features_doc.rst`](docs/source/Eng/doc/new_features/v58_features_doc.rst).
117+
118+
- **`scan_components` / `match_package` / `is_affected` / `findings_to_sarif`** (`AC_scan_vulns`, `ac_scan_vulns`): `build_sbom` only *inventoried* dependencies and `to_sarif` only *exported* findings — nothing ever **produced** a vulnerability finding. This matches the SBOM's `(ecosystem, name, version)` components against an [OSV](https://osv.dev) advisory database (sweeping `introduced`/`fixed`/`last_affected` ranges, PEP-503 name normalization, severity→SARIF level) and bridges results into the existing SARIF exporter for GitHub/Azure DevOps code scanning. The advisory DB is **injected as data** (offline, deterministic); the live `osv.dev` query is an optional `fetcher` seam. Pure-stdlib `re`.
119+
113120
## What's new (2026-06-21) — JSON Schema Validation
114121

115122
Validate nested JSON against a real schema. Full reference: [`docs/source/Eng/doc/new_features/v57_features_doc.rst`](docs/source/Eng/doc/new_features/v57_features_doc.rst).

README/README_zh-CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目录
1414

15+
- [本次更新 (2026-06-21) — 依赖项漏洞扫描(OSV)](#本次更新-2026-06-21--依赖项漏洞扫描osv)
1516
- [本次更新 (2026-06-21) — JSON Schema 验证](#本次更新-2026-06-21--json-schema-验证)
1617
- [本次更新 (2026-06-20) — SARIF 2.1.0 发现项目导出](#本次更新-2026-06-20--sarif-210-发现项目导出)
1718
- [本次更新 (2026-06-20) — 文本 PII 检测与遮蔽](#本次更新-2026-06-20--文本-pii-检测与遮蔽)
@@ -109,6 +110,12 @@
109110

110111
---
111112

113+
## 本次更新 (2026-06-21) — 依赖项漏洞扫描(OSV)
114+
115+
以 SBOM 比对已知 CVE。完整参考:[`docs/source/Zh/doc/new_features/v58_features_doc.rst`](../docs/source/Zh/doc/new_features/v58_features_doc.rst)
116+
117+
- **`scan_components` / `match_package` / `is_affected` / `findings_to_sarif`**(`AC_scan_vulns``ac_scan_vulns`):`build_sbom` 只会*盘点*依赖项,`to_sarif` 只会*导出*发现项目 —— 从未真正**产生**漏洞发现项目。本功能以 SBOM 的 `(ecosystem, name, version)` 组件比对 [OSV](https://osv.dev) 咨询数据库(扫描 `introduced`/`fixed`/`last_affected` 范围、PEP-503 名称规范化、严重度对应 SARIF 等级),并把结果桥接到既有 SARIF 导出器供 GitHub/Azure DevOps 代码扫描。咨询数据库以**数据注入**(离线、确定性);线上 `osv.dev` 查询为可选的 `fetcher` 接缝。纯标准库 `re`
118+
112119
## 本次更新 (2026-06-21) — JSON Schema 验证
113120

114121
以真正的 schema 验证嵌套 JSON。完整参考:[`docs/source/Zh/doc/new_features/v57_features_doc.rst`](../docs/source/Zh/doc/new_features/v57_features_doc.rst)

README/README_zh-TW.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
## 目錄
1414

15+
- [本次更新 (2026-06-21) — 相依套件漏洞掃描(OSV)](#本次更新-2026-06-21--相依套件漏洞掃描osv)
1516
- [本次更新 (2026-06-21) — JSON Schema 驗證](#本次更新-2026-06-21--json-schema-驗證)
1617
- [本次更新 (2026-06-20) — SARIF 2.1.0 發現項目匯出](#本次更新-2026-06-20--sarif-210-發現項目匯出)
1718
- [本次更新 (2026-06-20) — 文字 PII 偵測與遮蔽](#本次更新-2026-06-20--文字-pii-偵測與遮蔽)
@@ -109,6 +110,12 @@
109110

110111
---
111112

113+
## 本次更新 (2026-06-21) — 相依套件漏洞掃描(OSV)
114+
115+
以 SBOM 比對已知 CVE。完整參考:[`docs/source/Zh/doc/new_features/v58_features_doc.rst`](../docs/source/Zh/doc/new_features/v58_features_doc.rst)
116+
117+
- **`scan_components` / `match_package` / `is_affected` / `findings_to_sarif`**(`AC_scan_vulns``ac_scan_vulns`):`build_sbom` 只會*盤點*相依套件,`to_sarif` 只會*匯出*發現項目 —— 從未真正**產生**漏洞發現項目。本功能以 SBOM 的 `(ecosystem, name, version)` 元件比對 [OSV](https://osv.dev) 諮詢資料庫(掃描 `introduced`/`fixed`/`last_affected` 範圍、PEP-503 名稱正規化、嚴重度對應 SARIF 等級),並把結果橋接到既有 SARIF 匯出器供 GitHub/Azure DevOps 程式碼掃描。諮詢資料庫以**資料注入**(離線、具決定性);線上 `osv.dev` 查詢為選用的 `fetcher` 接縫。純標準函式庫 `re`
118+
112119
## 本次更新 (2026-06-21) — JSON Schema 驗證
113120

114121
以真正的 schema 驗證巢狀 JSON。完整參考:[`docs/source/Zh/doc/new_features/v57_features_doc.rst`](../docs/source/Zh/doc/new_features/v57_features_doc.rst)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
Dependency Vulnerability Scanning (OSV)
2+
=======================================
3+
4+
``build_sbom`` *inventories* dependencies and ``to_sarif`` *exports* findings,
5+
but nothing in between ever **produced** a vulnerability finding — there was no
6+
advisory matching at all. This closes that loop: given the SBOM's
7+
``(ecosystem, name, version)`` components and an `OSV <https://osv.dev>`_
8+
advisory database, ``scan_components`` reports which packages are affected, and
9+
``findings_to_sarif`` bridges the results straight into the existing SARIF
10+
exporter for GitHub / Azure DevOps code scanning.
11+
12+
The advisory database is **injected as plain data** (a list of OSV records), so
13+
matching is fully offline and deterministic; the live ``osv.dev`` query is a
14+
separate, optional ``fetcher`` seam. Pure standard library (``re``); imports no
15+
``PySide6``.
16+
17+
Headless API
18+
------------
19+
20+
.. code-block:: python
21+
22+
from je_auto_control import (
23+
build_sbom, scan_components, findings_to_sarif, write_sarif)
24+
25+
advisories = [{
26+
"id": "GHSA-foo", "summary": "RCE in foo",
27+
"database_specific": {"severity": "HIGH"},
28+
"affected": [{
29+
"package": {"ecosystem": "PyPI", "name": "foo"},
30+
"ranges": [{"type": "ECOSYSTEM",
31+
"events": [{"introduced": "0"}, {"fixed": "1.2.0"}]}],
32+
}],
33+
}]
34+
35+
sbom = build_sbom("je_auto_control")
36+
findings = scan_components(sbom["components"], advisories)
37+
# findings: [{id, package, version, summary, severity, fixed, aliases}]
38+
write_sarif(findings_to_sarif(findings), "vulns.sarif",
39+
tool_name="AutoControl-VulnScan")
40+
41+
``is_affected(version, osv_range)`` evaluates one OSV range by sweeping its
42+
``introduced`` / ``fixed`` / ``last_affected`` events; ``match_package`` checks
43+
a single package against the database (explicit ``versions`` **or** ranges);
44+
``scan_components`` runs the whole SBOM. Package names are compared with PEP-503
45+
normalization, and the OSV severity word (``CRITICAL`` / ``HIGH`` /
46+
``MODERATE`` / ``LOW``) maps to a SARIF ``error`` / ``warning`` / ``note``
47+
level (defaulting to ``warning``).
48+
49+
Version ordering is a pragmatic numeric comparison: release components compare
50+
as integers and a pre-release suffix (``1.2.0-rc1``) sorts before the final
51+
release. Git ranges and full CVSS-vector scoring are out of scope.
52+
53+
Live advisories (optional)
54+
--------------------------
55+
56+
Pass a ``fetcher`` callable to pull advisories at scan time (e.g. from the
57+
``osv.dev`` API). Tests inject a fake fetcher, so the matching logic is verified
58+
without any network:
59+
60+
.. code-block:: python
61+
62+
findings = scan_components(sbom["components"], None,
63+
fetcher=my_osv_fetcher)
64+
65+
Executor command
66+
----------------
67+
68+
``AC_scan_vulns`` takes ``components`` (a component list, a full SBOM dict, or a
69+
JSON string) and ``advisories`` (a list or JSON string), with an optional
70+
``sarif_path`` to write a SARIF report; it returns ``{findings, count}`` (plus
71+
``sarif_path`` when written). The same operation is exposed as the MCP tool
72+
``ac_scan_vulns`` and as a Script Builder command under **Security**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Comprehensive guides for all AutoControl features.
8080
doc/new_features/v55_features_doc
8181
doc/new_features/v56_features_doc
8282
doc/new_features/v57_features_doc
83+
doc/new_features/v58_features_doc
8384
doc/ocr_backends/ocr_backends_doc
8485
doc/observability/observability_doc
8586
doc/operations_layer/operations_layer_doc
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
相依套件漏洞掃描(OSV)
2+
======================
3+
4+
``build_sbom`` 會*盤點*相依套件,``to_sarif`` 會*匯出*發現項目,但兩者之間從未真正**產生**
5+
漏洞發現項目 —— 完全沒有諮詢比對。本功能補上這個環節:給定 SBOM 的
6+
``(ecosystem, name, version)`` 元件與一份 `OSV <https://osv.dev>`_ 諮詢資料庫,
7+
``scan_components`` 會回報哪些套件受影響,而 ``findings_to_sarif`` 把結果直接橋接到既有的
8+
SARIF 匯出器,供 GitHub / Azure DevOps 程式碼掃描使用。
9+
10+
諮詢資料庫以**純資料注入**(一份 OSV 紀錄清單),因此比對完全離線且具決定性;線上的
11+
``osv.dev`` 查詢則是另一個選用的 ``fetcher`` 接縫。純標準函式庫(``re``);不匯入
12+
``PySide6``。
13+
14+
無頭 API
15+
--------
16+
17+
.. code-block:: python
18+
19+
from je_auto_control import (
20+
build_sbom, scan_components, findings_to_sarif, write_sarif)
21+
22+
advisories = [{
23+
"id": "GHSA-foo", "summary": "RCE in foo",
24+
"database_specific": {"severity": "HIGH"},
25+
"affected": [{
26+
"package": {"ecosystem": "PyPI", "name": "foo"},
27+
"ranges": [{"type": "ECOSYSTEM",
28+
"events": [{"introduced": "0"}, {"fixed": "1.2.0"}]}],
29+
}],
30+
}]
31+
32+
sbom = build_sbom("je_auto_control")
33+
findings = scan_components(sbom["components"], advisories)
34+
# findings: [{id, package, version, summary, severity, fixed, aliases}]
35+
write_sarif(findings_to_sarif(findings), "vulns.sarif",
36+
tool_name="AutoControl-VulnScan")
37+
38+
``is_affected(version, osv_range)`` 透過掃描 ``introduced`` / ``fixed`` /
39+
``last_affected`` 事件來評估單一 OSV 範圍;``match_package`` 以資料庫比對單一套件(明確的
40+
``versions`` **或**範圍);``scan_components`` 跑完整份 SBOM。套件名稱以 PEP-503 正規化後比較,
41+
OSV 嚴重度字詞(``CRITICAL`` / ``HIGH`` / ``MODERATE`` / ``LOW``)對應到 SARIF 的
42+
``error`` / ``warning`` / ``note`` 等級(預設為 ``warning``)。
43+
44+
版本排序採務實的數值比較:release 元件以整數比較,pre-release 後綴(``1.2.0-rc1``)排在最終
45+
release 之前。Git 範圍與完整 CVSS 向量評分不在範圍內。
46+
47+
線上諮詢(選用)
48+
----------------
49+
50+
傳入 ``fetcher`` callable 即可在掃描當下抓取諮詢(例如從 ``osv.dev`` API)。測試注入假的
51+
fetcher,因此比對邏輯不需任何網路即可驗證:
52+
53+
.. code-block:: python
54+
55+
findings = scan_components(sbom["components"], None,
56+
fetcher=my_osv_fetcher)
57+
58+
執行器命令
59+
----------
60+
61+
``AC_scan_vulns`` 接受 ``components``(元件清單、完整 SBOM dict 或 JSON 字串)與
62+
``advisories``(清單或 JSON 字串),並可選用 ``sarif_path`` 寫出 SARIF 報告;回傳
63+
``{findings, count}``(寫檔時另含 ``sarif_path``)。同一操作亦以 MCP 工具 ``ac_scan_vulns``
64+
以及 Script Builder 中 **Security** 分類下的命令提供。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ AutoControl 所有功能的完整使用指南。
8080
doc/new_features/v55_features_doc
8181
doc/new_features/v56_features_doc
8282
doc/new_features/v57_features_doc
83+
doc/new_features/v58_features_doc
8384
doc/ocr_backends/ocr_backends_doc
8485
doc/observability/observability_doc
8586
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@
302302
from je_auto_control.utils.json_schema import (
303303
SchemaValidationResult, assert_schema, is_valid, validate_json,
304304
)
305+
# OSV vulnerability matching for SBOM components
306+
from je_auto_control.utils.vuln_scan import (
307+
findings_to_sarif, is_affected, match_package, scan_components, version_key,
308+
)
305309
# Background popup/interrupt watchdog (unattended automation)
306310
from je_auto_control.utils.watchdog import (
307311
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -772,6 +776,8 @@ def start_autocontrol_gui(*args, **kwargs):
772776
"from_audit_findings", "from_lint_issues", "make_finding", "to_sarif",
773777
"write_sarif",
774778
"SchemaValidationResult", "assert_schema", "is_valid", "validate_json",
779+
"findings_to_sarif", "is_affected", "match_package", "scan_components",
780+
"version_key",
775781
# MCP server
776782
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
777783
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,19 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
11551155
),
11561156
description="Validate JSON against a JSON Schema; returns {ok, errors}.",
11571157
))
1158+
specs.append(CommandSpec(
1159+
"AC_scan_vulns", "Security", "Scan Dependencies for Vulnerabilities",
1160+
fields=(
1161+
FieldSpec("components", FieldType.STRING,
1162+
placeholder='{"components": [{"name": "foo", '
1163+
'"version": "1.0", "purl": "pkg:pypi/foo@1.0"}]}'),
1164+
FieldSpec("advisories", FieldType.STRING,
1165+
placeholder='[{"id": "GHSA-...", "affected": [...]}]'),
1166+
FieldSpec("sarif_path", FieldType.STRING, optional=True,
1167+
placeholder="vulns.sarif"),
1168+
),
1169+
description="Match SBOM components against an OSV advisory database.",
1170+
))
11581171
specs.append(CommandSpec(
11591172
"AC_run_saga", "Flow", "Run Saga (Compensating Rollback)",
11601173
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,28 @@ def _scan_secrets(data: Any) -> Dict[str, Any]:
28632863
return {"findings": scan_secrets(data)}
28642864

28652865

2866+
def _scan_vulns(components: Any, advisories: Any = None,
2867+
sarif_path: Optional[str] = None) -> Dict[str, Any]:
2868+
"""Adapter: match SBOM components against an OSV advisory database."""
2869+
import json
2870+
from je_auto_control.utils.vuln_scan import (
2871+
findings_to_sarif, scan_components)
2872+
if isinstance(components, str):
2873+
components = json.loads(components)
2874+
if isinstance(components, dict):
2875+
components = components.get("components", [])
2876+
if isinstance(advisories, str):
2877+
advisories = json.loads(advisories)
2878+
findings = scan_components(components, advisories or [])
2879+
result: Dict[str, Any] = {"findings": findings, "count": len(findings)}
2880+
if sarif_path:
2881+
from je_auto_control.utils.sarif import write_sarif
2882+
result["sarif_path"] = write_sarif(
2883+
findings_to_sarif(findings), sarif_path,
2884+
tool_name="AutoControl-VulnScan")
2885+
return result
2886+
2887+
28662888
def _generate_sop(actions: List[Any], title: str = "Automation Procedure",
28672889
path: Optional[str] = None) -> Dict[str, Any]:
28682890
"""Adapter: build (or write) a step-by-step SOP from an action list."""
@@ -3642,6 +3664,7 @@ def __init__(self):
36423664
"AC_clip_history_stop": _clip_history_stop,
36433665
"AC_heal_stats": _heal_stats,
36443666
"AC_scan_secrets": _scan_secrets,
3667+
"AC_scan_vulns": _scan_vulns,
36453668
"AC_generate_sop": _generate_sop,
36463669
"AC_tween_drag": _tween_drag,
36473670
"AC_list_plugins": _list_plugins,

0 commit comments

Comments
 (0)