# 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: ```json { "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:`) | | `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: ```bash # 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: ```bash # 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 ` | 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 ` | 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 ```bash # 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: ```bash # 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 ```json { "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**: ```bash mkdir -p /staging/risk-bundle tar -xzf risk-bundle.tar.gz -C /staging/risk-bundle ``` 4. **Validate provider data**: ```bash # Verify individual provider hashes sha256sum /staging/risk-bundle/providers/cisa-kev/snapshot sha256sum /staging/risk-bundle/providers/first-epss/snapshot ``` 5. **Import into Concelier**: ```bash 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) ```yaml # 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: ```json { "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: ```json { "payloadType": "application/vnd.stellaops.risk-bundle.manifest+json", "payload": "", "signatures": [ { "keyid": "risk-bundle-signing-key", "sig": "" } ] } ``` ### Offline Trust Roots For air-gapped verification, include public keys in the bundle: ``` signatures/ ├── provider-manifest.dsse └── pubkeys/ └── .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: - [x] Fixed timestamps for tar entries (`--mtime="@"`) - [x] Sorted file ordering (`--sort=name`) - [x] Numeric owner/group (`--owner=0 --group=0 --numeric-owner`) - [x] Deterministic gzip compression (`gzip -n`) - [x] Providers sorted by `providerId` in manifest - [x] Files sorted lexicographically in bundle - [x] UTF-8 canonical paths - [x] 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: ```bash # 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: ```bash stella config set risk-bundle.trust-root /path/to/pubkey.pem ``` ## Related Documentation - [Offline Update Kit](../24_OFFLINE_KIT.md) - Complete offline kit documentation - [Mirror Bundles](./mirror-bundles.md) - OCI artifact bundles for air-gap - [Provider Matrix](../modules/export-center/operations/risk-bundle-provider-matrix.md) - Detailed provider specifications - [ExportCenter Architecture](../modules/export-center/architecture.md) - Export service design