- 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.
324 lines
9.4 KiB
Markdown
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`
|