Files
git.stella-ops.org/docs/airgap/risk-bundles.md
StellaOps Bot 49922dff5a
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Risk Bundle CI / risk-bundle-build (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Risk Bundle CI / risk-bundle-offline-kit (push) Has been cancelled
Risk Bundle CI / publish-checksums (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
up the blokcing tasks
2025-12-11 02:32:18 +02:00

12 KiB

Risk Bundles (Airgap)

Risk bundles package vulnerability intelligence data for offline/air-gapped environments. They provide deterministic, signed archives containing provider datasets (CISA KEV, FIRST EPSS, OSV) that can be verified and imported without network connectivity.

Bundle Structure

A risk bundle is a gzip-compressed tar archive (risk-bundle.tar.gz) with the following structure:

risk-bundle.tar.gz
├── manifests/
│   └── provider-manifest.json      # Bundle metadata and provider entries
├── providers/
│   ├── cisa-kev/
│   │   └── snapshot                # CISA Known Exploited Vulnerabilities JSON
│   ├── first-epss/
│   │   └── snapshot                # FIRST EPSS scores CSV/JSON
│   └── osv/                        # (optional) OpenSSF OSV bulk JSON
│       └── snapshot
└── signatures/
    └── provider-manifest.dsse      # DSSE envelope for manifest

Provider Manifest

The provider-manifest.json contains bundle metadata and per-provider entries:

{
  "version": "1.0.0",
  "bundleId": "risk-bundle-20241211-120000",
  "createdAt": "2024-12-11T12:00:00Z",
  "inputsHash": "sha256:abc123...",
  "providers": [
    {
      "providerId": "cisa-kev",
      "digest": "sha256:def456...",
      "snapshotDate": "2024-12-11T00:00:00Z",
      "optional": false
    },
    {
      "providerId": "first-epss",
      "digest": "sha256:789abc...",
      "snapshotDate": "2024-12-11T00:00:00Z",
      "optional": true
    }
  ]
}
Field Description
version Manifest schema version (currently 1.0.0)
bundleId Unique identifier for this bundle
createdAt ISO-8601 UTC timestamp of bundle creation
inputsHash SHA-256 hash of concatenated provider digests (deterministic ordering)
providers[] Array of provider entries sorted by providerId

Provider Entry Fields

Field Description
providerId Provider identifier (cisa-kev, first-epss, osv)
digest SHA-256 hash of snapshot file (sha256:<hex>)
snapshotDate ISO-8601 timestamp of provider data snapshot
optional Whether provider is required for bundle validity

Provider Catalog

Provider Source Coverage Refresh Required
cisa-kev CISA Known Exploited Vulnerabilities Exploited CVEs with KEV flag Daily Yes
first-epss FIRST EPSS scores Exploitation probability per CVE Daily No
osv OpenSSF OSV OSS advisories with affected ranges Weekly No (opt-in)

Building Risk Bundles

Using the Export Worker

The ExportCenter worker can build risk bundles via the stella export risk-bundle job:

# Build bundle with default providers (CISA KEV + EPSS)
stella export risk-bundle --output /path/to/output

# Include OSV providers (larger bundle)
stella export risk-bundle --output /path/to/output --include-osv

# Build with specific bundle ID
stella export risk-bundle --output /path/to/output --bundle-id "custom-bundle-id"

Using the CI Build Script

For CI pipelines and deterministic testing, use the shell scripts:

# Build fixture bundle for CI testing (deterministic)
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only

# Build with OSV
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only --include-osv

# Build with custom bundle ID
ops/devops/risk-bundle/build-bundle.sh --output /tmp/bundle --fixtures-only --bundle-id "ci-test-bundle"

Build Script Options

Option Description
--output <dir> Output directory for bundle artifacts (required)
--fixtures-only Use fixture data instead of live provider downloads
--include-osv Include OSV providers (increases bundle size)
--bundle-id <id> Custom bundle ID (default: auto-generated with timestamp)

Build Outputs

After building, the output directory contains:

output/
├── risk-bundle.tar.gz          # The bundle archive
├── risk-bundle.tar.gz.sha256   # SHA-256 checksum
└── manifest.json               # Copy of provider-manifest.json

Verifying Risk Bundles

Using the CLI

# Basic verification
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz

# With detached signature
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --signature-path ./bundle.sig

# Check Sigstore Rekor transparency log
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --check-rekor

# JSON output for automation
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --json

# Verbose output with warnings
stella risk bundle verify --bundle-path ./risk-bundle.tar.gz --verbose

CLI Options

Option Description
--bundle-path, -b Path to risk bundle file (required)
--signature-path, -s Path to detached signature file
--check-rekor Verify transparency log entry in Sigstore Rekor
--json Output results as JSON
--tenant Tenant context for verification
--verbose Show detailed output including warnings

Using the Verification Script

For offline/air-gap verification without the CLI:

# Basic verification
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz

# With detached signature
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --signature /path/to/bundle.sig

# Strict mode (warnings are errors)
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --strict

# JSON output
ops/devops/risk-bundle/verify-bundle.sh /path/to/risk-bundle.tar.gz --json

Verification Steps

The verification process performs these checks:

  1. Archive integrity - Bundle is a valid tar.gz archive
  2. Structure validation - Required files present (manifests/provider-manifest.json)
  3. Manifest parsing - Valid JSON with required fields (bundleId, version, providers)
  4. Provider hash verification - Each provider snapshot matches its declared digest
  5. Mandatory provider check - cisa-kev must be present and valid
  6. DSSE signature validation - Manifest signature verified (if present)
  7. Detached signature - Bundle archive signature verified (if provided)

Exit Codes

Code Meaning
0 Bundle is valid
1 Bundle is invalid or verification failed
2 Input error (missing file, bad arguments)

JSON Output Format

{
  "valid": true,
  "bundleId": "risk-bundle-20241211-120000",
  "version": "1.0.0",
  "providerCount": 2,
  "mandatoryProviderFound": true,
  "errorCount": 0,
  "warningCount": 1,
  "errors": [],
  "warnings": ["Optional provider not found: osv"]
}

Importing Risk Bundles

Prerequisites

  1. Verify the bundle before import (see above)
  2. Ensure the target system has sufficient storage
  3. Back up existing provider data if replacing

Import Steps

  1. Transfer the bundle to the air-gapped environment via approved media
  2. Verify the bundle using the CLI or verification script
  3. Extract to staging:
    mkdir -p /staging/risk-bundle
    tar -xzf risk-bundle.tar.gz -C /staging/risk-bundle
    
  4. Validate provider data:
    # Verify individual provider hashes
    sha256sum /staging/risk-bundle/providers/cisa-kev/snapshot
    sha256sum /staging/risk-bundle/providers/first-epss/snapshot
    
  5. Import into Concelier:
    stella concelier import-risk-bundle --path /staging/risk-bundle
    

Error Handling

Error Cause Resolution
"Bundle is not a valid tar.gz archive" Corrupted download/transfer Re-download and verify checksum
"Missing required file: manifests/provider-manifest.json" Incomplete bundle Rebuild bundle
"Missing mandatory provider: cisa-kev" KEV snapshot missing Rebuild with valid provider data
"Hash mismatch: cisa-kev" Corrupted provider data Re-download provider snapshot
"DSSE signature validation failed" Tampered manifest Investigate chain of custody

CI/CD Integration

GitHub Actions / Gitea Workflow

The .gitea/workflows/risk-bundle-ci.yml workflow:

  1. Build job: Compiles RiskBundles library, runs tests, builds fixture bundle
  2. Offline kit job: Packages bundle for offline kit distribution
  3. Publish checksums job: Publishes checksums to artifact store (main branch only)
# Trigger manually or on push to relevant paths
on:
  push:
    paths:
      - 'src/ExportCenter/StellaOps.ExportCenter.RiskBundles/**'
      - 'ops/devops/risk-bundle/**'
  workflow_dispatch:
    inputs:
      include_osv:
        type: boolean
        default: false

Offline Kit Integration

Risk bundles are included in the Offline Update Kit:

offline-kit/
└── risk-bundles/
    ├── risk-bundle.tar.gz
    ├── risk-bundle.tar.gz.sha256
    ├── manifest.json
    ├── checksums.txt
    └── kit-manifest.json

The kit-manifest.json provides metadata for offline kit consumers:

{
  "component": "risk-bundle",
  "version": "20241211-120000",
  "files": [
    {"path": "risk-bundle.tar.gz", "checksum_file": "risk-bundle.tar.gz.sha256"},
    {"path": "manifest.json", "checksum_file": "manifest.json.sha256"}
  ],
  "verification": {
    "checksums": "checksums.txt",
    "signature": "risk-bundle.tar.gz.sig"
  }
}

Signing and Trust

DSSE Manifest Signature

The signatures/provider-manifest.dsse file contains a Dead Simple Signing Envelope:

{
  "payloadType": "application/vnd.stellaops.risk-bundle.manifest+json",
  "payload": "<base64-encoded-manifest>",
  "signatures": [
    {
      "keyid": "risk-bundle-signing-key",
      "sig": "<signature>"
    }
  ]
}

Offline Trust Roots

For air-gapped verification, include public keys in the bundle:

signatures/
├── provider-manifest.dsse
└── pubkeys/
    └── <tenant>.pem

Sigstore/Rekor Integration

When --check-rekor is specified, verification queries the Sigstore Rekor transparency log to confirm the bundle was published to the public ledger.

Determinism Checklist

Risk bundles are designed for reproducible builds:

  • Fixed timestamps for tar entries (--mtime="@<epoch>")
  • Sorted file ordering (--sort=name)
  • Numeric owner/group (--owner=0 --group=0 --numeric-owner)
  • Deterministic gzip compression (gzip -n)
  • Providers sorted by providerId in manifest
  • Files sorted lexicographically in bundle
  • UTF-8 canonical paths
  • ISO-8601 UTC timestamps

Troubleshooting

Common Issues

Q: Bundle verification fails with "jq not available"

A: The verification script uses jq for JSON parsing. Install it or use the CLI (stella risk bundle verify) which has built-in JSON support.

Q: Hash mismatch after transfer

A: Binary transfers can corrupt files. Use checksums:

# On source system
sha256sum risk-bundle.tar.gz > checksum.txt

# On target system
sha256sum -c checksum.txt

Q: "Optional provider not found" warning

A: This is informational. Optional providers (EPSS, OSV) enhance risk analysis but aren't required. Use --strict if you want to enforce their presence.

Q: DSSE signature validation fails in air-gap

A: Ensure the offline trust root is configured:

stella config set risk-bundle.trust-root /path/to/pubkey.pem