audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
354
docs/modules/evidence-locker/export-format.md
Normal file
354
docs/modules/evidence-locker/export-format.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# Evidence Bundle Export Format Specification
|
||||
|
||||
> **Version:** 1.0.0
|
||||
> **Status:** FINAL
|
||||
> **Sprint Reference:** [SPRINT_20260106_003_003](../../../docs/implplan/SPRINT_20260106_003_003_EVIDENCE_export_bundle.md)
|
||||
|
||||
## Overview
|
||||
|
||||
This document specifies the standard export format for StellaOps evidence bundles. The export format enables offline verification of software supply chain artifacts including SBOMs, VEX statements, attestations, and policy verdicts.
|
||||
|
||||
## Export Archive Format
|
||||
|
||||
### Filename Convention
|
||||
|
||||
```
|
||||
evidence-bundle-<bundle-id>.tar.gz
|
||||
```
|
||||
|
||||
Where `<bundle-id>` follows the pattern: `eb-<YYYY-MM-DD>-<unique-suffix>`
|
||||
|
||||
Example: `evidence-bundle-eb-2026-01-06-abc123.tar.gz`
|
||||
|
||||
### Compression
|
||||
|
||||
- **Format:** gzip-compressed tar archive
|
||||
- **Compression level:** Configurable (1-9, default: 6)
|
||||
- **Determinism:** Fixed gzip header timestamp (`2026-01-01T00:00:00Z`)
|
||||
- **Permissions:** All files `0644`, directories `0755`, uid/gid `0:0`
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
evidence-bundle-<id>/
|
||||
├── manifest.json # Bundle manifest with all artifact refs
|
||||
├── metadata.json # Bundle metadata (provenance, timestamps)
|
||||
├── README.md # Human-readable verification instructions
|
||||
├── verify.sh # Bash verification script (POSIX-compliant)
|
||||
├── verify.ps1 # PowerShell verification script
|
||||
├── checksums.sha256 # BSD-format SHA256 checksums
|
||||
├── keys/
|
||||
│ ├── signing-key-001.pem # Public key(s) for DSSE verification
|
||||
│ ├── signing-key-002.pem # Additional keys (multi-signature)
|
||||
│ └── trust-bundle.pem # CA chain (if applicable)
|
||||
├── sboms/
|
||||
│ ├── image.cdx.json # Aggregated CycloneDX SBOM
|
||||
│ ├── image.spdx.json # Aggregated SPDX SBOM
|
||||
│ └── layers/
|
||||
│ ├── <layer-digest>.cdx.json # Per-layer CycloneDX
|
||||
│ └── <layer-digest>.spdx.json # Per-layer SPDX
|
||||
├── vex/
|
||||
│ ├── statements/
|
||||
│ │ └── <statement-id>.openvex.json
|
||||
│ └── consensus/
|
||||
│ └── image-consensus.json # VEX consensus result
|
||||
├── attestations/
|
||||
│ ├── sbom.dsse.json # SBOM attestation envelope
|
||||
│ ├── vex.dsse.json # VEX attestation envelope
|
||||
│ ├── policy.dsse.json # Policy verdict attestation
|
||||
│ └── rekor-proofs/
|
||||
│ └── <uuid>.proof.json # Rekor inclusion proofs
|
||||
├── findings/
|
||||
│ ├── scan-results.json # Vulnerability findings
|
||||
│ └── gate-results.json # VEX gate decisions
|
||||
└── audit/
|
||||
└── timeline.ndjson # Audit event timeline
|
||||
```
|
||||
|
||||
## Core Artifacts
|
||||
|
||||
### manifest.json
|
||||
|
||||
The manifest provides a complete inventory of all artifacts in the bundle.
|
||||
|
||||
```json
|
||||
{
|
||||
"manifestVersion": "1.0.0",
|
||||
"bundleId": "eb-2026-01-06-abc123",
|
||||
"createdAt": "2026-01-06T10:30:00.000000Z",
|
||||
"subject": {
|
||||
"type": "container-image",
|
||||
"digest": "sha256:abcdef1234567890...",
|
||||
"name": "registry.example.com/app:v1.2.3"
|
||||
},
|
||||
"artifacts": [
|
||||
{
|
||||
"path": "sboms/image.cdx.json",
|
||||
"type": "sbom",
|
||||
"format": "cyclonedx-1.7",
|
||||
"digest": "sha256:...",
|
||||
"size": 45678
|
||||
},
|
||||
{
|
||||
"path": "attestations/sbom.dsse.json",
|
||||
"type": "attestation",
|
||||
"format": "dsse-v1",
|
||||
"predicateType": "StellaOps.SBOMAttestation@1",
|
||||
"digest": "sha256:...",
|
||||
"size": 12345,
|
||||
"signedBy": ["sha256:keyabc..."]
|
||||
}
|
||||
],
|
||||
"verification": {
|
||||
"merkleRoot": "sha256:...",
|
||||
"algorithm": "sha256",
|
||||
"checksumFile": "checksums.sha256"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### metadata.json
|
||||
|
||||
Provides provenance and chain information.
|
||||
|
||||
```json
|
||||
{
|
||||
"bundleId": "eb-2026-01-06-abc123",
|
||||
"exportedAt": "2026-01-06T10:35:00.000000Z",
|
||||
"exportedBy": "stella evidence export",
|
||||
"exportVersion": "2026.04",
|
||||
"provenance": {
|
||||
"tenantId": "tenant-xyz",
|
||||
"scanId": "scan-abc123",
|
||||
"pipelineId": "pipeline-def456",
|
||||
"sourceRepository": "https://github.com/example/app",
|
||||
"sourceCommit": "abc123def456..."
|
||||
},
|
||||
"chainInfo": {
|
||||
"previousBundleId": "eb-2026-01-05-xyz789",
|
||||
"sequenceNumber": 42
|
||||
},
|
||||
"transparency": {
|
||||
"rekorLogUrl": "https://rekor.sigstore.dev",
|
||||
"rekorEntryUuids": ["uuid1", "uuid2"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### checksums.sha256
|
||||
|
||||
BSD-format SHA256 checksums for all artifacts:
|
||||
|
||||
```
|
||||
SHA256 (manifest.json) = abc123...
|
||||
SHA256 (metadata.json) = def456...
|
||||
SHA256 (sboms/image.cdx.json) = 789abc...
|
||||
SHA256 (attestations/sbom.dsse.json) = cde012...
|
||||
```
|
||||
|
||||
## Verification Scripts
|
||||
|
||||
### verify.sh (Bash)
|
||||
|
||||
POSIX-compliant bash script for Unix/Linux/macOS verification:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
BUNDLE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
MANIFEST="$BUNDLE_DIR/manifest.json"
|
||||
CHECKSUMS="$BUNDLE_DIR/checksums.sha256"
|
||||
|
||||
echo "=== StellaOps Evidence Bundle Verification ==="
|
||||
echo "Bundle: $(basename "$BUNDLE_DIR")"
|
||||
echo ""
|
||||
|
||||
# Step 1: Verify checksums
|
||||
echo "[1/4] Verifying artifact checksums..."
|
||||
cd "$BUNDLE_DIR"
|
||||
sha256sum -c "$CHECKSUMS" --quiet
|
||||
echo " OK: All checksums match"
|
||||
|
||||
# Step 2: Verify Merkle root
|
||||
echo "[2/4] Verifying Merkle root..."
|
||||
COMPUTED_ROOT=$(compute-merkle-root "$CHECKSUMS")
|
||||
EXPECTED_ROOT=$(jq -r '.verification.merkleRoot' "$MANIFEST")
|
||||
if [ "$COMPUTED_ROOT" = "$EXPECTED_ROOT" ]; then
|
||||
echo " OK: Merkle root verified"
|
||||
else
|
||||
echo " FAIL: Merkle root mismatch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 3: Verify DSSE signatures
|
||||
echo "[3/4] Verifying attestation signatures..."
|
||||
for dsse in "$BUNDLE_DIR"/attestations/*.dsse.json; do
|
||||
verify-dsse "$dsse" --keys "$BUNDLE_DIR/keys/"
|
||||
echo " OK: $(basename "$dsse")"
|
||||
done
|
||||
|
||||
# Step 4: Verify Rekor proofs (if online)
|
||||
echo "[4/4] Verifying Rekor proofs..."
|
||||
if [ "${OFFLINE:-false}" = "true" ]; then
|
||||
echo " SKIP: Offline mode, Rekor verification skipped"
|
||||
else
|
||||
for proof in "$BUNDLE_DIR"/attestations/rekor-proofs/*.proof.json; do
|
||||
verify-rekor-proof "$proof"
|
||||
echo " OK: $(basename "$proof")"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Verification Complete: PASSED ==="
|
||||
```
|
||||
|
||||
### verify.ps1 (PowerShell)
|
||||
|
||||
Windows PowerShell verification script:
|
||||
|
||||
```powershell
|
||||
#Requires -Version 5.1
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$BundleDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$Manifest = Join-Path $BundleDir 'manifest.json'
|
||||
$Checksums = Join-Path $BundleDir 'checksums.sha256'
|
||||
|
||||
Write-Host "=== StellaOps Evidence Bundle Verification ===" -ForegroundColor Cyan
|
||||
Write-Host "Bundle: $(Split-Path -Leaf $BundleDir)"
|
||||
Write-Host ""
|
||||
|
||||
# Step 1: Verify checksums
|
||||
Write-Host "[1/4] Verifying artifact checksums..." -ForegroundColor Yellow
|
||||
Push-Location $BundleDir
|
||||
try {
|
||||
Get-Content $Checksums | ForEach-Object {
|
||||
if ($_ -match 'SHA256 \((.+)\) = (.+)') {
|
||||
$File = $Matches[1]
|
||||
$Expected = $Matches[2]
|
||||
$Actual = (Get-FileHash -Path $File -Algorithm SHA256).Hash.ToLower()
|
||||
if ($Actual -ne $Expected) {
|
||||
throw "Checksum mismatch: $File"
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-Host " OK: All checksums match" -ForegroundColor Green
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# Step 2-4: Continue verification...
|
||||
Write-Host ""
|
||||
Write-Host "=== Verification Complete: PASSED ===" -ForegroundColor Green
|
||||
```
|
||||
|
||||
## Determinism Requirements
|
||||
|
||||
### Timestamp Handling
|
||||
|
||||
- All timestamps MUST be UTC ISO-8601 with microsecond precision
|
||||
- Format: `YYYY-MM-DDTHH:MM:SS.ffffffZ`
|
||||
- Archive metadata timestamps are fixed for reproducibility
|
||||
|
||||
### Ordering
|
||||
|
||||
- Manifest artifacts: sorted lexicographically by `path`
|
||||
- Checksum entries: sorted lexicographically by filename
|
||||
- JSON object keys: sorted lexicographically (RFC 8785)
|
||||
- NDJSON records: sorted by primary key (e.g., `observationId`)
|
||||
|
||||
### Hash Computation
|
||||
|
||||
- Algorithm: SHA-256 (lowercase hex)
|
||||
- Input: raw file bytes (no BOM, LF line endings)
|
||||
- Merkle tree: RFC 6962 compliant binary Merkle tree
|
||||
|
||||
## Artifact Types
|
||||
|
||||
### SBOMs
|
||||
|
||||
| Format | File Extension | MIME Type |
|
||||
|--------|---------------|-----------|
|
||||
| CycloneDX 1.7 | `.cdx.json` | `application/vnd.cyclonedx+json` |
|
||||
| SPDX 3.0.1 | `.spdx.json` | `application/spdx+json` |
|
||||
|
||||
### Attestations
|
||||
|
||||
| Type | Predicate Type | File Pattern |
|
||||
|------|---------------|--------------|
|
||||
| SBOM | `StellaOps.SBOMAttestation@1` | `sbom.dsse.json` |
|
||||
| VEX | `StellaOps.VEXAttestation@1` | `vex.dsse.json` |
|
||||
| Policy | `StellaOps.PolicyEvaluation@1` | `policy.dsse.json` |
|
||||
| Gate | `StellaOps.VexGate@1` | `gate.dsse.json` |
|
||||
|
||||
### VEX Statements
|
||||
|
||||
- Format: OpenVEX 0.2.0+
|
||||
- File extension: `.openvex.json`
|
||||
- Location: `vex/statements/`
|
||||
|
||||
## Export Options
|
||||
|
||||
### CLI Command
|
||||
|
||||
```bash
|
||||
# Basic export
|
||||
stella evidence export --bundle <bundle-id> --output ./audit-bundle.tar.gz
|
||||
|
||||
# With options
|
||||
stella evidence export --bundle <bundle-id> \
|
||||
--output ./bundle.tar.gz \
|
||||
--include-layers \
|
||||
--include-rekor-proofs \
|
||||
--compression 9
|
||||
|
||||
# Verify exported bundle
|
||||
stella evidence verify ./audit-bundle.tar.gz
|
||||
|
||||
# Verify offline (skip Rekor)
|
||||
stella evidence verify ./audit-bundle.tar.gz --offline
|
||||
```
|
||||
|
||||
### API Endpoint
|
||||
|
||||
```http
|
||||
POST /api/v1/bundles/{bundleId}/export
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"format": "tar.gz",
|
||||
"compression": "gzip",
|
||||
"compressionLevel": 6,
|
||||
"includeRekorProofs": true,
|
||||
"includeLayerSboms": true
|
||||
}
|
||||
```
|
||||
|
||||
## Offline Verification
|
||||
|
||||
For air-gapped environments:
|
||||
|
||||
1. Transfer `evidence-bundle-<id>.tar.gz` to isolated system
|
||||
2. Extract archive: `tar -xzf evidence-bundle-<id>.tar.gz`
|
||||
3. Run verification: `./verify.sh` (Unix) or `.\verify.ps1` (Windows)
|
||||
4. Pass `OFFLINE=true` to skip Rekor verification: `OFFLINE=true ./verify.sh`
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **StellaOps CLI:** 2026.04+
|
||||
- **Export Library:** StellaOps.EvidenceLocker.Export 1.0.0+
|
||||
- **Verify scripts:** bash 4.0+ / PowerShell 5.1+
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Bundle Packaging](bundle-packaging.md) - Internal bundle structure
|
||||
- [Evidence Bundle v1](evidence-bundle-v1.md) - Core bundle contract
|
||||
- [Verify Offline](verify-offline.md) - Offline verification procedures
|
||||
- [Attestation Contract](attestation-contract.md) - DSSE envelope format
|
||||
|
||||
## Change Log
|
||||
|
||||
| Date | Version | Author | Description |
|
||||
|------|---------|--------|-------------|
|
||||
| 2026-01-07 | 1.0.0 | StellaOps | Initial specification for Sprint 003_003 |
|
||||
Reference in New Issue
Block a user