Files
git.stella-ops.org/docs/modules/scanner/design/offline-kit-parity.md
StellaOps Bot 8768c27f30
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / sign-signals-artifacts (push) Has been cancelled
Signals DSSE Sign & Evidence Locker / verify-signatures (push) Has been cancelled
Add signal contracts for reachability, exploitability, trust, and unknown symbols
- Introduced `ReachabilityState`, `RuntimeHit`, `ExploitabilitySignal`, `ReachabilitySignal`, `SignalEnvelope`, `SignalType`, `TrustSignal`, and `UnknownSymbolSignal` records to define various signal types and their properties.
- Implemented JSON serialization attributes for proper data interchange.
- Created project files for the new signal contracts library and corresponding test projects.
- Added deterministic test fixtures for micro-interaction testing.
- Included cryptographic keys for secure operations with cosign.
2025-12-05 00:27:00 +02:00

9.4 KiB

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

{
  "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

{
  "payloadType": "application/vnd.stellaops.scanner.manifest+json",
  "payload": "<base64-encoded-manifest>",
  "signatures": [
    {
      "keyid": "stellaops-scanner-release-2025",
      "sig": "<base64-signature>"
    }
  ]
}

Signing Process

#!/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

#!/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

// src/Scanner/StellaOps.Scanner.Offline/OfflineKitVerifier.cs
public class OfflineKitVerifier
{
    public async Task<VerificationResult> 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<OfflineManifest>(
            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

# .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

  • 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