# Offline Kit Parity for Scanner Standards (SC10) Status: Draft · Date: 2025-12-04 Scope: Define offline-kit contents, DSSE signing, and parity requirements for schemas, adapters, mappings, and fixtures to enable air-gapped operation. ## Objectives - Bundle all schema/adapter/fixture artifacts for offline use. - Sign bundles with DSSE for integrity verification. - Ensure offline kit matches online capabilities. - Document verification procedures for air-gapped environments. ## Bundle Structure ``` out/offline/scanner-standards-kit-v1/ ├── manifest.json # Bundle manifest with all hashes ├── manifest.dsse # DSSE signature over manifest ├── schemas/ │ ├── cyclonedx-1.7.schema.json # CDX 1.7 JSON schema │ ├── cyclonedx-1.6.schema.json # CDX 1.6 JSON schema │ ├── spdx-3.0.1.schema.json # SPDX 3.0.1 JSON-LD schema │ └── slsa-provenance-v1.schema.json ├── adapters/ │ ├── mapping-cvss4-to-cvss3.csv │ ├── mapping-cdx17-to-cdx16.csv │ ├── mapping-slsa12-to-slsa10.csv │ └── hashes.txt ├── fixtures/ │ ├── cdx17-cbom/ │ │ ├── sample-cdx17-cbom.json │ │ ├── sample-cdx16.json │ │ ├── source-track.sample.json │ │ └── hashes.txt │ └── competitor-adapters/ │ ├── fixtures/ │ │ ├── normalized-syft.json │ │ ├── normalized-trivy.json │ │ └── normalized-clair.json │ └── coverage.csv ├── tools/ │ ├── versions.json # Pinned tool versions │ └── checksums.txt # Tool binary hashes └── trust/ ├── root-ca.pem # Trust root for signature verification └── keyring.json # Signing key metadata ``` ## Manifest Format ```json { "version": "1.0.0", "created": "2025-12-04T00:00:00Z", "creator": "stellaops-scanner", "artifacts": [ { "path": "schemas/cyclonedx-1.7.schema.json", "type": "schema", "format": "cyclonedx", "version": "1.7", "blake3": "a1b2c3d4...", "sha256": "e5f6a7b8..." }, { "path": "adapters/mapping-cvss4-to-cvss3.csv", "type": "adapter", "source": "cvss4", "target": "cvss3.1", "blake3": "fa600b26...", "sha256": "072b66be..." }, { "path": "fixtures/cdx17-cbom/sample-cdx17-cbom.json", "type": "fixture", "format": "cyclonedx", "version": "1.7", "blake3": "27c6de0c...", "sha256": "22d8f6f8..." } ], "tools": { "syft": { "version": "1.0.0", "blake3": "...", "sha256": "..." }, "trivy": { "version": "0.50.0", "blake3": "...", "sha256": "..." } }, "manifestHash": { "blake3": "...", "sha256": "..." } } ``` ## DSSE Signing ### Signature Format ```json { "payloadType": "application/vnd.stellaops.scanner.manifest+json", "payload": "", "signatures": [ { "keyid": "stellaops-scanner-release-2025", "sig": "" } ] } ``` ### Signing Process ```bash #!/bin/bash # scripts/scanner/sign-offline-kit.sh MANIFEST="out/offline/scanner-standards-kit-v1/manifest.json" DSSE="out/offline/scanner-standards-kit-v1/manifest.dsse" KEY_ID="stellaops-scanner-release-2025" # Compute manifest hash MANIFEST_HASH=$(b3sum "${MANIFEST}" | cut -d' ' -f1) # Sign with DSSE stellaops-sign dsse \ --payload-type "application/vnd.stellaops.scanner.manifest+json" \ --payload "${MANIFEST}" \ --key-id "${KEY_ID}" \ --output "${DSSE}" echo "Signed manifest: ${DSSE}" echo "Manifest BLAKE3: ${MANIFEST_HASH}" ``` ## Verification ### Offline Verification Steps ```bash #!/bin/bash # scripts/scanner/verify-offline-kit.sh KIT_DIR="out/offline/scanner-standards-kit-v1" # 1. Verify DSSE signature stellaops-verify dsse \ --envelope "${KIT_DIR}/manifest.dsse" \ --trust-root "${KIT_DIR}/trust/root-ca.pem" \ --expected-payload-type "application/vnd.stellaops.scanner.manifest+json" # 2. Extract manifest and verify artifacts MANIFEST=$(stellaops-verify dsse --envelope "${KIT_DIR}/manifest.dsse" --extract-payload) # 3. Verify each artifact hash for artifact in $(echo "${MANIFEST}" | jq -r '.artifacts[] | @base64'); do path=$(echo "${artifact}" | base64 -d | jq -r '.path') expected_blake3=$(echo "${artifact}" | base64 -d | jq -r '.blake3') actual_blake3=$(b3sum "${KIT_DIR}/${path}" | cut -d' ' -f1) if [[ "${actual_blake3}" != "${expected_blake3}" ]]; then echo "FAIL: ${path} hash mismatch" exit 1 fi echo "PASS: ${path}" done echo "All artifacts verified" ``` ### Programmatic Verification ```csharp // src/Scanner/StellaOps.Scanner.Offline/OfflineKitVerifier.cs public class OfflineKitVerifier { public async Task VerifyAsync( string kitPath, ITrustRootProvider trustRoots) { var manifestPath = Path.Combine(kitPath, "manifest.json"); var dssePath = Path.Combine(kitPath, "manifest.dsse"); // Verify DSSE signature var envelope = await DsseEnvelope.LoadAsync(dssePath); var signatureValid = await _verifier.VerifyAsync(envelope, trustRoots); if (!signatureValid) return VerificationResult.SignatureInvalid; // Parse manifest var manifest = JsonSerializer.Deserialize( envelope.Payload); // Verify each artifact foreach (var artifact in manifest.Artifacts) { var artifactPath = Path.Combine(kitPath, artifact.Path); var actualHash = await HashUtil.Blake3Async(artifactPath); if (actualHash != artifact.Blake3) return VerificationResult.ArtifactHashMismatch(artifact.Path); } return VerificationResult.Success(manifest); } } ``` ## Parity Requirements ### Required Contents | Category | Online | Offline Kit | Notes | |----------|--------|-------------|-------| | CDX 1.7 Schema | CDN fetch | Bundled | Schema validation | | CDX 1.6 Schema | CDN fetch | Bundled | Downgrade validation | | CVSS v4→v3 Adapter | API lookup | Bundled CSV | Pure function | | CDX 1.7→1.6 Adapter | API lookup | Bundled CSV | Pure function | | SLSA 1.2→1.0 Adapter | API lookup | Bundled CSV | Pure function | | Golden Fixtures | Test repo | Bundled | Determinism tests | | Tool Binaries | Package registry | Bundled/Checksum | Syft, Trivy | | Trust Roots | Online PKI | Bundled PEM | Signature verification | ### Functional Parity The offline kit must support: | Operation | Online | Offline | |-----------|--------|---------| | Schema validation | Yes | Yes | | SBOM serialization | Yes | Yes | | Downgrade conversion | Yes | Yes | | Hash verification | Yes | Yes | | DSSE verification | Yes | Yes | | Determinism testing | Yes | Yes | | Rekor transparency | Yes | No* | *Offline mode uses mirrored checkpoints instead of live Rekor ## Bundle Generation ### CI Workflow ```yaml # .gitea/workflows/offline-kit.yml jobs: build-offline-kit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Collect schemas run: | mkdir -p out/offline/scanner-standards-kit-v1/schemas cp schemas/cyclonedx-*.json out/offline/scanner-standards-kit-v1/schemas/ cp schemas/spdx-*.json out/offline/scanner-standards-kit-v1/schemas/ - name: Collect adapters run: | mkdir -p out/offline/scanner-standards-kit-v1/adapters cp docs/modules/scanner/fixtures/adapters/*.csv out/offline/scanner-standards-kit-v1/adapters/ cp docs/modules/scanner/fixtures/adapters/hashes.txt out/offline/scanner-standards-kit-v1/adapters/ - name: Collect fixtures run: | mkdir -p out/offline/scanner-standards-kit-v1/fixtures cp -r docs/modules/scanner/fixtures/cdx17-cbom out/offline/scanner-standards-kit-v1/fixtures/ cp -r docs/modules/scanner/fixtures/competitor-adapters out/offline/scanner-standards-kit-v1/fixtures/ - name: Generate manifest run: scripts/scanner/generate-manifest.sh - name: Sign bundle run: scripts/scanner/sign-offline-kit.sh env: SIGNING_KEY: ${{ secrets.SCANNER_SIGNING_KEY }} - name: Verify bundle run: scripts/scanner/verify-offline-kit.sh - name: Upload artifact uses: actions/upload-artifact@v4 with: name: scanner-standards-kit-v1 path: out/offline/scanner-standards-kit-v1/ ``` ## Update Procedure ### Offline Kit Refresh When schemas/adapters change: 1. Update source artifacts in repository 2. Run hash verification CI 3. Generate new manifest 4. Sign with release key 5. Publish new kit version 6. Document changes in release notes ### Version Compatibility | Kit Version | Scanner Version | CDX Version | CVSS Support | |-------------|-----------------|-------------|--------------| | v1.0.0 | 1.x | 1.6, 1.7 | v3.1, v4.0 | | v1.1.0 | 1.x | 1.6, 1.7, 1.8* | v3.1, v4.0 | *Future versions ## Links - Sprint: `docs/implplan/SPRINT_0186_0001_0001_record_deterministic_execution.md` (SC10) - Roadmap: `docs/modules/scanner/design/standards-convergence-roadmap.md` (SC1) - Governance: `docs/modules/scanner/design/schema-governance.md` (SC9) - Offline Operation: `docs/24_OFFLINE_KIT.md`