Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ These workflows analyze the repository, code, and activity to produce reports, i
## Security Workflows

- [🔍 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
- [🔒 VEX Generator](docs/vex-generator.md) - Auto-generate OpenVEX statements for dismissed Dependabot alerts, capturing security assessments in a machine-readable format

## Meta-Workflows

Expand Down
120 changes: 120 additions & 0 deletions docs/vex-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# 🔒 VEX Generator

> For an overview of all available workflows, see the [main README](../README.md).

**Auto-generate OpenVEX statements for dismissed Dependabot alerts**

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.

## Installation

```bash
# Install the 'gh aw' extension
gh extension install github/gh-aw

# Add the workflow to your repository
gh aw add-wizard githubnext/agentics/vex-generator
```

This walks you through adding the workflow to your repository.

## How It Works

```mermaid
graph LR
A[Trigger: workflow_dispatch] --> B[Read Alert Details]
B --> C{Dismissal Reason?}
C -->|not_used / inaccurate / tolerable_risk| D[Map to VEX Status]
C -->|no_bandwidth| E[Skip: Post Comment]
D --> F[Construct Package URL]
F --> G[Generate OpenVEX JSON]
G --> H[Write .vex/<ghsa-id>.json]
H --> I[Open Pull Request]
```

## Usage

### Triggering the Workflow

Run the workflow manually via the GitHub Actions UI or CLI, providing the dismissed alert's details as inputs:

```bash
gh workflow run vex-generator \
--field alert_number=42 \
--field ghsa_id=GHSA-xvch-5gv4-984h \
--field cve_id=CVE-2021-44906 \
--field package_name=minimist \
--field package_ecosystem=npm \
--field severity=high \
--field summary="Prototype pollution in minimist" \
--field dismissed_reason=not_used
```

### Inputs

| Input | Description | Required |
|---|---|---|
| `alert_number` | Dependabot alert number | Yes |
| `ghsa_id` | GHSA ID (e.g., `GHSA-xvch-5gv4-984h`) | Yes |
| `cve_id` | CVE ID (e.g., `CVE-2021-44906`) | Yes |
| `package_name` | Affected package name | Yes |
| `package_ecosystem` | Ecosystem: `npm`, `pip`, `maven`, `gem`, `golang`, `nuget` | Yes |
| `severity` | Severity: `low`, `medium`, `high`, `critical` | Yes |
| `summary` | Brief vulnerability summary | Yes |
| `dismissed_reason` | One of: `not_used`, `inaccurate`, `tolerable_risk`, `no_bandwidth` | Yes |

### Dismissal Reason Mapping

The workflow maps Dependabot dismissal reasons to OpenVEX statuses:

| Dismissal Reason | VEX Status | VEX Justification |
|---|---|---|
| `not_used` | `not_affected` | `vulnerable_code_not_present` |
| `inaccurate` | `not_affected` | `vulnerable_code_not_in_execute_path` |
| `tolerable_risk` | `not_affected` | `inline_mitigations_already_exist` |
| `no_bandwidth` | _(skipped)_ | This dismissal does not represent a security assessment |

### Output

For each eligible dismissal, the workflow creates a pull request adding a `.vex/<ghsa-id>.json` file — a valid OpenVEX v0.2.0 document:

```json
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://github.com/owner/repo/vex/GHSA-xvch-5gv4-984h",
"author": "GitHub Agentic Workflow <vex-generator@github.com>",
"role": "automated-tool",
"timestamp": "2024-01-15T12:00:00Z",
"version": 1,
"tooling": "GitHub Agentic Workflows (gh-aw) VEX Generator",
"statements": [
{
"vulnerability": {
"@id": "GHSA-xvch-5gv4-984h",
"name": "CVE-2021-44906",
"description": "Prototype pollution in minimist"
},
"products": [
{
"@id": "pkg:npm/my-package@1.2.3"
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_present",
"impact_statement": "The minimist package is listed as a dev dependency only and is not included in production builds."
}
]
}
```

### Human in the Loop

- Review the generated VEX statement PR before merging
- Verify the `impact_statement` accurately reflects your project's situation
- The `.vex/README.md` created alongside the statements explains the directory to future contributors

## Learn More

- [OpenVEX Specification](https://openvex.dev/)
- [GitHub Dependabot Alerts Documentation](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)
- [Package URL (purl) Specification](https://github.com/package-url/purl-spec)
207 changes: 207 additions & 0 deletions workflows/vex-generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
---
on:
workflow_dispatch:
inputs:
alert_number:
description: "Dependabot alert number"
required: true
type: string
ghsa_id:
description: "GHSA ID (e.g., GHSA-xvch-5gv4-984h)"
required: true
type: string
cve_id:
description: "CVE ID (e.g., CVE-2021-44906)"
required: true
type: string
package_name:
description: "Affected package name (e.g., minimist)"
required: true
type: string
package_ecosystem:
description: "Package ecosystem (e.g., npm, pip, maven)"
required: true
type: string
severity:
description: "Vulnerability severity (low, medium, high, critical)"
required: true
type: string
summary:
description: "Brief vulnerability summary"
required: true
type: string
dismissed_reason:
description: "Dismissal reason"
required: true
type: choice
options:
- not_used
- inaccurate
- tolerable_risk
- no_bandwidth

description: >
Auto-generates an OpenVEX statement for a dismissed Dependabot alert.
Provide the alert details as inputs — the agent generates a standards-compliant
OpenVEX document and opens a PR.

permissions:
contents: read
issues: read
pull-requests: read

env:
ALERT_NUMBER: ${{ github.event.inputs.alert_number }}
ALERT_GHSA_ID: ${{ github.event.inputs.ghsa_id }}
ALERT_CVE_ID: ${{ github.event.inputs.cve_id }}
ALERT_PACKAGE: ${{ github.event.inputs.package_name }}
ALERT_ECOSYSTEM: ${{ github.event.inputs.package_ecosystem }}
ALERT_SEVERITY: ${{ github.event.inputs.severity }}
ALERT_SUMMARY: ${{ github.event.inputs.summary }}
ALERT_DISMISSED_REASON: ${{ github.event.inputs.dismissed_reason }}

tools:
bash: true
edit:

safe-outputs:
create-pull-request:
title-prefix: "[VEX] "
labels: [vex, automated]
draft: false

engine:
id: copilot
---

# Auto-Generate OpenVEX Statement on Dependabot Alert Dismissal

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.

## Context

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.

The OpenVEX specification: https://openvex.dev/

## Your Task

### Step 1: Get the Dismissed Alert Details

All alert details are available as environment variables. Read them with bash:

```bash
echo "Alert #: $ALERT_NUMBER"
echo "GHSA ID: $ALERT_GHSA_ID"
echo "CVE ID: $ALERT_CVE_ID"
echo "Package: $ALERT_PACKAGE"
echo "Ecosystem: $ALERT_ECOSYSTEM"
echo "Severity: $ALERT_SEVERITY"
echo "Summary: $ALERT_SUMMARY"
echo "Dismissed reason: $ALERT_DISMISSED_REASON"
```

The repository is `${{ github.repository }}`.

Verify all required fields are present before proceeding. Also read the package.json (or equivalent manifest) to get this project's version number.

### Step 2: Map Dismissal Reason to VEX Status

Map the Dependabot dismissal reason to an OpenVEX status and justification:

| Dependabot Dismissal | VEX Status | VEX Justification |
|---|---|---|
| `not_used` | `not_affected` | `vulnerable_code_not_present` |
| `inaccurate` | `not_affected` | `vulnerable_code_not_in_execute_path` |
| `tolerable_risk` | `not_affected` | `inline_mitigations_already_exist` |
| `no_bandwidth` | `under_investigation` | *(none - this is not a VEX-worthy dismissal)* |

**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.

### Step 3: Determine Package URL (purl)

Construct a valid Package URL (purl) for the affected product. The purl format depends on the ecosystem:

- npm: `pkg:npm/<package>@<version>`
- PyPI: `pkg:pypi/<package>@<version>`
- Maven: `pkg:maven/<group>/<artifact>@<version>`
- RubyGems: `pkg:gem/<package>@<version>`
- Go: `pkg:golang/<module>@<version>`
- NuGet: `pkg:nuget/<package>@<version>`

Use the repository's own package version from its manifest file (package.json, setup.py, go.mod, etc.) as the product version.

### Step 4: Generate the OpenVEX Document

Create a valid OpenVEX JSON document following the v0.2.0 specification:

```json
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://github.com/<owner>/<repo>/vex/<ghsa-id>",
"author": "GitHub Agentic Workflow <vex-generator@github.com>",
"role": "automated-tool",
"timestamp": "<current ISO 8601 timestamp>",
"version": 1,
"tooling": "GitHub Agentic Workflows (gh-aw) VEX Generator",
"statements": [
{
"vulnerability": {
"@id": "<GHSA or CVE ID>",
"name": "<CVE ID if available>",
"description": "<brief vulnerability description>"
},
"products": [
{
"@id": "<purl of this package>"
}
],
"status": "<mapped VEX status>",
"justification": "<mapped VEX justification>",
"impact_statement": "<human-readable explanation combining the dismissal reason and any maintainer comment>"
}
]
}
```

### Step 5: Write the VEX File

Save the OpenVEX document to `.vex/<ghsa-id>.json` in the repository.

If the `.vex/` directory doesn't exist yet, create it. Also create or update a `.vex/README.md` explaining the VEX directory:

```markdown
# VEX Statements

This directory contains [OpenVEX](https://openvex.dev/) statements documenting
vulnerabilities that have been assessed and determined to not affect this project.

These statements are auto-generated when Dependabot alerts are dismissed by
maintainers, capturing their security assessment in a machine-readable format.

## Format

Each file is a valid OpenVEX v0.2.0 JSON document that can be consumed by
vulnerability scanners and SBOM tools to reduce false positive alerts for
downstream consumers of this package.
```

### Step 6: Create a Pull Request

Create a pull request with:
- Title: `Add VEX statement for <CVE-ID> (<package name>)`
- Body explaining:
- Which vulnerability was assessed
- The maintainer's dismissal reason
- What VEX status was assigned and why
- A note that this is auto-generated and should be reviewed
- Link to the original Dependabot alert

Use the `create-pull-request` safe output to create the PR.

## Important Notes

- Always validate that the generated JSON is valid before creating the PR
- Use clear, descriptive impact statements — these will be consumed by downstream users
- If multiple alerts are dismissed at once, handle each one individually
- The VEX document should be self-contained and not require external context to understand
Loading