Skip to content

Commit 5749cfb

Browse files
committed
Add OpenVEX vulnerability triage
The OSV scanner surfaces every known CVE on every run with no way to record that a vulnerability does not affect the product. Add OpenVEX 0.2.0 authoring (vex_statement/build_vex) and apply_vex, which suppresses not_affected/fixed findings and annotates the rest, joining on the vuln id or an alias with optional product scoping. Composes directly with the OSV scanner; wired through the facade, AC_apply_vex executor command, ac_apply_vex MCP tool and Script Builder.
1 parent 727473b commit 5749cfb

15 files changed

Lines changed: 436 additions & 1 deletion

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) — OpenVEX Vulnerability Triage](#whats-new-2026-06-21--openvex-vulnerability-triage)
1617
- [What's new (2026-06-21) — Dependency Vulnerability Scanning (OSV)](#whats-new-2026-06-21--dependency-vulnerability-scanning-osv)
1718
- [What's new (2026-06-21) — JSON Schema Validation](#whats-new-2026-06-21--json-schema-validation)
1819
- [What's new (2026-06-20) — SARIF 2.1.0 Findings Export](#whats-new-2026-06-20--sarif-210-findings-export)
@@ -111,6 +112,12 @@
111112

112113
---
113114

115+
## What's new (2026-06-21) — OpenVEX Vulnerability Triage
116+
117+
Suppress the vulns that don't affect you. Full reference: [`docs/source/Eng/doc/new_features/v59_features_doc.rst`](docs/source/Eng/doc/new_features/v59_features_doc.rst).
118+
119+
- **`vex_statement` / `build_vex` / `apply_vex`** (`AC_apply_vex`, `ac_apply_vex`): the OSV scanner surfaces every known CVE forever — there was no way to record "we checked, this one doesn't affect us". This authors [OpenVEX](https://openvex.dev) 0.2.0 statements and applies them to the scanner's findings: `not_affected`/`fixed` **suppress** a finding, `affected`/`under_investigation` **annotate** it. Statements join on the vuln id *or* an alias, optionally product-scoped; `not_affected` requires a justification or impact statement. Pure-stdlib; chains directly after `AC_scan_vulns`.
120+
114121
## What's new (2026-06-21) — Dependency Vulnerability Scanning (OSV)
115122

116123
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).

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) — OpenVEX 漏洞分级](#本次更新-2026-06-21--openvex-漏洞分级)
1516
- [本次更新 (2026-06-21) — 依赖项漏洞扫描(OSV)](#本次更新-2026-06-21--依赖项漏洞扫描osv)
1617
- [本次更新 (2026-06-21) — JSON Schema 验证](#本次更新-2026-06-21--json-schema-验证)
1718
- [本次更新 (2026-06-20) — SARIF 2.1.0 发现项目导出](#本次更新-2026-06-20--sarif-210-发现项目导出)
@@ -110,6 +111,12 @@
110111

111112
---
112113

114+
## 本次更新 (2026-06-21) — OpenVEX 漏洞分级
115+
116+
抑制不影响你的漏洞。完整参考:[`docs/source/Zh/doc/new_features/v59_features_doc.rst`](../docs/source/Zh/doc/new_features/v59_features_doc.rst)
117+
118+
- **`vex_statement` / `build_vex` / `apply_vex`**(`AC_apply_vex``ac_apply_vex`):OSV 扫描器会让每个已知 CVE 一直出现 —— 没有办法记录「我们查过了,这个不影响我们」。本功能撰写 [OpenVEX](https://openvex.dev) 0.2.0 陈述并套用到扫描器的发现项目:`not_affected`/`fixed` **抑制**一项发现,`affected`/`under_investigation` **标注**它。陈述以漏洞 id **别名配对,并可限定产品;`not_affected` 需附理由或冲击说明。纯标准库;可直接接在 `AC_scan_vulns` 之后。
119+
113120
## 本次更新 (2026-06-21) — 依赖项漏洞扫描(OSV)
114121

115122
以 SBOM 比对已知 CVE。完整参考:[`docs/source/Zh/doc/new_features/v58_features_doc.rst`](../docs/source/Zh/doc/new_features/v58_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) — OpenVEX 漏洞分級](#本次更新-2026-06-21--openvex-漏洞分級)
1516
- [本次更新 (2026-06-21) — 相依套件漏洞掃描(OSV)](#本次更新-2026-06-21--相依套件漏洞掃描osv)
1617
- [本次更新 (2026-06-21) — JSON Schema 驗證](#本次更新-2026-06-21--json-schema-驗證)
1718
- [本次更新 (2026-06-20) — SARIF 2.1.0 發現項目匯出](#本次更新-2026-06-20--sarif-210-發現項目匯出)
@@ -110,6 +111,12 @@
110111

111112
---
112113

114+
## 本次更新 (2026-06-21) — OpenVEX 漏洞分級
115+
116+
抑制不影響你的漏洞。完整參考:[`docs/source/Zh/doc/new_features/v59_features_doc.rst`](../docs/source/Zh/doc/new_features/v59_features_doc.rst)
117+
118+
- **`vex_statement` / `build_vex` / `apply_vex`**(`AC_apply_vex``ac_apply_vex`):OSV 掃描器會讓每個已知 CVE 一直出現 —— 沒有辦法記錄「我們查過了,這個不影響我們」。本功能撰寫 [OpenVEX](https://openvex.dev) 0.2.0 陳述並套用到掃描器的發現項目:`not_affected`/`fixed` **抑制**一項發現,`affected`/`under_investigation` **標註**它。陳述以漏洞 id **別名配對,並可限定產品;`not_affected` 需附理由或衝擊說明。純標準函式庫;可直接接在 `AC_scan_vulns` 之後。
119+
113120
## 本次更新 (2026-06-21) — 相依套件漏洞掃描(OSV)
114121

115122
以 SBOM 比對已知 CVE。完整參考:[`docs/source/Zh/doc/new_features/v58_features_doc.rst`](../docs/source/Zh/doc/new_features/v58_features_doc.rst)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
OpenVEX Vulnerability Triage
2+
============================
3+
4+
``scan_components`` (the OSV matcher) produces vulnerability findings, but every
5+
known CVE then shows up on every run forever — there was no way to record "we
6+
looked, this one does not affect us" and drop it. VEX (Vulnerability
7+
Exploitability eXchange) is the standard for exactly that triage signal. This
8+
authors `OpenVEX <https://openvex.dev>`_ 0.2.0 statements and applies them to
9+
the scanner's findings.
10+
11+
``not_affected`` / ``fixed`` statements **suppress** a finding; ``affected`` /
12+
``under_investigation`` **annotate** it with the assessed status. Statements
13+
join to findings on the vulnerability id *or* any of its aliases, optionally
14+
scoped to a product. Pure standard library (``hashlib`` + ``json`` +
15+
``datetime``); imports no ``PySide6``.
16+
17+
Headless API
18+
------------
19+
20+
.. code-block:: python
21+
22+
from je_auto_control import (
23+
scan_components, vex_statement, build_vex, apply_vex)
24+
25+
findings = scan_components(sbom["components"], advisories)
26+
27+
statements = [
28+
vex_statement("CVE-2024-1", "not_affected",
29+
products=["pkg:pypi/foo"],
30+
justification="vulnerable_code_not_present"),
31+
vex_statement("GHSA-bar", "under_investigation"),
32+
]
33+
vex = build_vex(statements, author="security@example.com")
34+
35+
triaged = apply_vex(findings, vex)
36+
# CVE-2024-1 (not_affected) dropped; GHSA-bar kept with vex_status set
37+
38+
``vex_statement`` validates the inputs: ``status`` must be one of
39+
``VEX_STATUSES`` (``not_affected`` / ``affected`` / ``fixed`` /
40+
``under_investigation``); a ``not_affected`` statement must carry a
41+
``justification`` (one of ``VEX_JUSTIFICATIONS``) or an ``impact_statement``.
42+
``build_vex`` wraps statements in an OpenVEX document (pass an explicit
43+
``timestamp`` for a reproducible ``@id``). ``apply_vex`` returns the surviving
44+
findings, each non-suppressed match annotated with ``vex_status``.
45+
46+
Executor command
47+
----------------
48+
49+
``AC_apply_vex`` takes ``findings`` and a ``vex`` document (each a list/object
50+
or a JSON string) and returns ``{findings, count}`` of the survivors. The same
51+
operation is exposed as the MCP tool ``ac_apply_vex`` and as a Script Builder
52+
command under **Security**. It chains directly after ``AC_scan_vulns``.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Comprehensive guides for all AutoControl features.
8181
doc/new_features/v56_features_doc
8282
doc/new_features/v57_features_doc
8383
doc/new_features/v58_features_doc
84+
doc/new_features/v59_features_doc
8485
doc/ocr_backends/ocr_backends_doc
8586
doc/observability/observability_doc
8687
doc/operations_layer/operations_layer_doc
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
OpenVEX 漏洞分級
2+
===============
3+
4+
``scan_components``(OSV 比對器)會產生漏洞發現項目,但之後每次執行每個已知 CVE 都會一直出現
5+
—— 沒有辦法記錄「我們看過了,這個不影響我們」並把它捨棄。VEX(Vulnerability Exploitability
6+
eXchange)正是這個分級訊號的標準。本功能撰寫 `OpenVEX <https://openvex.dev>`_ 0.2.0 陳述並
7+
套用到掃描器的發現項目上。
8+
9+
``not_affected`` / ``fixed`` 陳述會**抑制**一項發現;``affected`` / ``under_investigation``
10+
則以評估後的狀態**標註**它。陳述以漏洞 id *或*其任一別名與發現項目配對,並可選擇性地限定到某個
11+
產品。純標準函式庫(``hashlib`` + ``json`` + ``datetime``);不匯入 ``PySide6``。
12+
13+
無頭 API
14+
--------
15+
16+
.. code-block:: python
17+
18+
from je_auto_control import (
19+
scan_components, vex_statement, build_vex, apply_vex)
20+
21+
findings = scan_components(sbom["components"], advisories)
22+
23+
statements = [
24+
vex_statement("CVE-2024-1", "not_affected",
25+
products=["pkg:pypi/foo"],
26+
justification="vulnerable_code_not_present"),
27+
vex_statement("GHSA-bar", "under_investigation"),
28+
]
29+
vex = build_vex(statements, author="security@example.com")
30+
31+
triaged = apply_vex(findings, vex)
32+
# CVE-2024-1(not_affected)被捨棄;GHSA-bar 保留並設定 vex_status
33+
34+
``vex_statement`` 會驗證輸入:``status`` 必須是 ``VEX_STATUSES`` 之一(``not_affected`` /
35+
``affected`` / ``fixed`` / ``under_investigation``);``not_affected`` 陳述必須帶有
36+
``justification``(``VEX_JUSTIFICATIONS`` 其一)或 ``impact_statement``。``build_vex`` 把
37+
陳述包成 OpenVEX 文件(傳入明確的 ``timestamp`` 可得到可重現的 ``@id``)。``apply_vex`` 回傳
38+
存活的發現項目,每個未被抑制的配對都會標註 ``vex_status``。
39+
40+
執行器命令
41+
----------
42+
43+
``AC_apply_vex`` 接受 ``findings`` 與一份 ``vex`` 文件(各為 list/object 或 JSON 字串),回傳
44+
存活者的 ``{findings, count}``。同一操作亦以 MCP 工具 ``ac_apply_vex`` 以及 Script Builder
45+
中 **Security** 分類下的命令提供。它可直接接在 ``AC_scan_vulns`` 之後。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ AutoControl 所有功能的完整使用指南。
8181
doc/new_features/v56_features_doc
8282
doc/new_features/v57_features_doc
8383
doc/new_features/v58_features_doc
84+
doc/new_features/v59_features_doc
8485
doc/ocr_backends/ocr_backends_doc
8586
doc/observability/observability_doc
8687
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
@@ -306,6 +306,10 @@
306306
from je_auto_control.utils.vuln_scan import (
307307
findings_to_sarif, is_affected, match_package, scan_components, version_key,
308308
)
309+
# OpenVEX triage over vulnerability findings
310+
from je_auto_control.utils.vex import (
311+
VEX_JUSTIFICATIONS, VEX_STATUSES, apply_vex, build_vex, vex_statement,
312+
)
309313
# Background popup/interrupt watchdog (unattended automation)
310314
from je_auto_control.utils.watchdog import (
311315
PopupWatchdog, WatchdogRule, default_popup_watchdog,
@@ -778,6 +782,8 @@ def start_autocontrol_gui(*args, **kwargs):
778782
"SchemaValidationResult", "assert_schema", "is_valid", "validate_json",
779783
"findings_to_sarif", "is_affected", "match_package", "scan_components",
780784
"version_key",
785+
"VEX_JUSTIFICATIONS", "VEX_STATUSES", "apply_vex", "build_vex",
786+
"vex_statement",
781787
# MCP server
782788
"AuditLogger", "HttpMCPServer", "MCPContent", "MCPPrompt",
783789
"MCPPromptArgument", "MCPResource", "MCPServer", "MCPTool",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,17 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
11681168
),
11691169
description="Match SBOM components against an OSV advisory database.",
11701170
))
1171+
specs.append(CommandSpec(
1172+
"AC_apply_vex", "Security", "Apply VEX Triage to Findings",
1173+
fields=(
1174+
FieldSpec("findings", FieldType.STRING,
1175+
placeholder='[{"id": "GHSA-...", "package": "foo"}]'),
1176+
FieldSpec("vex", FieldType.STRING,
1177+
placeholder='{"statements": [{"vulnerability": '
1178+
'{"name": "CVE-..."}, "status": "not_affected"}]}'),
1179+
),
1180+
description="Suppress not_affected/fixed vulns via an OpenVEX document.",
1181+
))
11711182
specs.append(CommandSpec(
11721183
"AC_run_saga", "Flow", "Run Saga (Compensating Rollback)",
11731184
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,18 @@ def _scan_vulns(components: Any, advisories: Any = None,
28852885
return result
28862886

28872887

2888+
def _apply_vex(findings: Any, vex: Any) -> Dict[str, Any]:
2889+
"""Adapter: suppress VEX'd vulnerability findings (each JSON string/obj)."""
2890+
import json
2891+
from je_auto_control.utils.vex import apply_vex
2892+
if isinstance(findings, str):
2893+
findings = json.loads(findings)
2894+
if isinstance(vex, str):
2895+
vex = json.loads(vex)
2896+
kept = apply_vex(findings, vex)
2897+
return {"findings": kept, "count": len(kept)}
2898+
2899+
28882900
def _generate_sop(actions: List[Any], title: str = "Automation Procedure",
28892901
path: Optional[str] = None) -> Dict[str, Any]:
28902902
"""Adapter: build (or write) a step-by-step SOP from an action list."""
@@ -3665,6 +3677,7 @@ def __init__(self):
36653677
"AC_heal_stats": _heal_stats,
36663678
"AC_scan_secrets": _scan_secrets,
36673679
"AC_scan_vulns": _scan_vulns,
3680+
"AC_apply_vex": _apply_vex,
36683681
"AC_generate_sop": _generate_sop,
36693682
"AC_tween_drag": _tween_drag,
36703683
"AC_list_plugins": _list_plugins,

0 commit comments

Comments
 (0)