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.
This commit is contained in:
323
docs/modules/scanner/design/offline-kit-parity.md
Normal file
323
docs/modules/scanner/design/offline-kit-parity.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user