If you run Trivy or Grype in CI and triage the output by CVSS, this is the thing I wish I’d had two years ago.
Quick recap. Trivy and Grype hand you a list of CVEs. CVSS is a score in a vacuum — it doesn’t know whether a service runs in a private subnet behind mTLS, or sits on the open internet handling payment cards. vens reads your scan output plus a YAML describing the service (exposure, data sensitivity, business criticality, controls, compliance, …), runs every CVE through an LLM with that context, and emits a CycloneDX VEX with OWASP Risk Rating scores. You gate the build on those instead.
vens-action is the GitHub Action wrapper — install, invocation, build gate, packaged as a composite. Here’s the minimum to drop it in.
What you need
- A Trivy or Grype JSON report (you’re probably running one of these already).
- A
.vens/config.yaml. Three context fields are the floor; the full annotated reference is inexamples/quickstart/config.yaml. - An LLM API key — OpenAI, Anthropic, Google, or a self-hosted Ollama.
- The
serialNumberof your CycloneDX SBOM (or an ad-hoc one — see below).
The config
The bare minimum:
project:
name: "checkout-api"
context:
exposure: "internet"
data_sensitivity: "high"
business_criticality: "critical"
For scoring that actually reflects your service, fill in the rest — security controls (WAF, IDS, segmentation, …), compliance requirements, availability target, free-form notes. The annotated reference lives in examples/quickstart/config.yaml. Wrong values → wrong scores, so this file deserves the same review process as the rest of your code (CODEOWNERS, PR review, the works).
The SBOM serial number
vens writes a CycloneDX VEX whose vulnerabilities[].affects[].ref entries are BOM-Link references — they must point back to the serialNumber of the SBOM the scan was produced from.
If you already have a CycloneDX SBOM for the artifact, pull the serial from it:
SBOM_UUID=$(jq -r .serialNumber sbom.cdx.json)
If you don’t have one yet, generate an ad-hoc serial and reuse it across rescans of the same service so the BOM-Link stays stable:
SBOM_UUID="urn:uuid:$(uuidgen | tr '[:upper:]' '[:lower:]')"
Store the value as a repo variable, say vars.SBOM_SERIAL. Flag reference: vens generate --sbom-serial-number.
The workflow
.github/workflows/scan.yml:
name: scan
on: [push]
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy scan
run: trivy image python:3.11-slim --format json --output report.json
- name: vens
id: vens
uses: venslabs/vens-action@v0.1.0
with:
version: v0.3.2
config-file: .vens/config.yaml
input-report: report.json
sbom-serial-number: ${{ vars.SBOM_SERIAL }}
llm-provider: openai
llm-model: gpt-4o
llm-api-key: ${{ secrets.OPENAI_API_KEY }}
fail-on-severity: critical
enrich: "true"
- uses: actions/upload-artifact@v4
with:
name: vens
path: |
${{ steps.vens.outputs.vex-file }}
${{ steps.vens.outputs.enriched-report }}
Each run gives you a CycloneDX VEX (vex-file), your original Trivy report annotated with Custom.owasp_score (enriched-report, when enrich: true), and per-severity counts as step outputs (count-critical, count-high, …). Pipe the counts into dashboards, PR comments, whatever you already do with scan metrics.
fail-on-severity: critical makes the step fail if any CVE comes out CRITICAL by OWASP (score ≥ 60). Drop the line if you just want artifacts and a manual review.
Things you’ll probably want to tweak
-
Self-hosted models.
llm-provider: ollama+llm-base-url: http://ollama.corp:11434. -
Air-gapped runners. Build vens yourself and pass
bin-pathinstead ofversion. Skips the download + checksum step entirely. -
Pin by SHA.
uses: venslabs/vens-action@. Renovate and Dependabot both follow SHA-pinned actions.
Why I bothered building it
CVSS sorts vulnerabilities like a smoke detector that can’t tell if you’re cooking or your kitchen is on fire. A 9.8 on an internal service with no PII and no internet exposure is rarely your urgent problem. A 5.4 on the auth path with cleartext token logging probably is. Your team knows the difference — but a spreadsheet per service doesn’t scale, and most tools that do contextual scoring are paid SaaS with their own opinions.
vens is OSS, Apache 2.0. The action is a thin composite around the CLI. Issues, feedback, PRs — I read them.
- Action: github.com/venslabs/vens-action
- CLI: github.com/venslabs/vens
- Docs: venslabs.github.io/vens