Skip to content

Commit d88ca0e

Browse files
authored
Add vex-generator agentic workflow (#290)
1 parent ee86d0f commit d88ca0e

3 files changed

Lines changed: 328 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ These workflows analyze the repository, code, and activity to produce reports, i
8787
## Security Workflows
8888

8989
- [🔍 Daily Malicious Code Scan](docs/daily-malicious-code-scan.md) - Daily scan of recent code changes for suspicious patterns indicating malicious activity or supply chain attacks
90+
- [🔒 VEX Generator](docs/vex-generator.md) - Auto-generate OpenVEX statements for dismissed Dependabot alerts, capturing security assessments in a machine-readable format
9091

9192
## Meta-Workflows
9293

docs/vex-generator.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# 🔒 VEX Generator
2+
3+
> For an overview of all available workflows, see the [main README](../README.md).
4+
5+
**Auto-generate OpenVEX statements for dismissed Dependabot alerts**
6+
7+
The [VEX Generator workflow](../workflows/vex-generator.md?plain=1) captures Dependabot alert dismissal decisions as machine-readable [OpenVEX v0.2.0](https://openvex.dev/) statements, making them consumable by downstream vulnerability scanners and SBOM tools.
8+
9+
## Installation
10+
11+
```bash
12+
# Install the 'gh aw' extension
13+
gh extension install github/gh-aw
14+
15+
# Add the workflow to your repository
16+
gh aw add-wizard githubnext/agentics/vex-generator
17+
```
18+
19+
This walks you through adding the workflow to your repository.
20+
21+
## How It Works
22+
23+
```mermaid
24+
graph LR
25+
A[Trigger: workflow_dispatch] --> B[Read Alert Details]
26+
B --> C{Dismissal Reason?}
27+
C -->|not_used / inaccurate / tolerable_risk| D[Map to VEX Status]
28+
C -->|no_bandwidth| E[Skip: Post Comment]
29+
D --> F[Construct Package URL]
30+
F --> G[Generate OpenVEX JSON]
31+
G --> H[Write .vex/<ghsa-id>.json]
32+
H --> I[Open Pull Request]
33+
```
34+
35+
## Usage
36+
37+
### Triggering the Workflow
38+
39+
Run the workflow manually via the GitHub Actions UI or CLI, providing the dismissed alert's details as inputs:
40+
41+
```bash
42+
gh workflow run vex-generator \
43+
--field alert_number=42 \
44+
--field ghsa_id=GHSA-xvch-5gv4-984h \
45+
--field cve_id=CVE-2021-44906 \
46+
--field package_name=minimist \
47+
--field package_ecosystem=npm \
48+
--field severity=high \
49+
--field summary="Prototype pollution in minimist" \
50+
--field dismissed_reason=not_used
51+
```
52+
53+
### Inputs
54+
55+
| Input | Description | Required |
56+
|---|---|---|
57+
| `alert_number` | Dependabot alert number | Yes |
58+
| `ghsa_id` | GHSA ID (e.g., `GHSA-xvch-5gv4-984h`) | Yes |
59+
| `cve_id` | CVE ID (e.g., `CVE-2021-44906`) | Yes |
60+
| `package_name` | Affected package name | Yes |
61+
| `package_ecosystem` | Ecosystem: `npm`, `pip`, `maven`, `gem`, `golang`, `nuget` | Yes |
62+
| `severity` | Severity: `low`, `medium`, `high`, `critical` | Yes |
63+
| `summary` | Brief vulnerability summary | Yes |
64+
| `dismissed_reason` | One of: `not_used`, `inaccurate`, `tolerable_risk`, `no_bandwidth` | Yes |
65+
66+
### Dismissal Reason Mapping
67+
68+
The workflow maps Dependabot dismissal reasons to OpenVEX statuses:
69+
70+
| Dismissal Reason | VEX Status | VEX Justification |
71+
|---|---|---|
72+
| `not_used` | `not_affected` | `vulnerable_code_not_present` |
73+
| `inaccurate` | `not_affected` | `vulnerable_code_not_in_execute_path` |
74+
| `tolerable_risk` | `not_affected` | `inline_mitigations_already_exist` |
75+
| `no_bandwidth` | _(skipped)_ | This dismissal does not represent a security assessment |
76+
77+
### Output
78+
79+
For each eligible dismissal, the workflow creates a pull request adding a `.vex/<ghsa-id>.json` file — a valid OpenVEX v0.2.0 document:
80+
81+
```json
82+
{
83+
"@context": "https://openvex.dev/ns/v0.2.0",
84+
"@id": "https://github.com/owner/repo/vex/GHSA-xvch-5gv4-984h",
85+
"author": "GitHub Agentic Workflow <vex-generator@github.com>",
86+
"role": "automated-tool",
87+
"timestamp": "2024-01-15T12:00:00Z",
88+
"version": 1,
89+
"tooling": "GitHub Agentic Workflows (gh-aw) VEX Generator",
90+
"statements": [
91+
{
92+
"vulnerability": {
93+
"@id": "GHSA-xvch-5gv4-984h",
94+
"name": "CVE-2021-44906",
95+
"description": "Prototype pollution in minimist"
96+
},
97+
"products": [
98+
{
99+
"@id": "pkg:npm/my-package@1.2.3"
100+
}
101+
],
102+
"status": "not_affected",
103+
"justification": "vulnerable_code_not_present",
104+
"impact_statement": "The minimist package is listed as a dev dependency only and is not included in production builds."
105+
}
106+
]
107+
}
108+
```
109+
110+
### Human in the Loop
111+
112+
- Review the generated VEX statement PR before merging
113+
- Verify the `impact_statement` accurately reflects your project's situation
114+
- The `.vex/README.md` created alongside the statements explains the directory to future contributors
115+
116+
## Learn More
117+
118+
- [OpenVEX Specification](https://openvex.dev/)
119+
- [GitHub Dependabot Alerts Documentation](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)
120+
- [Package URL (purl) Specification](https://github.com/package-url/purl-spec)

workflows/vex-generator.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
alert_number:
6+
description: "Dependabot alert number"
7+
required: true
8+
type: string
9+
ghsa_id:
10+
description: "GHSA ID (e.g., GHSA-xvch-5gv4-984h)"
11+
required: true
12+
type: string
13+
cve_id:
14+
description: "CVE ID (e.g., CVE-2021-44906)"
15+
required: true
16+
type: string
17+
package_name:
18+
description: "Affected package name (e.g., minimist)"
19+
required: true
20+
type: string
21+
package_ecosystem:
22+
description: "Package ecosystem (e.g., npm, pip, maven)"
23+
required: true
24+
type: string
25+
severity:
26+
description: "Vulnerability severity (low, medium, high, critical)"
27+
required: true
28+
type: string
29+
summary:
30+
description: "Brief vulnerability summary"
31+
required: true
32+
type: string
33+
dismissed_reason:
34+
description: "Dismissal reason"
35+
required: true
36+
type: choice
37+
options:
38+
- not_used
39+
- inaccurate
40+
- tolerable_risk
41+
- no_bandwidth
42+
43+
description: >
44+
Auto-generates an OpenVEX statement for a dismissed Dependabot alert.
45+
Provide the alert details as inputs — the agent generates a standards-compliant
46+
OpenVEX document and opens a PR.
47+
48+
permissions:
49+
contents: read
50+
issues: read
51+
pull-requests: read
52+
53+
env:
54+
ALERT_NUMBER: ${{ github.event.inputs.alert_number }}
55+
ALERT_GHSA_ID: ${{ github.event.inputs.ghsa_id }}
56+
ALERT_CVE_ID: ${{ github.event.inputs.cve_id }}
57+
ALERT_PACKAGE: ${{ github.event.inputs.package_name }}
58+
ALERT_ECOSYSTEM: ${{ github.event.inputs.package_ecosystem }}
59+
ALERT_SEVERITY: ${{ github.event.inputs.severity }}
60+
ALERT_SUMMARY: ${{ github.event.inputs.summary }}
61+
ALERT_DISMISSED_REASON: ${{ github.event.inputs.dismissed_reason }}
62+
63+
tools:
64+
bash: true
65+
edit:
66+
67+
safe-outputs:
68+
create-pull-request:
69+
title-prefix: "[VEX] "
70+
labels: [vex, automated]
71+
draft: false
72+
73+
engine:
74+
id: copilot
75+
---
76+
77+
# Auto-Generate OpenVEX Statement on Dependabot Alert Dismissal
78+
79+
You are a security automation agent. When a Dependabot alert is dismissed, you generate a standards-compliant OpenVEX statement documenting why the vulnerability does not affect this project.
80+
81+
## Context
82+
83+
VEX (Vulnerability Exploitability eXchange) is a standard for communicating that a software product is NOT affected by a known vulnerability. When maintainers dismiss Dependabot alerts, they're making exactly this kind of assessment — but today that knowledge is lost. This workflow captures it in a machine-readable format.
84+
85+
The OpenVEX specification: https://openvex.dev/
86+
87+
## Your Task
88+
89+
### Step 1: Get the Dismissed Alert Details
90+
91+
All alert details are available as environment variables. Read them with bash:
92+
93+
```bash
94+
echo "Alert #: $ALERT_NUMBER"
95+
echo "GHSA ID: $ALERT_GHSA_ID"
96+
echo "CVE ID: $ALERT_CVE_ID"
97+
echo "Package: $ALERT_PACKAGE"
98+
echo "Ecosystem: $ALERT_ECOSYSTEM"
99+
echo "Severity: $ALERT_SEVERITY"
100+
echo "Summary: $ALERT_SUMMARY"
101+
echo "Dismissed reason: $ALERT_DISMISSED_REASON"
102+
```
103+
104+
The repository is `${{ github.repository }}`.
105+
106+
Verify all required fields are present before proceeding. Also read the package.json (or equivalent manifest) to get this project's version number.
107+
108+
### Step 2: Map Dismissal Reason to VEX Status
109+
110+
Map the Dependabot dismissal reason to an OpenVEX status and justification:
111+
112+
| Dependabot Dismissal | VEX Status | VEX Justification |
113+
|---|---|---|
114+
| `not_used` | `not_affected` | `vulnerable_code_not_present` |
115+
| `inaccurate` | `not_affected` | `vulnerable_code_not_in_execute_path` |
116+
| `tolerable_risk` | `not_affected` | `inline_mitigations_already_exist` |
117+
| `no_bandwidth` | `under_investigation` | *(none - this is not a VEX-worthy dismissal)* |
118+
119+
**Important**: If the dismissal reason is `no_bandwidth`, do NOT generate a VEX statement. Instead, skip and post a comment explaining that "no_bandwidth" dismissals don't represent a security assessment and therefore shouldn't generate VEX statements.
120+
121+
### Step 3: Determine Package URL (purl)
122+
123+
Construct a valid Package URL (purl) for the affected product. The purl format depends on the ecosystem:
124+
125+
- npm: `pkg:npm/<package>@<version>`
126+
- PyPI: `pkg:pypi/<package>@<version>`
127+
- Maven: `pkg:maven/<group>/<artifact>@<version>`
128+
- RubyGems: `pkg:gem/<package>@<version>`
129+
- Go: `pkg:golang/<module>@<version>`
130+
- NuGet: `pkg:nuget/<package>@<version>`
131+
132+
Use the repository's own package version from its manifest file (package.json, setup.py, go.mod, etc.) as the product version.
133+
134+
### Step 4: Generate the OpenVEX Document
135+
136+
Create a valid OpenVEX JSON document following the v0.2.0 specification:
137+
138+
```json
139+
{
140+
"@context": "https://openvex.dev/ns/v0.2.0",
141+
"@id": "https://github.com/<owner>/<repo>/vex/<ghsa-id>",
142+
"author": "GitHub Agentic Workflow <vex-generator@github.com>",
143+
"role": "automated-tool",
144+
"timestamp": "<current ISO 8601 timestamp>",
145+
"version": 1,
146+
"tooling": "GitHub Agentic Workflows (gh-aw) VEX Generator",
147+
"statements": [
148+
{
149+
"vulnerability": {
150+
"@id": "<GHSA or CVE ID>",
151+
"name": "<CVE ID if available>",
152+
"description": "<brief vulnerability description>"
153+
},
154+
"products": [
155+
{
156+
"@id": "<purl of this package>"
157+
}
158+
],
159+
"status": "<mapped VEX status>",
160+
"justification": "<mapped VEX justification>",
161+
"impact_statement": "<human-readable explanation combining the dismissal reason and any maintainer comment>"
162+
}
163+
]
164+
}
165+
```
166+
167+
### Step 5: Write the VEX File
168+
169+
Save the OpenVEX document to `.vex/<ghsa-id>.json` in the repository.
170+
171+
If the `.vex/` directory doesn't exist yet, create it. Also create or update a `.vex/README.md` explaining the VEX directory:
172+
173+
```markdown
174+
# VEX Statements
175+
176+
This directory contains [OpenVEX](https://openvex.dev/) statements documenting
177+
vulnerabilities that have been assessed and determined to not affect this project.
178+
179+
These statements are auto-generated when Dependabot alerts are dismissed by
180+
maintainers, capturing their security assessment in a machine-readable format.
181+
182+
## Format
183+
184+
Each file is a valid OpenVEX v0.2.0 JSON document that can be consumed by
185+
vulnerability scanners and SBOM tools to reduce false positive alerts for
186+
downstream consumers of this package.
187+
```
188+
189+
### Step 6: Create a Pull Request
190+
191+
Create a pull request with:
192+
- Title: `Add VEX statement for <CVE-ID> (<package name>)`
193+
- Body explaining:
194+
- Which vulnerability was assessed
195+
- The maintainer's dismissal reason
196+
- What VEX status was assigned and why
197+
- A note that this is auto-generated and should be reviewed
198+
- Link to the original Dependabot alert
199+
200+
Use the `create-pull-request` safe output to create the PR.
201+
202+
## Important Notes
203+
204+
- Always validate that the generated JSON is valid before creating the PR
205+
- Use clear, descriptive impact statements — these will be consumed by downstream users
206+
- If multiple alerts are dismissed at once, handle each one individually
207+
- The VEX document should be self-contained and not require external context to understand

0 commit comments

Comments
 (0)