feat: add security sink detection patterns for JavaScript/TypeScript
- 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.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# Verdict Manifest Specification
|
||||
|
||||
> **Status**: Draft (Sprint 7100)
|
||||
> **Status**: Implementation Complete (Sprint 7100)
|
||||
> **Version**: 1.0.0
|
||||
> **Last Updated**: 2025-12-22
|
||||
> **Source Advisory**: `docs/product-advisories/archived/22-Dec-2026 - Building a Trust Lattice for VEX Sources.md`
|
||||
|
||||
@@ -454,9 +455,44 @@ Content-Disposition: attachment; filename="verdict-{manifestId}.json"
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 9. Implementation Reference
|
||||
|
||||
### 9.1 Source Files
|
||||
|
||||
| Component | Location |
|
||||
|-----------|----------|
|
||||
| VerdictManifest model | `src/Authority/__Libraries/StellaOps.Authority.Core/VerdictManifest/VerdictManifest.cs` |
|
||||
| VerdictManifestBuilder | `src/Authority/__Libraries/StellaOps.Authority.Core/VerdictManifest/VerdictManifestBuilder.cs` |
|
||||
| IVerdictManifestSigner | `src/Authority/__Libraries/StellaOps.Authority.Core/VerdictManifest/IVerdictManifestSigner.cs` |
|
||||
| IVerdictManifestStore | `src/Authority/__Libraries/StellaOps.Authority.Core/VerdictManifest/IVerdictManifestStore.cs` |
|
||||
| VerdictReplayVerifier | `src/Authority/__Libraries/StellaOps.Authority.Core/VerdictManifest/VerdictReplayVerifier.cs` |
|
||||
| PostgreSQL Store | `src/Authority/__Libraries/StellaOps.Authority.Persistence/Stores/PostgresVerdictManifestStore.cs` |
|
||||
|
||||
### 9.2 Database Migration
|
||||
|
||||
- Schema migration: `src/Authority/__Libraries/StellaOps.Authority.Persistence/Migrations/001_verdict_manifest_schema.sql`
|
||||
|
||||
### 9.3 Test Coverage
|
||||
|
||||
| Test Suite | Location |
|
||||
|------------|----------|
|
||||
| VerdictManifest tests | `src/Authority/__Tests/StellaOps.Authority.Core.Tests/VerdictManifest/` |
|
||||
| Replay verification tests | `src/Authority/__Tests/StellaOps.Authority.Core.Tests/VerdictManifest/VerdictReplayVerifierTests.cs` |
|
||||
| Integration tests | `src/Authority/__Tests/StellaOps.Authority.Integration.Tests/VerdictManifest/` |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Trust Lattice Specification](../excititor/trust-lattice.md)
|
||||
- [Authority Architecture](./architecture.md)
|
||||
- [DSSE Signing](../../dev/dsse-signing.md)
|
||||
- [API Reference](../../09_API_CLI_REFERENCE.md)
|
||||
|
||||
---
|
||||
|
||||
*Document Version: 1.0.0*
|
||||
*Sprint: 7100.0003.0002*
|
||||
*Created: 2025-12-22*
|
||||
|
||||
@@ -559,6 +559,159 @@ public interface IVexConnector
|
||||
|
||||
---
|
||||
|
||||
## 7.1) Trust Lattice Framework
|
||||
|
||||
The Trust Lattice extends the basic consensus algorithm with a sophisticated 3-component trust vector model that enables explainable, deterministically replayable vulnerability decisioning.
|
||||
|
||||
### 7.1.1 Trust Vector Model (P/C/R)
|
||||
|
||||
Each VEX source is assigned a `TrustVector` with three components:
|
||||
|
||||
| Component | Symbol | Description | Range |
|
||||
|-----------|--------|-------------|-------|
|
||||
| **Provenance** | P | Cryptographic & process integrity (signatures, key management) | 0.0–1.0 |
|
||||
| **Coverage** | C | Scope match precision (how well claims match the target) | 0.0–1.0 |
|
||||
| **Replayability** | R | Determinism and input pinning (reproducibility) | 0.0–1.0 |
|
||||
|
||||
**Base Trust Calculation:**
|
||||
```
|
||||
BaseTrust(S) = wP * P + wC * C + wR * R
|
||||
|
||||
Default weights:
|
||||
wP = 0.45 (provenance)
|
||||
wC = 0.35 (coverage)
|
||||
wR = 0.20 (replayability)
|
||||
```
|
||||
|
||||
**Default Trust Vectors by Source Class:**
|
||||
|
||||
| Source Class | P | C | R | Notes |
|
||||
|-------------|---|---|---|-------|
|
||||
| Vendor | 0.90 | 0.70 | 0.60 | High provenance, moderate coverage |
|
||||
| Distro | 0.80 | 0.85 | 0.60 | Strong coverage for package-level claims |
|
||||
| Internal | 0.85 | 0.95 | 0.90 | Highest coverage and replayability |
|
||||
| Hub | 0.60 | 0.50 | 0.40 | Aggregated sources, lower baseline |
|
||||
| Attestation | 0.95 | 0.80 | 0.70 | Cryptographically verified statements |
|
||||
|
||||
### 7.1.2 Claim Scoring
|
||||
|
||||
Each VEX claim is scored using the formula:
|
||||
|
||||
```
|
||||
ClaimScore = BaseTrust(S) * M * F
|
||||
|
||||
Where:
|
||||
S = Source's TrustVector
|
||||
M = Claim strength multiplier [0.40–1.00]
|
||||
F = Freshness decay factor [floor–1.00]
|
||||
```
|
||||
|
||||
**Claim Strength Multipliers:**
|
||||
|
||||
| Evidence Type | Strength (M) |
|
||||
|--------------|--------------|
|
||||
| Exploitability analysis + reachability proof | 1.00 |
|
||||
| Config/feature-flag reason with evidence | 0.80 |
|
||||
| Vendor blanket statement | 0.60 |
|
||||
| Under investigation | 0.40 |
|
||||
|
||||
**Freshness Decay:**
|
||||
|
||||
```
|
||||
F = max(exp(-ln(2) * age_days / half_life), floor)
|
||||
|
||||
Default:
|
||||
half_life = 90 days
|
||||
floor = 0.35 (minimum freshness)
|
||||
```
|
||||
|
||||
### 7.1.3 Lattice Merge Algorithm
|
||||
|
||||
The `ClaimScoreMerger` combines multiple scored claims into a deterministic verdict:
|
||||
|
||||
1. **Score claims** using the ClaimScore formula.
|
||||
2. **Detect conflicts** when claims have different statuses.
|
||||
3. **Apply conflict penalty** (default δ=0.25) to all claims when conflicts exist.
|
||||
4. **Order candidates** by: adjusted score → scope specificity → original score → source ID.
|
||||
5. **Select winner** as the highest-ranked claim.
|
||||
6. **Generate audit trail** with all claims, scores, and conflict records.
|
||||
|
||||
**Merge Result:**
|
||||
```jsonc
|
||||
{
|
||||
"status": "not_affected",
|
||||
"confidence": 0.82,
|
||||
"hasConflicts": true,
|
||||
"winningClaim": { "sourceId": "vendor:redhat", "status": "not_affected", ... },
|
||||
"conflicts": [
|
||||
{ "sourceId": "hub:osv", "status": "affected", "reason": "status_conflict" }
|
||||
],
|
||||
"requiresReplayProof": true
|
||||
}
|
||||
```
|
||||
|
||||
### 7.1.4 Policy Gates
|
||||
|
||||
Policy gates enforce trust-based constraints on verdicts:
|
||||
|
||||
| Gate | Purpose | Default Threshold |
|
||||
|------|---------|-------------------|
|
||||
| `MinimumConfidenceGate` | Reject verdicts below confidence threshold | 0.75 (prod), 0.60 (staging) |
|
||||
| `UnknownsBudgetGate` | Fail if unknowns exceed budget | 5 per scan |
|
||||
| `SourceQuotaGate` | Cap single-source influence | 60% unless corroborated |
|
||||
| `ReachabilityRequirementGate` | Require reachability proof for criticals | Enabled |
|
||||
|
||||
Gates are evaluated via `PolicyGateRegistry` and can be configured per environment.
|
||||
|
||||
### 7.1.5 Calibration
|
||||
|
||||
Trust vectors are automatically calibrated based on post-mortem truth comparison:
|
||||
|
||||
```
|
||||
TrustVector' = TrustVector + Δ
|
||||
|
||||
Δ = f(accuracy, detected_bias, learning_rate, momentum)
|
||||
|
||||
Defaults:
|
||||
learning_rate = 0.02 per epoch
|
||||
max_adjustment = 0.05 per epoch
|
||||
momentum_factor = 0.9
|
||||
```
|
||||
|
||||
**Bias Types:**
|
||||
- `OptimisticBias` → reduce Provenance
|
||||
- `PessimisticBias` → increase Provenance
|
||||
- `ScopeBias` → reduce Coverage
|
||||
|
||||
Calibration manifests are stored for auditing and rollback.
|
||||
|
||||
### 7.1.6 Configuration
|
||||
|
||||
Trust lattice settings in `etc/trust-lattice.yaml.sample`:
|
||||
|
||||
```yaml
|
||||
trustLattice:
|
||||
weights:
|
||||
provenance: 0.45
|
||||
coverage: 0.35
|
||||
replayability: 0.20
|
||||
freshness:
|
||||
halfLifeDays: 90
|
||||
floor: 0.35
|
||||
defaults:
|
||||
vendor: { p: 0.90, c: 0.70, r: 0.60 }
|
||||
distro: { p: 0.80, c: 0.85, r: 0.60 }
|
||||
internal: { p: 0.85, c: 0.95, r: 0.90 }
|
||||
calibration:
|
||||
enabled: true
|
||||
learningRate: 0.02
|
||||
maxAdjustmentPerEpoch: 0.05
|
||||
```
|
||||
|
||||
See `docs/modules/excititor/trust-lattice.md` for the complete specification.
|
||||
|
||||
---
|
||||
|
||||
## 8) Query & export APIs
|
||||
|
||||
All endpoints are versioned under `/api/v1/vex`.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# VEX Trust Lattice Specification
|
||||
|
||||
> **Status**: Draft (Sprint 7100)
|
||||
> **Status**: Implementation Complete (Sprint 7100)
|
||||
> **Version**: 1.0.0
|
||||
> **Last Updated**: 2025-12-22
|
||||
> **Source Advisory**: `docs/product-advisories/archived/22-Dec-2026 - Building a Trust Lattice for VEX Sources.md`
|
||||
|
||||
@@ -452,9 +453,63 @@ Note: Conflict recorded in audit trail
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 10. Implementation Reference
|
||||
|
||||
### 10.1 Source Files
|
||||
|
||||
| Component | Location |
|
||||
|-----------|----------|
|
||||
| TrustVector | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/TrustVector.cs` |
|
||||
| TrustWeights | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/TrustWeights.cs` |
|
||||
| ClaimStrength | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/ClaimStrength.cs` |
|
||||
| FreshnessCalculator | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/FreshnessCalculator.cs` |
|
||||
| DefaultTrustVectors | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/DefaultTrustVectors.cs` |
|
||||
| ProvenanceScorer | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/ProvenanceScorer.cs` |
|
||||
| CoverageScorer | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/CoverageScorer.cs` |
|
||||
| ReplayabilityScorer | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/ReplayabilityScorer.cs` |
|
||||
| SourceClassificationService | `src/Excititor/__Libraries/StellaOps.Excititor.Core/TrustVector/SourceClassificationService.cs` |
|
||||
| ClaimScoreMerger | `src/Policy/__Libraries/StellaOps.Policy/TrustLattice/ClaimScoreMerger.cs` |
|
||||
| MinimumConfidenceGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/MinimumConfidenceGate.cs` |
|
||||
| UnknownsBudgetGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/UnknownsBudgetGate.cs` |
|
||||
| SourceQuotaGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/SourceQuotaGate.cs` |
|
||||
| ReachabilityRequirementGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/ReachabilityRequirementGate.cs` |
|
||||
| TrustVectorCalibrator | `src/Excititor/__Libraries/StellaOps.Excititor.Core/Calibration/TrustVectorCalibrator.cs` |
|
||||
|
||||
### 10.2 Configuration Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `etc/trust-lattice.yaml.sample` | Trust vector weights, freshness parameters, default vectors |
|
||||
| `etc/policy-gates.yaml.sample` | Gate thresholds and enable/disable flags |
|
||||
| `etc/excititor-calibration.yaml.sample` | Calibration learning parameters |
|
||||
|
||||
### 10.3 Database Schema
|
||||
|
||||
- **Calibration manifests**: `src/Excititor/__Libraries/StellaOps.Excititor.Storage.Postgres/Migrations/002_calibration_schema.sql`
|
||||
- **Verdict storage**: See Authority module for verdict manifest persistence
|
||||
|
||||
### 10.4 Test Coverage
|
||||
|
||||
| Test Suite | Location |
|
||||
|------------|----------|
|
||||
| TrustVector tests | `src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/TrustVector/` |
|
||||
| ClaimScoreMerger tests | `src/Policy/__Tests/StellaOps.Policy.Tests/TrustLattice/` |
|
||||
| Gate tests | `src/Policy/__Tests/StellaOps.Policy.Tests/Gates/` |
|
||||
| Calibration tests | `src/Excititor/__Tests/StellaOps.Excititor.Core.Tests/Calibration/` |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Excititor Architecture](./architecture.md)
|
||||
- [Verdict Manifest Specification](../authority/verdict-manifest.md)
|
||||
- [Policy Gates Configuration](../policy/architecture.md)
|
||||
- [API Reference](../../09_API_CLI_REFERENCE.md)
|
||||
|
||||
---
|
||||
|
||||
*Document Version: 1.0.0*
|
||||
*Sprint: 7100.0003.0002*
|
||||
*Created: 2025-12-22*
|
||||
|
||||
@@ -203,6 +203,150 @@ Determinism guard instrumentation wraps the evaluator, rejecting access to forbi
|
||||
|
||||
All payloads are immutable and include analyzer fingerprints (`scanner.native@sha256:...`, `policyEngine@sha256:...`) so replay tooling can recompute identical digests. Determinism tests cover both the OpenVEX JSON and the DSSE payload bytes.
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 6.2 · Trust Lattice Policy Gates
|
||||
|
||||
The Policy Engine evaluates Trust Lattice gates after claim score merging to enforce trust-based constraints on VEX verdicts.
|
||||
|
||||
#### Gate Interface
|
||||
|
||||
```csharp
|
||||
public interface IPolicyGate
|
||||
{
|
||||
Task<GateResult> EvaluateAsync(
|
||||
MergeResult mergeResult,
|
||||
PolicyGateContext context,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
|
||||
public sealed record GateResult
|
||||
{
|
||||
public required string GateName { get; init; }
|
||||
public required bool Passed { get; init; }
|
||||
public string? Reason { get; init; }
|
||||
public ImmutableDictionary<string, object> Details { get; init; }
|
||||
}
|
||||
```
|
||||
|
||||
#### Available Gates
|
||||
|
||||
| Gate | Purpose | Configuration Key |
|
||||
|------|---------|-------------------|
|
||||
| **MinimumConfidenceGate** | Reject verdicts below confidence threshold per environment | `gates.minimumConfidence` |
|
||||
| **UnknownsBudgetGate** | Fail scan if unknowns exceed budget | `gates.unknownsBudget` |
|
||||
| **SourceQuotaGate** | Prevent single-source dominance without corroboration | `gates.sourceQuota` |
|
||||
| **ReachabilityRequirementGate** | Require reachability proof for critical CVEs | `gates.reachabilityRequirement` |
|
||||
| **EvidenceFreshnessGate** | Reject stale evidence below freshness threshold | `gates.evidenceFreshness` |
|
||||
|
||||
#### MinimumConfidenceGate
|
||||
|
||||
Requires minimum confidence threshold for suppression verdicts:
|
||||
|
||||
```yaml
|
||||
gates:
|
||||
minimumConfidence:
|
||||
enabled: true
|
||||
thresholds:
|
||||
production: 0.75 # High bar for production
|
||||
staging: 0.60 # Moderate for staging
|
||||
development: 0.40 # Permissive for dev
|
||||
applyToStatuses:
|
||||
- not_affected
|
||||
- fixed
|
||||
```
|
||||
|
||||
- **Behavior**: `affected` status bypasses this gate (conservative default).
|
||||
- **Result**: `confidence_below_threshold` when verdict confidence < environment threshold.
|
||||
|
||||
#### UnknownsBudgetGate
|
||||
|
||||
Limits exposure to unknown/unscored dependencies:
|
||||
|
||||
```yaml
|
||||
gates:
|
||||
unknownsBudget:
|
||||
enabled: true
|
||||
maxUnknownCount: 5
|
||||
maxCumulativeUncertainty: 2.0
|
||||
escalateOnExceed: true
|
||||
```
|
||||
|
||||
- **Behavior**: Fails when unknowns exceed count limit OR cumulative uncertainty exceeds budget.
|
||||
- **Cumulative uncertainty**: `sum(1 - ClaimScore)` across all verdicts.
|
||||
|
||||
#### SourceQuotaGate
|
||||
|
||||
Prevents single-source verdicts without corroboration:
|
||||
|
||||
```yaml
|
||||
gates:
|
||||
sourceQuota:
|
||||
enabled: true
|
||||
maxInfluencePercent: 60
|
||||
corroborationDelta: 0.10
|
||||
requireCorroboration: true
|
||||
```
|
||||
|
||||
- **Behavior**: Fails when single source provides > 60% of verdict weight AND no second source is within delta (0.10).
|
||||
- **Rationale**: Ensures critical decisions have multi-source validation.
|
||||
|
||||
#### ReachabilityRequirementGate
|
||||
|
||||
Requires reachability proof for high-severity vulnerabilities:
|
||||
|
||||
```yaml
|
||||
gates:
|
||||
reachabilityRequirement:
|
||||
enabled: true
|
||||
applySeverities:
|
||||
- critical
|
||||
- high
|
||||
exemptStatuses:
|
||||
- not_affected
|
||||
bypassReasons:
|
||||
- component_not_present
|
||||
```
|
||||
|
||||
- **Behavior**: Fails when CRITICAL/HIGH CVE marked `not_affected` lacks reachability proof (unless bypass reason applies).
|
||||
|
||||
#### Gate Registry
|
||||
|
||||
Gates are registered via DI and evaluated in sequence:
|
||||
|
||||
```csharp
|
||||
public interface IPolicyGateRegistry
|
||||
{
|
||||
IEnumerable<IPolicyGate> GetEnabledGates(string environment);
|
||||
Task<GateEvaluationResult> EvaluateAllAsync(
|
||||
MergeResult mergeResult,
|
||||
PolicyGateContext context,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
```
|
||||
|
||||
#### Gate Metrics
|
||||
|
||||
- `policy_gate_evaluations_total{gate,result}` — Count of gate evaluations by outcome
|
||||
- `policy_gate_failures_total{gate,reason}` — Count of gate failures by reason
|
||||
- `policy_gate_latency_seconds{gate}` — Gate evaluation latency histogram
|
||||
|
||||
#### Gate Implementation Reference
|
||||
|
||||
| Gate | Source File |
|
||||
|------|-------------|
|
||||
| MinimumConfidenceGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/MinimumConfidenceGate.cs` |
|
||||
| UnknownsBudgetGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/UnknownsBudgetGate.cs` |
|
||||
| SourceQuotaGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/SourceQuotaGate.cs` |
|
||||
| ReachabilityRequirementGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/ReachabilityRequirementGate.cs` |
|
||||
| EvidenceFreshnessGate | `src/Policy/__Libraries/StellaOps.Policy/Gates/EvidenceFreshnessGate.cs` |
|
||||
|
||||
See `etc/policy-gates.yaml.sample` for complete gate configuration options.
|
||||
|
||||
**Related Documentation:**
|
||||
- [Trust Lattice Specification](../excititor/trust-lattice.md)
|
||||
- [Verdict Manifest Specification](../authority/verdict-manifest.md)
|
||||
---
|
||||
|
||||
## 7 · Security & Tenancy
|
||||
|
||||
447
docs/modules/vexhub/integration-guide.md
Normal file
447
docs/modules/vexhub/integration-guide.md
Normal file
@@ -0,0 +1,447 @@
|
||||
# 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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`:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
1. CVE ID
|
||||
2. Product identifier (PURL)
|
||||
3. 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:
|
||||
|
||||
```bash
|
||||
#!/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):**
|
||||
```bash
|
||||
curl -H "X-Api-Key: your-api-key-here" https://vexhub.example.com/api/v1/vex/export
|
||||
```
|
||||
|
||||
**Query parameter:**
|
||||
```bash
|
||||
curl "https://vexhub.example.com/api/v1/vex/export?api_key=your-api-key-here"
|
||||
```
|
||||
|
||||
### Rate Limit Headers
|
||||
|
||||
Responses include rate limit information:
|
||||
|
||||
```http
|
||||
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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```yaml
|
||||
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
|
||||
|
||||
```groovy
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```python
|
||||
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 `products` field 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-After` header 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:
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"@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.*
|
||||
Reference in New Issue
Block a user