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

324 lines
9.4 KiB
Markdown

# 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": "<base64-encoded-manifest>",
"signatures": [
{
"keyid": "stellaops-scanner-release-2025",
"sig": "<base64-signature>"
}
]
}
```
### 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<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
```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`