diff --git a/README.md b/README.md index da9539a..aeaa4b7 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/vex-generator.md b/docs/vex-generator.md new file mode 100644 index 0000000..1bef5b1 --- /dev/null +++ b/docs/vex-generator.md @@ -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/.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/.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 ", + "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) diff --git a/workflows/vex-generator.md b/workflows/vex-generator.md new file mode 100644 index 0000000..a41360d --- /dev/null +++ b/workflows/vex-generator.md @@ -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/@` +- PyPI: `pkg:pypi/@` +- Maven: `pkg:maven//@` +- RubyGems: `pkg:gem/@` +- Go: `pkg:golang/@` +- NuGet: `pkg:nuget/@` + +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///vex/", + "author": "GitHub Agentic Workflow ", + "role": "automated-tool", + "timestamp": "", + "version": 1, + "tooling": "GitHub Agentic Workflows (gh-aw) VEX Generator", + "statements": [ + { + "vulnerability": { + "@id": "", + "name": "", + "description": "" + }, + "products": [ + { + "@id": "" + } + ], + "status": "", + "justification": "", + "impact_statement": "" + } + ] +} +``` + +### Step 5: Write the VEX File + +Save the OpenVEX document to `.vex/.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 ()` +- 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