- Introduced `sink-detect.js` with various security sink detection patterns categorized by type (e.g., command injection, SQL injection, file operations). - Implemented functions to build a lookup map for fast sink detection and to match sink calls against known patterns. - Added `package-lock.json` for dependency management.
11 KiB
VexHub Integration Guide
Scope. Integration instructions for consuming VEX statements from VexHub with Trivy, Grype, and other vulnerability scanning tools.
1) Overview
VexHub provides VEX (Vulnerability Exploitability eXchange) statements in OpenVEX format that can be consumed by vulnerability scanners to suppress false positives and reduce noise in scan results. This guide covers integration with:
- Trivy (Aqua Security)
- Grype (Anchore)
- Direct API consumption
2) Prerequisites
- VexHub service running and accessible (default:
http://localhost:5200) - Network access from the scanning tool to VexHub
- (Optional) API key for authenticated access with higher rate limits
3) VexHub Endpoints
Index Manifest
GET /api/v1/vex/index
Returns the VEX index manifest with available sources and statistics:
{
"version": "1.0",
"lastUpdated": "2025-12-22T12:00:00Z",
"sources": ["redhat-csaf", "cisco-csaf", "ubuntu-csaf"],
"totalStatements": 45678,
"endpoints": {
"byCve": "/api/v1/vex/cve/{cve}",
"byPackage": "/api/v1/vex/package/{purl}",
"bulk": "/api/v1/vex/export"
}
}
Bulk Export (OpenVEX)
GET /api/v1/vex/export
Accept: application/vnd.openvex+json
Returns all VEX statements in OpenVEX format. Supports pagination:
GET /api/v1/vex/export?pageSize=1000&pageToken=abc123
Query by CVE
GET /api/v1/vex/cve/{cve-id}
Accept: application/vnd.openvex+json
Example: GET /api/v1/vex/cve/CVE-2024-1234
Query by Package (PURL)
GET /api/v1/vex/package/{purl}
Accept: application/vnd.openvex+json
Example: GET /api/v1/vex/package/pkg%3Anpm%2Fexpress%404.17.1
Note: PURL must be URL-encoded.
4) Trivy Integration
Option A: VEX URL (Recommended)
Trivy 0.48.0+ supports fetching VEX from a URL with the --vex flag:
# Scan container image with VexHub VEX
trivy image --vex https://vexhub.example.com/api/v1/vex/export alpine:3.18
# Scan filesystem with VexHub VEX
trivy fs --vex https://vexhub.example.com/api/v1/vex/export /app
Option B: Local VEX File
Download VEX statements and use locally:
# Download VEX statements
curl -H "Accept: application/vnd.openvex+json" \
https://vexhub.example.com/api/v1/vex/export > vexhub.openvex.json
# Scan with local VEX file
trivy image --vex vexhub.openvex.json alpine:3.18
Option C: VEX Repository
Configure Trivy to use VexHub as a VEX repository in trivy.yaml:
# ~/.trivy.yaml or ./trivy.yaml
vex:
- repository:
url: https://vexhub.example.com/api/v1/vex
Trivy VEX Filtering Behavior
When a VEX statement matches a vulnerability:
| VEX Status | Trivy Behavior |
|---|---|
not_affected |
Vulnerability suppressed from results |
fixed |
Vulnerability shown with fix information |
under_investigation |
Vulnerability shown, marked as under investigation |
affected |
Vulnerability shown as confirmed affected |
Authentication with Trivy
For authenticated access, use environment variables or headers:
# Using environment variable
export TRIVY_VEX_AUTH_HEADER="X-Api-Key: your-api-key-here"
trivy image --vex https://vexhub.example.com/api/v1/vex/export alpine:3.18
# Or download with authentication
curl -H "X-Api-Key: your-api-key-here" \
-H "Accept: application/vnd.openvex+json" \
https://vexhub.example.com/api/v1/vex/export > vexhub.openvex.json
5) Grype Integration
Option A: VEX File
Grype supports VEX via the --vex flag (OpenVEX format):
# Download VEX statements
curl -H "Accept: application/vnd.openvex+json" \
https://vexhub.example.com/api/v1/vex/export > vexhub.openvex.json
# Scan with VEX
grype alpine:3.18 --vex vexhub.openvex.json
Option B: Multiple VEX Files
Grype supports multiple VEX files:
# Download VEX by source
curl "https://vexhub.example.com/api/v1/vex/source/redhat-csaf" > redhat.openvex.json
curl "https://vexhub.example.com/api/v1/vex/source/ubuntu-csaf" > ubuntu.openvex.json
# Scan with multiple VEX files
grype alpine:3.18 --vex redhat.openvex.json --vex ubuntu.openvex.json
Grype VEX Matching
Grype matches VEX statements by:
- CVE ID
- Product identifier (PURL)
- VEX status and justification
When matched, vulnerabilities with not_affected status are filtered from results.
Automated VEX Updates for Grype
Create a script to refresh VEX before scans:
#!/bin/bash
# refresh-vex.sh
VEX_URL="https://vexhub.example.com/api/v1/vex/export"
VEX_FILE="/var/lib/grype/vexhub.openvex.json"
API_KEY="${VEXHUB_API_KEY:-}"
HEADERS=(-H "Accept: application/vnd.openvex+json")
if [ -n "$API_KEY" ]; then
HEADERS+=(-H "X-Api-Key: $API_KEY")
fi
curl -s "${HEADERS[@]}" "$VEX_URL" > "$VEX_FILE.tmp" && \
mv "$VEX_FILE.tmp" "$VEX_FILE"
echo "VEX file updated: $(jq '.statements | length' "$VEX_FILE") statements"
6) API Authentication
VexHub supports API key authentication for increased rate limits and access control.
Rate Limits
| Client Type | Rate Limit (per minute) |
|---|---|
| Anonymous (by IP) | Configured default (e.g., 60) |
| Authenticated (API key) | 2x default (e.g., 120) |
| Custom (per-key config) | As configured |
Passing API Key
Header (recommended):
curl -H "X-Api-Key: your-api-key-here" https://vexhub.example.com/api/v1/vex/export
Query parameter:
curl "https://vexhub.example.com/api/v1/vex/export?api_key=your-api-key-here"
Rate Limit Headers
Responses include rate limit information:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 115
X-RateLimit-Reset: 1703260800
When rate limited, the response is 429 Too Many Requests with Retry-After header.
7) CI/CD Integration
GitHub Actions
name: Security Scan with VEX
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download VEX statements
run: |
curl -H "Accept: application/vnd.openvex+json" \
-H "X-Api-Key: ${{ secrets.VEXHUB_API_KEY }}" \
https://vexhub.example.com/api/v1/vex/export > vexhub.openvex.json
- name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
vex: 'vexhub.openvex.json'
exit-code: '1'
severity: 'CRITICAL,HIGH'
GitLab CI
security_scan:
stage: test
image: aquasec/trivy:latest
script:
- curl -H "Accept: application/vnd.openvex+json"
-H "X-Api-Key: $VEXHUB_API_KEY"
https://vexhub.example.com/api/v1/vex/export > vexhub.openvex.json
- trivy image --vex vexhub.openvex.json --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
variables:
TRIVY_SEVERITY: "CRITICAL,HIGH"
Jenkins Pipeline
pipeline {
agent any
environment {
VEXHUB_URL = 'https://vexhub.example.com/api/v1/vex/export'
}
stages {
stage('Download VEX') {
steps {
withCredentials([string(credentialsId: 'vexhub-api-key', variable: 'API_KEY')]) {
sh '''
curl -H "Accept: application/vnd.openvex+json" \
-H "X-Api-Key: $API_KEY" \
$VEXHUB_URL > vexhub.openvex.json
'''
}
}
}
stage('Security Scan') {
steps {
sh 'trivy image --vex vexhub.openvex.json --exit-code 1 my-app:latest'
}
}
}
}
8) Webhooks for Real-Time Updates
VexHub supports webhooks to notify when new VEX statements are available.
Subscribing to Updates
curl -X POST https://vexhub.example.com/api/v1/webhooks/subscribe \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-api-key" \
-d '{
"url": "https://your-service.example.com/webhook",
"events": ["vex.statement.created", "vex.statement.updated"],
"secret": "your-webhook-secret"
}'
Webhook Payload
{
"event": "vex.statement.created",
"timestamp": "2025-12-22T12:00:00Z",
"data": {
"statementId": "550e8400-e29b-41d4-a716-446655440000",
"vulnerabilityId": "CVE-2024-1234",
"status": "not_affected",
"source": "redhat-csaf"
}
}
Webhook Signature Verification
Webhooks include HMAC-SHA256 signature in X-VexHub-Signature header:
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
9) Troubleshooting
Common Issues
VEX not applied to vulnerabilities:
- Verify PURL format matches exactly
- Check VEX statement
productsfield includes your package - Ensure VEX document format is valid OpenVEX
Rate limit exceeded:
- Use API key authentication for higher limits
- Cache VEX locally and refresh periodically
- Check
Retry-Afterheader for wait time
Authentication failures:
- Verify API key is correct
- Check key has required scopes (
vexhub.read) - Ensure key hasn't expired
Debug Mode
Enable verbose output to troubleshoot:
# Trivy
trivy image --debug --vex https://vexhub.example.com/api/v1/vex/export alpine:3.18
# Grype
GRYPE_LOG_LEVEL=debug grype alpine:3.18 --vex vexhub.openvex.json
Validating VEX Format
Verify VEX document is valid:
curl -s https://vexhub.example.com/api/v1/vex/export | jq '.["@context"]'
# Should output: "https://openvex.dev/ns/v0.2.0"
10) OpenVEX Format Reference
VexHub exports in OpenVEX format. Key fields:
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://vexhub.example.com/vex/550e8400",
"author": "StellaOps VexHub",
"timestamp": "2025-12-22T12:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": {
"@id": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234",
"name": "CVE-2024-1234"
},
"products": [
{
"@id": "pkg:npm/express@4.17.1"
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_present",
"statement": "The vulnerable code path is not included in this package."
}
]
}
Status Values
| Status | Description |
|---|---|
not_affected |
Product not affected by vulnerability |
affected |
Product is affected |
fixed |
Vulnerability has been fixed in this version |
under_investigation |
Impact is being investigated |
Justification Values (for not_affected)
| Justification | Description |
|---|---|
component_not_present |
Vulnerable component not in product |
vulnerable_code_not_present |
Vulnerable code path not included |
vulnerable_code_not_in_execute_path |
Code present but not reachable |
vulnerable_code_cannot_be_controlled_by_adversary |
Attack vector not possible |
inline_mitigations_already_exist |
Mitigations prevent exploitation |
Last updated: 2025-12-22.