finish off sprint advisories and sprints

This commit is contained in:
master
2026-01-24 00:12:43 +02:00
parent 726d70dc7f
commit c70e83719e
266 changed files with 46699 additions and 1328 deletions

View File

@@ -191,6 +191,50 @@ stellaops alert bundle verify --file ./bundles/alert-123.stella.bundle.tgz
stellaops alert bundle import --file ./bundles/alert-123.stella.bundle.tgz
```
## Function Map Artifacts
Bundles can include runtime linkage verification artifacts. These are stored in dedicated subdirectories:
```
bundle.stella.bundle.tgz
├── ...existing structure...
├── function-maps/
│ ├── {service}-function-map.json
│ └── {service}-function-map.dsse.json
├── observations/
│ └── {date-label}-observations.ndjson
└── verification/
├── verification-report.json
└── verification-report.dsse.json
```
### Artifact Types
| Artifact Type | Media Type | Description |
|---------------|-----------|-------------|
| `function-map` | `application/vnd.stella.function-map+json` | Function map predicate |
| `function-map.dsse` | `application/vnd.dsse+json` | DSSE-signed function map |
| `observations` | `application/x-ndjson` | Runtime observations (NDJSON) |
| `verification-report` | `application/vnd.stella.verification-report+json` | Verification result |
| `verification-report.dsse` | `application/vnd.dsse+json` | DSSE-signed verification report |
### Offline Verification Workflow
In air-gapped environments:
1. Export the bundle with function map and observations included
2. Transfer to the air-gapped instance
3. Run offline verification:
```bash
stella function-map verify \
--function-map ./function-maps/my-service-function-map.json \
--offline --observations ./observations/2026-01-23-observations.ndjson
```
See [Function Map V1 Contract](../../../contracts/function-map-v1.md) for the predicate schema specification.
---
## Security Considerations
1. **Hash Verification**: Always verify bundle hash before processing

View File

@@ -44,7 +44,81 @@ Notes:
- Revocation evidence is verified using bundled OCSP/CRL data.
- Rekor proofs are verified against the pinned checkpoint when provided.
## 5. References
## 5. Two-Tier Bundle Modes
> **Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-04, 040-06)
Evidence bundles are exported in one of two modes:
### 5.1 Light Mode (Default)
Contains only metadata and attestation envelopes. Binary blobs referenced in `largeBlobs[]` are not embedded.
```
bundle/
├── manifest.json # Bundle manifest with exportMode: "light"
├── attestations/
│ └── delta-sig.dsse.json
└── tsa/
├── chain/
└── ocsp/
```
**Advantages:** Small size, fast transfer.
**Limitation:** Blob replay requires a source (`--blob-source`) or network access.
### 5.2 Full Mode (`--full`)
Includes all binary blobs referenced by attestations, enabling fully self-contained offline verification.
```
bundle/
├── manifest.json # Bundle manifest with exportMode: "full"
├── attestations/
│ └── delta-sig.dsse.json
├── blobs/
│ ├── sha256-<hex1> # Binary patch blob
│ └── sha256-<hex2> # SBOM fragment blob
└── tsa/
├── chain/
└── ocsp/
```
**Advantages:** Fully self-contained, no network needed for replay.
**Limitation:** Larger bundle size.
## 6. Blob Replay Verification
When `--replay` is specified, the verifier fetches and checks binary blobs referenced in attestation predicates:
```bash
# Full bundle: blobs are embedded, no external source needed
stella bundle verify --bundle full-bundle/ --offline --replay
# Light bundle: provide local blob source
stella bundle verify --bundle light-bundle/ --replay --blob-source /path/to/blobs/
# Light bundle: fetch from registry (requires network)
stella bundle verify --bundle light-bundle/ --replay --blob-source https://registry.example.com/blobs/
```
### 6.1 Replay Steps
1. Parse attestation envelopes in `attestations/` directory
2. Decode DSSE payloads and extract `largeBlobs[]` references
3. For each blob reference:
- Resolve content from embedded blobs, local source, or registry
- Compute digest using declared algorithm (sha256/sha384/sha512)
- Compare computed digest against declared digest
4. Report pass/fail for each blob
### 6.2 Offline Constraints
- In `--offline` mode, registry blob fetches are blocked
- Light bundles in offline mode require `--blob-source` pointing to a local directory
- Full bundles work in offline mode without additional configuration
## 7. References
- `docs/modules/attestor/guides/timestamp-policy.md`
- `docs/modules/attestor/airgap.md`

View File

@@ -1407,7 +1407,75 @@ Evidence bundles follow OCI/ORAS conventions:
└── sha256:<timestamp> # RFC 3161 timestamp
```
### 10.6 Related Documentation
### 10.6 Two-Tier Bundle Design and Large Blob References
> **Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-04)
Evidence bundles support two export modes to balance transfer speed with auditability:
| Mode | Export Flag | Contents | Use Case |
|------|------------|----------|----------|
| **Light** | (default) | Manifest + attestation envelopes + metadata | Quick transfer, metadata-only audit |
| **Full** | `--full` | Light + embedded binary blobs in `blobs/` | Air-gap replay, full provenance verification |
#### 10.6.1 `largeBlobs[]` Field
The `DeltaSigPredicate` includes a `largeBlobs` array referencing binary artifacts that may be too large to embed in attestation payloads:
```json
{
"schemaVersion": "1.0.0",
"subject": [...],
"delta": [...],
"largeBlobs": [
{
"kind": "binary-patch",
"digest": "sha256:a1b2c3...",
"mediaType": "application/octet-stream",
"sizeBytes": 1048576
},
{
"kind": "sbom-fragment",
"digest": "sha256:d4e5f6...",
"mediaType": "application/spdx+json",
"sizeBytes": 32768
}
],
"sbomDigest": "sha256:789abc..."
}
```
**Field Definitions:**
| Field | Type | Description |
|-------|------|-------------|
| `largeBlobs[].kind` | string | Blob category: `binary-patch`, `sbom-fragment`, `debug-symbols`, etc. |
| `largeBlobs[].digest` | string | Content-addressable digest (`sha256:<hex>`, `sha384:<hex>`, `sha512:<hex>`) |
| `largeBlobs[].mediaType` | string | IANA media type of the blob |
| `largeBlobs[].sizeBytes` | long | Blob size in bytes |
| `sbomDigest` | string | Digest of the canonical SBOM associated with this delta |
#### 10.6.2 Blob Fetch Strategy
During `stella bundle verify --replay`, blobs are resolved in priority order:
1. **Embedded** (full bundles): Read from `blobs/<digest-with-dash>` in bundle directory
2. **Local source** (`--blob-source /path/`): Read from specified local directory
3. **Registry** (`--blob-source https://...`): HTTP GET from OCI registry (blocked in `--offline` mode)
#### 10.6.3 Digest Verification
Fetched blobs are verified against their declared digest using the algorithm prefix:
```
sha256:<hex> → SHA-256
sha384:<hex> → SHA-384
sha512:<hex> → SHA-512
```
A mismatch fails the blob replay verification step.
### 10.7 Related Documentation
- [Golden Corpus KPIs](../../benchmarks/golden-corpus-kpis.md)
- [Golden Corpus Seed List](../../benchmarks/golden-corpus-seed-list.md)

View File

@@ -593,6 +593,159 @@ Token expires: 2025-12-24T10:30:00Z
---
## Score Commands
### stella score compute
Compute a unified trust score from signal values.
**Usage:**
```bash
stella score compute [OPTIONS]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--finding-id <ID>` | CVE@PURL finding identifier |
| `--cvss <score>` | CVSS base score (0-10) |
| `--epss <score>` | EPSS probability (0-1) |
| `--reachability <value>` | Reachability signal (0-1) |
| `--runtime <value>` | Runtime observation signal (0-1) |
| `--exploit <value>` | Exploit maturity signal (0-1) |
| `--backport <value>` | Backport availability signal (0-1) |
| `--source <value>` | Source confidence signal (0-1) |
| `--mitigation <value>` | Mitigation strength signal (0-1) |
| `--weights-version <ver>` | Pin specific weight manifest version |
| `--show-unknowns` | Include U metric and band in output |
| `--show-deltas` | Include delta-if-present calculations |
| `--format <fmt>` | Output format: `table`, `json`, `markdown` |
| `--offline` | Use bundled weights (no server required) |
**Examples:**
```bash
# Basic score computation
stella score compute --finding-id CVE-2024-1234@pkg:npm/lodash@4.17.0 \
--cvss 7.5 --epss 0.15 --reachability 0.9
# Full output with deltas
stella score compute --finding-id CVE-2024-1234@pkg:npm/lodash@4.17.0 \
--cvss 7.5 --reachability 0.9 --runtime 0.7 \
--show-unknowns --show-deltas --format json
```
---
### stella score explain
Display detailed breakdown of a score computation.
**Usage:**
```bash
stella score explain <FINDING-ID> [OPTIONS]
```
**Examples:**
```bash
stella score explain CVE-2024-1234@pkg:npm/lodash@4.17.0
stella score explain CVE-2024-1234@pkg:npm/lodash@4.17.0 --format markdown
```
---
### stella score replay
Fetch the signed replay proof for a previously computed score.
**Usage:**
```bash
stella score replay <SCORE-ID> [OPTIONS]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--format <fmt>` | Output format: `table`, `json`, `markdown` |
| `--verify-rekor` | Also verify Rekor inclusion proof |
**Examples:**
```bash
stella score replay score_a1b2c3d4e5f67890
stella score replay score_a1b2c3d4e5f67890 --format json --verify-rekor
```
---
### stella score verify
Re-execute a score computation and verify it matches the original.
**Usage:**
```bash
stella score verify <SCORE-ID> [OPTIONS]
```
**Options:**
| Option | Description |
|--------|-------------|
| `--format <fmt>` | Output format: `table`, `json`, `markdown` |
| `--verify-rekor` | Also verify Rekor inclusion proof |
**Examples:**
```bash
stella score verify score_a1b2c3d4e5f67890
```
---
### stella gate score evaluate
Compute unified score as part of a gate evaluation (enhanced with unknowns support).
**Usage:**
```bash
stella gate score evaluate [OPTIONS]
```
**Additional Options (new):**
| Option | Description |
|--------|-------------|
| `--show-unknowns` | Include U metric and unknowns band |
| `--show-deltas` | Include delta-if-present for missing signals |
| `--weights-version <ver>` | Pin specific weight manifest version |
---
### stella gate score weights
Manage EWS weight manifests.
**Usage:**
```bash
stella gate score weights <SUBCOMMAND>
```
**Subcommands:**
| Subcommand | Description |
|------------|-------------|
| `list` | List available weight manifest versions |
| `show <version>` | Display manifest details |
| `diff <v1> <v2>` | Compare two manifests |
**Examples:**
```bash
stella gate score weights list
stella gate score weights show v2026-01-22
stella gate score weights diff v2026-01-22 v2026-02-01
```
---
## Policy Commands
### stella policy test
@@ -781,6 +934,133 @@ stella analytics sbom-lake vulnerabilities --environment prod --min-severity hig
---
## Function Map Commands
### stella function-map generate
Generate a function map predicate from an SBOM and optional static analysis.
**Usage:**
```bash
stella function-map generate [OPTIONS]
```
**Options:**
| Option | Alias | Description |
|--------|-------|-------------|
| `--sbom <path>` | `-s` | Path to SBOM file (required) |
| `--service <name>` | | Service name (required) |
| `--subject <purl>` | | Subject artifact PURL (derived from SBOM if omitted) |
| `--static-analysis <path>` | | Path to static analysis results |
| `--hot-functions <glob>` | `-H` | Glob patterns for functions of interest (repeatable) |
| `--min-rate <value>` | | Minimum observation rate 0.0-1.0 (default 0.95) |
| `--window <seconds>` | | Observation window in seconds (default 1800) |
| `--fail-on-unexpected` | | Fail verification on unexpected symbols |
| `--output <path>` | `-o` | Output file path |
| `--format <fmt>` | `-f` | Output format: `json`, `yaml` (default json) |
| `--build-id <id>` | | Build ID for provenance correlation |
**Examples:**
```bash
# Basic generation from SBOM
stella function-map generate --sbom app.cdx.json --service my-backend
# With hot function filtering and custom thresholds
stella function-map generate \
--sbom app.cdx.json \
--service my-backend \
--hot-functions "crypto/*" --hot-functions "auth/*" \
--min-rate 0.90 --window 3600 \
--output function-map.json
```
---
### stella function-map verify
Verify runtime observations against a function map predicate.
**Usage:**
```bash
stella function-map verify [OPTIONS]
```
**Options:**
| Option | Alias | Description |
|--------|-------|-------------|
| `--function-map <path>` | `-m` | Path or OCI reference to predicate (required) |
| `--container <id>` | `-c` | Filter to specific container ID |
| `--from <timestamp>` | | ISO 8601 start time (default: 30 min ago) |
| `--to <timestamp>` | | ISO 8601 end time (default: now) |
| `--output <path>` | `-o` | Output verification report path |
| `--format <fmt>` | `-f` | Output format: `json`, `table`, `md` (default table) |
| `--strict` | | Fail on any unexpected symbols |
| `--offline` | | Use bundled observations file |
| `--observations <path>` | | Path to observations file (NDJSON) |
**Examples:**
```bash
# Online verification against live observations
stella function-map verify \
--function-map function-map.json \
--from "2026-01-23T00:00:00Z" --to "2026-01-23T01:00:00Z"
# Offline verification with bundled observations
stella function-map verify \
--function-map function-map.json \
--offline --observations obs.ndjson \
--format json --output report.json
```
---
## Observations Commands
### stella observations query
Query runtime observations from the observation store.
**Usage:**
```bash
stella observations query [OPTIONS]
```
**Options:**
| Option | Alias | Description |
|--------|-------|-------------|
| `--symbol <glob>` | `-s` | Glob pattern for symbol name |
| `--node-hash <hash>` | `-n` | Exact node hash filter |
| `--container <id>` | `-c` | Container ID filter |
| `--pod <name>` | `-p` | Pod name filter |
| `--namespace <ns>` | `-N` | Kubernetes namespace filter |
| `--probe-type <type>` | | Probe type filter |
| `--from <timestamp>` | | ISO 8601 start time (default: 1 hour ago) |
| `--to <timestamp>` | | ISO 8601 end time (default: now) |
| `--limit <n>` | `-l` | Maximum results (default 100) |
| `--offset <n>` | | Pagination offset (default 0) |
| `--format <fmt>` | `-f` | Output format: `json`, `table`, `csv` (default table) |
| `--summary` | | Show statistics instead of individual records |
| `--output <path>` | `-o` | Output file path |
| `--offline` | | Use local observations file |
| `--observations-file <path>` | | Path to observations file for offline mode |
**Examples:**
```bash
# Query all crypto-related observations
stella observations query --symbol "crypto_*" --from "2026-01-23T00:00:00Z"
# Summary for a specific container
stella observations query --container abc123 --summary
# Export as CSV for analysis
stella observations query --pod my-service-pod --format csv --output obs.csv
```
---
## Ground-Truth Corpus Commands
### stella groundtruth
@@ -1337,6 +1617,269 @@ KPIs:
**See Also:** [Ground-Truth CLI Guide](../ground-truth-cli.md)
---
## Attestation Commands
### stella attest attach
Attach an attestation (DSSE envelope) to an OCI image via ORAS referrers.
**Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-01)
**Usage:**
```bash
stella attest attach --image <ref> --attestation <path> [options]
```
**Options:**
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--image` | `-i` | OCI image reference (e.g., `registry.example.com/app:v1.2`) | (required) |
| `--attestation` | `-a` | Path to DSSE envelope JSON file | (required) |
| `--media-type` | | Media type for the attestation layer | `application/vnd.dsse.envelope.v1+json` |
| `--registry-url` | | Override registry URL | From image reference |
| `--verbose` | `-v` | Show detailed progress | `false` |
**Example:**
```bash
stella attest attach \
--image registry.example.com/app:v1.2 \
--attestation delta-sig.dsse.json \
--verbose
```
**Exit Codes:**
- `0` - Attestation attached successfully
- `1` - Attachment failed (registry error, invalid envelope)
- `2` - Invalid input or configuration error
---
### stella attest verify
Verify attestations attached to an OCI image. Lists and validates DSSE envelopes, checks signatures, and optionally verifies Rekor annotations.
**Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-02)
**Usage:**
```bash
stella attest verify --image <ref> [options]
```
**Options:**
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--image` | `-i` | OCI image reference to verify | (required) |
| `--predicate-type` | | Filter by predicate type URI | (all) |
| `--trusted-keys` | | Path to trusted public keys directory | (none) |
| `--require-rekor` | | Require valid Rekor inclusion annotations | `false` |
| `--output` | `-o` | Output format: `table`, `json` | `table` |
| `--verbose` | `-v` | Show detailed verification steps | `false` |
**Example:**
```bash
stella attest verify \
--image registry.example.com/app:v1.2 \
--predicate-type "https://stellaops.dev/delta-sig/v1" \
--require-rekor \
--output json
```
**Exit Codes:**
- `0` - All attestations verified successfully
- `1` - One or more attestations failed verification
- `2` - Invalid input or configuration error
---
## Binary Analysis Commands
### stella binary delta-sig attest
Sign a delta-sig predicate with an EC key and optionally submit to a Rekor transparency log. Produces a DSSE envelope suitable for `stella attest attach`.
**Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-05)
**Usage:**
```bash
stella binary delta-sig attest --predicate <path> --key <path> [options]
```
**Options:**
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--predicate` | `-p` | Path to delta-sig predicate JSON file | (required) |
| `--key` | `-k` | Path to EC private key (PEM) for DSSE signing | (required) |
| `--output` | `-o` | Path to write the DSSE envelope | stdout |
| `--rekor-url` | | Rekor transparency log URL for submission | (none) |
| `--receipt` | | Path to save Rekor receipt JSON | (none, only with `--rekor-url`) |
| `--dry-run` | | Validate predicate and key without signing | `false` |
| `--verbose` | `-v` | Show detailed signing and submission steps | `false` |
**Example:**
```bash
# Sign predicate and submit to Rekor
stella binary delta-sig attest \
--predicate delta-sig-predicate.json \
--key signing-key.pem \
--output signed-envelope.dsse.json \
--rekor-url https://rekor.sigstore.dev \
--receipt rekor-receipt.json \
--verbose
# Dry run (validate only)
stella binary delta-sig attest \
--predicate delta-sig-predicate.json \
--key signing-key.pem \
--dry-run
```
**Signing Behavior:**
- Key must be an ECDSA private key (PEM format)
- Produces an in-toto v1 statement wrapping the predicate as DSSE payload
- PAE (Pre-Authentication Encoding) used per DSSE specification
- Signature is Base64-encoded in the envelope
**Rekor Submission:**
- When `--rekor-url` is provided, the signed envelope is submitted to the transparency log
- On success, Rekor UUID and log index are displayed
- Receipt JSON includes `uuid`, `logIndex`, `integratedTime`, and `logUrl`
**Exit Codes:**
- `0` - Signing (and optional Rekor submission) succeeded
- `1` - Signing or submission failed
- `2` - Invalid predicate, key format, or configuration error
---
## Bundle Commands
### stella bundle verify
Verify offline evidence bundles with full cryptographic verification. Checks manifest integrity, blob digests, DSSE signatures, Rekor proofs, timestamps, payload types, and optionally replays large blob content verification.
**Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-06)
**Usage:**
```bash
stella bundle verify --bundle <path> [options]
```
**Options:**
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--bundle` | `-b` | Path to bundle (tar.gz or directory) | (required) |
| `--trust-root` | | Path to trusted root certificate (PEM) | (none) |
| `--rekor-checkpoint` | | Path to Rekor checkpoint for offline proof verification | (none) |
| `--offline` | | Run in offline mode (no network access) | `false` |
| `--output` | `-o` | Output format: `table`, `json` | `table` |
| `--strict` | | Fail on any warning (missing optional artifacts) | `false` |
| `--signer` | | Path to signing key (PEM) for verification report | (none) |
| `--signer-cert` | | Path to signer certificate PEM (for report metadata) | (none) |
| `--replay` | | Verify binary content by fetching/reading large blobs referenced in attestations | `false` |
| `--blob-source` | | Override blob source (registry URL or local directory path) | (auto-detect) |
| `--verbose` | `-v` | Show detailed verification steps | `false` |
**Verification Steps:**
1. **Manifest checksum** - Validate bundle manifest integrity
2. **Blob digests** - Verify all blob file SHA-256 digests match manifest
3. **DSSE signatures** - Validate envelope signatures against trusted keys
4. **Rekor proofs** - Verify inclusion proofs against checkpoint (when provided)
5. **Timestamps** - Validate RFC 3161 timestamps against TSA certificates
6. **Payload types** - Verify predicate types match expectations
7. **Blob Replay** (when `--replay`) - Fetch and verify large blobs referenced in attestations
**Blob Replay Behavior:**
- For **full bundles** (blobs embedded): verifies content from `blobs/` directory against attestation digests
- For **light bundles** (metadata only): fetches blobs from `--blob-source` (local dir or registry URL)
- Supports `sha256`, `sha384`, `sha512` digest algorithms
- In `--offline` mode, blob fetch from registries is blocked (only local sources work)
**Example:**
```bash
# Basic verification
stella bundle verify --bundle evidence-bundle.tar.gz
# Full verification with replay and trust root
stella bundle verify \
--bundle /path/to/bundle \
--trust-root /etc/stellaops/tsa-root.pem \
--rekor-checkpoint checkpoint.json \
--replay \
--verbose
# Light bundle with local blob source
stella bundle verify \
--bundle light-bundle/ \
--replay \
--blob-source /path/to/blobs/
# Strict offline verification with signed report
stella bundle verify \
--bundle evidence-bundle/ \
--offline \
--strict \
--signer report-key.pem \
--signer-cert report-cert.pem
```
**Exit Codes:**
- `0` - All verifications passed
- `1` - One or more verifications failed
- `2` - Invalid input or configuration error
---
## Evidence Commands
### stella evidence export-bundle
Export evidence bundles for offline verification. Supports two-tier export modes: **light** (metadata and attestations only) and **full** (includes embedded binary blobs).
**Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline (040-04)
**Usage:**
```bash
stella evidence export-bundle --image <ref> --output <path> [options]
```
**Options:**
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--image` | `-i` | OCI image reference to export evidence for | (required) |
| `--output` | `-o` | Output bundle path (.tar.gz or directory) | (required) |
| `--full` | | Export in full mode (embed binary blobs alongside attestations) | `false` (light mode) |
| `--sign-with` | | Signing method for bundle: `cosign`, `sigstore`, `none` | `none` |
| `--verbose` | `-v` | Show detailed export progress | `false` |
**Export Modes:**
| Mode | Flag | Contents | Size | Use Case |
|------|------|----------|------|----------|
| **Light** | (default) | Manifest, attestation envelopes, metadata | Small | Quick transfer, metadata audit |
| **Full** | `--full` | Light + embedded binary blobs in `blobs/` dir | Large | Air-gap verification, replay |
**Example:**
```bash
# Light export (default)
stella evidence export-bundle \
--image registry.example.com/app:v1.2 \
--output evidence-light.tar.gz
# Full export with embedded blobs
stella evidence export-bundle \
--image registry.example.com/app:v1.2 \
--output evidence-full.tar.gz \
--full \
--verbose
```
**Exit Codes:**
- `0` - Bundle exported successfully
- `1` - Export failed
- `2` - Invalid input or configuration error
---
## Reporting & Export Commands

View File

@@ -133,5 +133,95 @@ signed-sbom-{digest}-{timestamp}.tar.gz
### Related Commands
- `stella sbom generate` — Generate SBOM from container image
- `stella sbom publish` — Publish canonical SBOM as OCI referrer
- `stella attest verify --offline` — Verify attestation bundles offline
- `stella evidence export` — Export evidence bundle with signed SBOM
---
## stella sbom publish — OCI SBOM Publication
### Synopsis
```bash
stella sbom publish --image <ref> [--file <path>] [--format cdx|spdx] [--overwrite]
```
Publishes a canonical (volatile-fields-stripped, key-sorted) SBOM as an OCI referrer artifact attached to the specified container image. The published artifact is discoverable via the OCI Distribution Spec 1.1 referrers API.
### Options
| Option | Alias | Description |
|--------|-------|-------------|
| `--image <ref>` | `-i` | **Required.** Target image reference (`registry/repo@sha256:...`). Must include digest. |
| `--file <path>` | `-f` | Path to SBOM file. If omitted, fetches from Scanner CAS for this image. |
| `--format <fmt>` | | SBOM format: `cdx` (CycloneDX) or `spdx`. Auto-detected from file content if omitted. |
| `--overwrite` | | Supersede the current active SBOM referrer for this image. |
| `--registry-url <url>` | | Override registry URL (defaults to parsed from `--image`). |
| `--verbose` | | Show detailed output including blob digest and normalization info. |
### Behavior
1. **Normalization**: The SBOM is canonicalized before publication:
- Volatile fields stripped: `serialNumber`, `metadata.tools`, `metadata.authors`, `metadata.timestamp` (CycloneDX); `creationInfo.created`, `creationInfo.creators`, `creationInfo.licenseListVersion` (SPDX).
- Object keys sorted lexicographically (ordinal).
- Arrays of objects sorted by deterministic keys (bom-ref, purl, name@version).
- See `docs/contracts/sbom-volatile-fields.json` for the authoritative field list.
2. **Publication**: The canonical SBOM bytes are pushed as an OCI artifact with:
- `artifactType`: `application/vnd.stellaops.sbom.cdx+json` or `application/vnd.stellaops.sbom.spdx+json`
- `subject`: points to the image manifest digest
- Annotations: `dev.stellaops/sbom-version`, `dev.stellaops/sbom-format`
3. **Overwrite/Supersede**: When `--overwrite` is specified:
- The current active SBOM referrer is resolved (highest version number).
- A new referrer is pushed with `version = prior + 1` and a `dev.stellaops/sbom-supersedes` annotation pointing to the prior manifest digest.
- No registry deletes are performed (purely additive).
### Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Publication succeeded |
| 1 | Publication failed (registry error, auth failure) |
| 2 | Error (file not found, invalid image reference, parse error) |
### Examples
```bash
# Publish a CycloneDX SBOM to an image
stella sbom publish --image registry.example.com/myapp@sha256:abc123... --file app.cdx.json
# Publish with explicit format
stella sbom publish --image registry.example.com/myapp@sha256:abc123... --file app.json --format cdx
# Overwrite existing SBOM (supersede)
stella sbom publish --image registry.example.com/myapp@sha256:abc123... --file improved.cdx.json --overwrite
# Verbose output
stella sbom publish --image registry.example.com/myapp@sha256:abc123... --file app.cdx.json --verbose
```
### Sample Output
```
Published SBOM as OCI referrer:
Blob digest: sha256:e3b0c44298fc1c149afbf4c8996fb924...
Manifest digest: sha256:7d865e959b2466918c9863afca942d0f...
Version: 1
Artifact type: application/vnd.stellaops.sbom.cdx+json
```
### Verifier Discovery
Third-party verifiers can discover published SBOMs via the OCI referrers API:
```bash
# List SBOM referrers for an image (using oras CLI)
oras discover registry.example.com/myapp@sha256:abc123... \
--artifact-type application/vnd.stellaops.sbom.cdx+json
# Pull the latest SBOM
oras pull registry.example.com/myapp@sha256:abc123... \
--artifact-type application/vnd.stellaops.sbom.cdx+json
```

View File

@@ -0,0 +1,223 @@
# Delta Attestation Workflow Guide
> **Audience:** CI/CD engineers, release operators, security auditors
>
> **Purpose:** End-to-end guide for generating, signing, attaching, verifying, and exporting delta-sig attestations.
>
> **Sprint:** SPRINT_20260122_040_Platform_oci_delta_attestation_pipeline
## Overview
The delta attestation workflow provides verifiable evidence of binary-level changes between releases. It covers the full lifecycle from generating a delta-sig predicate through to offline bundle verification.
```
diff → attest → attach → verify → export → offline-verify
```
## Prerequisites
- `stella` CLI installed and configured
- EC signing key (PEM format) for attestation signing
- Access to target OCI registry (for attach/verify)
- (Optional) Rekor transparency log URL for public verifiability
## Step 1: Generate Delta-Sig Predicate
Compare two binary builds to produce a delta-sig predicate describing function-level changes:
```bash
stella binary delta-sig diff \
--old /path/to/old-binary \
--new /path/to/new-binary \
--output delta-predicate.json \
--arch linux-amd64
```
The predicate JSON follows the `https://stellaops.dev/delta-sig/v1` schema and includes:
- `subject[]` - Old and new binary references with digests
- `delta[]` - Function-level changes (added, removed, modified)
- `summary` - Aggregate change statistics
- `tooling` - Lifter and diff algorithm metadata
- `largeBlobs[]` - References to binary patches or SBOM fragments (optional)
- `sbomDigest` - Digest of the associated canonical SBOM (optional)
## Step 2: Sign and Attest
Sign the predicate with an EC key, producing a DSSE envelope. Optionally submit to a Rekor transparency log:
```bash
stella binary delta-sig attest \
--predicate delta-predicate.json \
--key signing-key.pem \
--output signed-envelope.dsse.json \
--rekor-url https://rekor.sigstore.dev \
--receipt rekor-receipt.json \
--verbose
```
**Output:**
- `signed-envelope.dsse.json` - DSSE envelope with in-toto v1 statement
- `rekor-receipt.json` - Rekor inclusion proof (UUID, log index, integrated time)
**Without Rekor (air-gapped environments):**
```bash
stella binary delta-sig attest \
--predicate delta-predicate.json \
--key signing-key.pem \
--output signed-envelope.dsse.json
```
## Step 3: Attach to OCI Image
Attach the signed attestation to the target OCI image via ORAS referrers:
```bash
stella attest attach \
--image registry.example.com/app:v1.2 \
--attestation signed-envelope.dsse.json \
--verbose
```
The attestation is stored as a referrer artifact in the registry, discoverable by image digest.
## Step 4: Verify Attestations
Verify that attestations are properly attached and valid:
```bash
stella attest verify \
--image registry.example.com/app:v1.2 \
--predicate-type "https://stellaops.dev/delta-sig/v1" \
--require-rekor \
--verbose
```
This checks:
- DSSE envelope signature validity
- Predicate type matches expected schema
- Rekor annotations are present and valid (when `--require-rekor`)
## Step 5: Export Evidence Bundle
Export all attestation evidence for offline environments:
```bash
# Light mode (metadata only, small size)
stella evidence export-bundle \
--image registry.example.com/app:v1.2 \
--output evidence-light.tar.gz
# Full mode (includes binary blobs for replay)
stella evidence export-bundle \
--image registry.example.com/app:v1.2 \
--output evidence-full.tar.gz \
--full
```
### Bundle Contents
**Light bundle:**
```
bundle/
├── manifest.json # exportMode: "light"
└── attestations/
└── delta-sig.dsse.json
```
**Full bundle:**
```
bundle/
├── manifest.json # exportMode: "full"
├── attestations/
│ └── delta-sig.dsse.json
└── blobs/
├── sha256-<hex1> # Binary patch
└── sha256-<hex2> # SBOM fragment
```
## Step 6: Offline Bundle Verification
Verify the exported bundle in air-gapped environments:
```bash
# Full bundle: self-contained verification with blob replay
stella bundle verify \
--bundle evidence-full.tar.gz \
--offline \
--trust-root /etc/stellaops/tsa-root.pem \
--replay \
--verbose
# Light bundle: provide local blob source for replay
stella bundle verify \
--bundle evidence-light.tar.gz \
--offline \
--replay \
--blob-source /path/to/cached-blobs/
```
### Verification Steps
| Step | Check | Failure Behavior |
|------|-------|------------------|
| 1 | Manifest checksum | Fatal |
| 2 | Blob digests | Fatal |
| 3 | DSSE signatures | Fatal |
| 4 | Rekor proofs | Fatal (if checkpoint provided) |
| 5 | RFC 3161 timestamps | Fatal (in strict mode) |
| 6 | Payload type expectations | Warning (fatal in strict) |
| 7 | Blob replay | Fatal (when `--replay` enabled) |
## CI/CD Integration Example
```yaml
# .gitea/workflows/release.yaml
jobs:
attest:
steps:
- name: Generate delta predicate
run: |
stella binary delta-sig diff \
--old ${{ steps.build.outputs.old_binary }} \
--new ${{ steps.build.outputs.new_binary }} \
--output delta-predicate.json
- name: Sign and submit to Rekor
run: |
stella binary delta-sig attest \
--predicate delta-predicate.json \
--key ${{ secrets.SIGNING_KEY_PATH }} \
--output envelope.dsse.json \
--rekor-url https://rekor.sigstore.dev \
--receipt rekor-receipt.json
- name: Attach to image
run: |
stella attest attach \
--image ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }} \
--attestation envelope.dsse.json
- name: Export full bundle for auditors
run: |
stella evidence export-bundle \
--image ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ env.TAG }} \
--output evidence-bundle.tar.gz \
--full
```
## Troubleshooting
| Issue | Cause | Resolution |
|-------|-------|------------|
| `Blob Replay ✗` | Missing blobs in light bundle | Use `--blob-source` or export with `--full` |
| `DSSE signature ✗` | Key mismatch | Verify signing key matches trusted keys |
| `Rekor proof ✗` | No checkpoint provided | Add `--rekor-checkpoint` for offline |
| Exit code 2 | Invalid predicate JSON | Check required fields: subject, delta, summary, tooling, computedAt |
## See Also
- [CLI Commands Reference](commands/reference.md)
- [Offline Verification Guide](../../attestor/guides/offline-verification.md)
- [BinaryIndex Architecture](../../binary-index/architecture.md)
- [Audit Bundle Format](audit-bundle-format.md)

View File

@@ -173,6 +173,10 @@ The Determinization subsystem calculates uncertainty scores based on signal comp
Determinization scores are exposed to SPL policies via the `signals.trust.*` and `signals.uncertainty.*` namespaces. Use `signals.uncertainty.entropy` to access entropy values and `signals.trust.score` for aggregated trust scores that combine VEX, reachability, runtime, and other signals with decay/weighting.
**Weight Manifests:**
EWS weights are externalized to versioned JSON manifests in `etc/weights/`. The unified score facade (`IUnifiedScoreService`) loads weights from these manifests rather than using compiled defaults, enabling auditable weight changes without code modifications. See [Unified Score Architecture](../../technical/scoring-algebra.md) §4 for manifest schema and versioning rules.
### 3.2 - License compliance configuration
License compliance evaluation runs during SBOM evaluation when enabled in
@@ -856,4 +860,141 @@ The following product advisories provide strategic context for Policy Engine fea
---
*Last updated: 2025-12-26 (Sprint 006).*
## 13 · Policy Interop Layer
> **Sprint:** SPRINT_20260122_041_Policy_interop_import_export_rego
The Interop Layer provides bidirectional policy exchange between Stella's native C# gate engine and OPA/Rego. The C# engine remains primary; Rego serves as an interoperability adapter for teams using OPA-based toolchains.
### 13.1 · Supported Formats
| Format | Schema | Direction | Notes |
|--------|--------|-----------|-------|
| **PolicyPack v2 (JSON)** | `policy.stellaops.io/v2` | Import + Export | Canonical format with typed gates, environment overrides, remediation hints |
| **OPA/Rego** | `package stella.release` | Export (+ Import with pattern matching) | Deny-by-default pattern, `remediation` output rules |
### 13.2 · Architecture
```mermaid
graph TD
subgraph Interop["StellaOps.Policy.Interop"]
Exporter[JsonPolicyExporter / RegoPolicyExporter]
Importer[JsonPolicyImporter / RegoPolicyImporter]
Validator[PolicySchemaValidator]
Generator[RegoCodeGenerator]
Resolver[RemediationResolver]
OPA[EmbeddedOpaEvaluator]
Detector[FormatDetector]
end
subgraph Consumers
CLI[stella policy export/import/validate/evaluate]
API[Platform API /api/v1/policy/interop]
UI[Policy Editor UI]
end
CLI --> Exporter
CLI --> Importer
CLI --> Validator
API --> Exporter
API --> Importer
API --> Validator
UI --> API
Exporter --> Generator
Exporter --> Resolver
Importer --> Detector
Importer --> OPA
Generator --> Resolver
```
### 13.3 · Gate-to-Rego Translation
Each C# gate type maps to a Rego deny rule pattern:
| Gate Type | Rego Pattern | Remediation Code |
|-----------|-------------|-----------------|
| `CvssThresholdGate` | `input.cvss.score >= threshold` | `CVSS_EXCEED` |
| `SignatureRequiredGate` | `not input.dsse.verified` | `SIG_MISS` |
| `EvidenceFreshnessGate` | `not input.freshness.tstVerified` | `FRESH_EXPIRED` |
| `SbomPresenceGate` | `not input.sbom.canonicalDigest` | `SBOM_MISS` |
| `MinimumConfidenceGate` | `input.confidence < threshold` | `CONF_LOW` |
| `UnknownsBudgetGate` | `input.unknownsRatio > threshold` | `UNK_EXCEED` |
| `ReachabilityRequirementGate` | `not input.reachability.status` | `REACH_REQUIRED` |
### 13.4 · Remediation Hints
When a gate blocks, the system resolves structured remediation hints:
```
Priority: Gate-defined hint > Built-in defaults > null
RemediationHint:
Code: Machine-readable (e.g., "CVSS_EXCEED")
Title: Human-readable summary
Actions[]: CLI command templates with {placeholders}
References: External documentation links
Severity: critical | high | medium | low
```
Placeholders (`{purl}`, `{image}`, `{reason}`) are resolved via `RemediationContext` at evaluation time.
### 13.5 · Determinism
All exports and evaluations are deterministic:
- Same policy + same input = same output (hash-verifiable)
- Exports include SHA-256 `digest` field
- No time-dependent logic in deterministic mode
- `outputDigest` in evaluation results enables replay verification
### 13.6 · Implementation Reference
| Component | Source File |
|-----------|-------------|
| Contracts | `src/Policy/__Libraries/StellaOps.Policy.Interop/Contracts/PolicyPackDocument.cs` |
| Remediation Models | `src/Policy/__Libraries/StellaOps.Policy.Interop/Contracts/RemediationModels.cs` |
| Interfaces | `src/Policy/__Libraries/StellaOps.Policy.Interop/Abstractions/` |
| JSON Exporter | `src/Policy/__Libraries/StellaOps.Policy.Interop/Export/JsonPolicyExporter.cs` |
| JSON Importer | `src/Policy/__Libraries/StellaOps.Policy.Interop/Import/JsonPolicyImporter.cs` |
| Rego Generator | `src/Policy/__Libraries/StellaOps.Policy.Interop/Rego/RegoCodeGenerator.cs` |
| Rego Importer | `src/Policy/__Libraries/StellaOps.Policy.Interop/Import/RegoPolicyImporter.cs` |
| Embedded OPA | `src/Policy/__Libraries/StellaOps.Policy.Interop/Evaluation/EmbeddedOpaEvaluator.cs` |
| Remediation Resolver | `src/Policy/__Libraries/StellaOps.Policy.Interop/Evaluation/RemediationResolver.cs` |
| Format Detector | `src/Policy/__Libraries/StellaOps.Policy.Interop/Import/FormatDetector.cs` |
| Schema Validator | `src/Policy/__Libraries/StellaOps.Policy.Interop/Validation/PolicySchemaValidator.cs` |
| CLI Commands | `src/Cli/StellaOps.Cli/Commands/Policy/PolicyInteropCommandGroup.cs` |
| Platform API | `src/Platform/StellaOps.Platform.WebService/Endpoints/PolicyInteropEndpoints.cs` |
| JSON Schema | `docs/schemas/policy-pack-v2.schema.json` |
### 13.7 · CLI Interface
```bash
# Export to Rego
stella policy export --file policy.json --format rego --output-file release.rego
# Import with validation
stella policy import --file external.rego --validate-only
# Validate policy document
stella policy validate --file policy.json --strict
# Evaluate with remediation hints
stella policy evaluate --policy baseline.json --input evidence.json --environment production
```
Exit codes: `0` = success/allow, `1` = warn, `2` = block/errors, `10` = input-error, `12` = policy-error.
### 13.8 · Platform API
Group: `/api/v1/policy/interop` with tag `PolicyInterop`
| Method | Path | Auth Policy | Description |
|--------|------|-------------|-------------|
| POST | `/export` | `platform.policy.read` | Export policy to format |
| POST | `/import` | `platform.policy.write` | Import policy from format |
| POST | `/validate` | `platform.policy.read` | Validate policy document |
| POST | `/evaluate` | `platform.policy.evaluate` | Evaluate policy against input |
| GET | `/formats` | `platform.policy.read` | List supported formats |
---
*Last updated: 2026-01-23 (Sprint 041).*

View File

@@ -0,0 +1,219 @@
# Policy Import/Export Guide
This guide covers bidirectional policy exchange between Stella's native C# engine and OPA/Rego.
## Overview
Stella supports two policy formats:
- **PolicyPack v2 (JSON)**: Canonical format with typed gates, environment overrides, and remediation hints.
- **OPA/Rego**: Standard policy-as-code format for interoperability with OPA-based toolchains.
The C# gate engine remains primary. Rego is an export target for teams using OPA, and an import source for adopting external policies.
## Formats
### PolicyPack v2 (JSON)
Schema: `policy.stellaops.io/v2`
Structure:
```json
{
"apiVersion": "policy.stellaops.io/v2",
"kind": "PolicyPack",
"metadata": { "name": "...", "version": "1.0.0" },
"spec": {
"settings": { "defaultAction": "block", "deterministicMode": true },
"gates": [...],
"rules": [...]
}
}
```
Key features:
- Per-environment configuration overrides (production/staging/development thresholds)
- Structured remediation hints with CLI command templates
- Deterministic evaluation mode
- SHA-256 content digest for integrity
### OPA/Rego
Generated or imported Rego follows the deny-by-default pattern:
```rego
package stella.release
import rego.v1
default allow := false
deny contains msg if {
not input.dsse.verified
msg := "DSSE signature missing"
}
allow if { count(deny) == 0 }
remediation contains hint if {
some msg in deny
msg == "DSSE signature missing"
hint := {"code": "DSSE_MISS", "fix": "...", "severity": "critical"}
}
```
## CLI Commands
### Export
Export a policy to JSON or Rego:
```bash
# Export to Rego
stella policy export --file policy.json --format rego --output-file release.rego
# Export with environment-specific thresholds
stella policy export --file policy.json --format rego --environment production
# Export without remediation hints
stella policy export --file policy.json --format json --include-remediation false
# Export to stdout (pipe-friendly)
stella policy export --file policy.json --format rego | opa check -
```
### Import
Import a policy from JSON or Rego:
```bash
# Import and validate a JSON policy
stella policy import --file production-baseline.json
# Import with validation only (no persist)
stella policy import --file external-policy.rego --validate-only
# Dry-run to preview changes
stella policy import --file new-rules.json --dry-run
# Force format detection
stella policy import --file rules.txt --format rego
```
### Validate
Validate a policy file:
```bash
# Basic validation
stella policy validate --file policy.json
# Strict mode (warnings become errors)
stella policy validate --file policy.json --strict
# JSON output for CI integration
stella policy validate --file policy.json --output json
```
Exit codes: `0` = valid, `1` = warnings, `2` = errors.
### Evaluate
Evaluate a policy against evidence:
```bash
# Evaluate with table output
stella policy evaluate --policy baseline.json --input evidence.json
# With environment override
stella policy evaluate --policy baseline.json --input evidence.json --environment staging
# JSON output for programmatic use
stella policy evaluate --policy baseline.json --input evidence.json --output json
# CI mode (GitHub Actions annotations)
stella policy evaluate --policy baseline.json --input evidence.json --output ci
```
Exit codes: `0` = allow, `1` = warn, `2` = block.
## Evidence Input Format
The evaluation input follows the canonical evidence JSON schema:
```json
{
"environment": "production",
"subject": {
"imageDigest": "sha256:abc...",
"purl": "pkg:docker/myapp@1.0.0",
"tags": ["env:prod"]
},
"dsse": { "verified": true, "signers": ["ca://fulcio/..."] },
"rekor": { "verified": true, "logID": "...", "integratedTime": 1737480000 },
"sbom": { "format": "cyclonedx-1.6", "canonicalDigest": "sha256:..." },
"freshness": { "tstVerified": true, "timestamp": "2026-01-22T10:00:00Z", "maxAgeHours": 24 },
"cvss": { "score": 7.5, "version": "3.1" },
"reachability": { "status": "confirmed", "confidence": 0.85 },
"confidence": 0.82
}
```
## Remediation Hints
When a gate blocks, the CLI displays actionable fix suggestions:
```
Decision: BLOCK
Gate Type Result Reason
signature SignatureRequiredGate FAIL Required signature missing
sbom SbomPresenceGate PASS passed
Remediation:
SIG_MISS: Required signature missing
- Sign attestation with DSSE.
$ stella attest attach --sign --image sha256:abc...
- Anchor attestation in Rekor.
$ stella attest attach --rekor --image sha256:abc...
```
## Rego Import Behavior
When importing Rego files, the system:
1. Parses `deny` rules and maps known patterns to native gates (CVSS comparisons, boolean checks).
2. Extracts `remediation` rules into structured hints.
3. Unknown patterns are preserved and evaluated via the embedded OPA evaluator.
4. Validation reports which rules mapped natively vs. remain OPA-evaluated.
## Determinism
All evaluations are deterministic:
- Same policy + same input = same output (hash-verifiable)
- No time-dependent logic in deterministic mode
- `outputDigest` in evaluation results enables replay verification
## API Endpoints
The Platform API exposes policy interop at `/api/v1/policy/interop`:
| Method | Path | Description |
|--------|------|-------------|
| POST | `/export` | Export policy to format |
| POST | `/import` | Import policy from format |
| POST | `/validate` | Validate policy document |
| POST | `/evaluate` | Evaluate policy against input |
| GET | `/formats` | List supported formats |
## Gate Types
Supported gate types with Rego translation:
| Gate Type | Rego Pattern | Remediation Code |
|-----------|-------------|-----------------|
| `CvssThresholdGate` | `input.cvss.score >= threshold` | `CVSS_EXCEED` |
| `SignatureRequiredGate` | `not input.dsse.verified` | `SIG_MISS` |
| `EvidenceFreshnessGate` | `not input.freshness.tstVerified` | `FRESH_EXPIRED` |
| `SbomPresenceGate` | `not input.sbom.canonicalDigest` | `SBOM_MISS` |
| `MinimumConfidenceGate` | `input.confidence < threshold` | `CONF_LOW` |
| `UnknownsBudgetGate` | `input.unknownsRatio > threshold` | `UNK_EXCEED` |
| `ReachabilityRequirementGate` | `not input.reachability.status` | `REACH_REQUIRED` |

View File

@@ -0,0 +1,198 @@
# Runtime Linkage Verification Guide
> **Ownership:** Scanner Guild / Signals Guild
> **Services:** `StellaOps.Scanner.Reachability.FunctionMap`
> **API:** `POST /api/v1/function-maps`, `POST /api/v1/function-maps/{id}/verify`
> **CLI:** `stella function-map generate|verify`, `stella observations query`
## What is Runtime Linkage Verification?
Runtime linkage verification bridges the gap between **static analysis** (what code _could_ run) and **runtime observation** (what code _actually_ runs). It works by:
1. **Generating a function map** from static analysis (SBOM + call graph) that declares expected call paths
2. **Deploying probes** (eBPF uprobes/kprobes) to observe actual function invocations at runtime
3. **Verifying** that observed call patterns match the expected static model
This produces a confidence metric (observation rate) quantifying how much of the declared attack surface has been confirmed by runtime evidence.
---
## When to Use Function Maps
| Scenario | Benefit |
|----------|---------|
| **High-risk vulnerabilities** | Confirm whether vulnerable code paths are actually exercised |
| **Reachability disputes** | Resolve static "maybe reachable" findings with runtime evidence |
| **Compliance audits** | Provide cryptographic proof of runtime behavior |
| **Air-gapped environments** | Bundle function maps and observations for offline verification |
| **Continuous monitoring** | Track coverage drift over deployment lifecycle |
---
## Step-by-Step Guide
### 1. Generate a Function Map
Create a function map predicate from your SBOM and optional static analysis:
```bash
stella function-map generate \
--sbom ./app.cdx.json \
--service my-backend \
--hot-functions "crypto/*" --hot-functions "auth/*" \
--min-rate 0.95 \
--window 1800 \
--output function-map.json
```
**Key options:**
- `--hot-functions`: Glob patterns for functions of interest (crypto, auth, network are common)
- `--min-rate`: Minimum observation rate to consider "verified" (default 0.95 = 95%)
- `--window`: Observation window in seconds (default 1800 = 30 minutes)
- `--static-analysis`: Path to static analysis results for richer call paths
The output is a JSON predicate conforming to `https://stella.ops/predicates/function-map/v1`.
### 2. Deploy Probes
Configure the Stella runtime agent to attach probes for the functions declared in your map. The agent uses eBPF to observe function calls without modifying application code.
Supported probe types:
- `uprobe` / `uretprobe` — User-space function entry/exit
- `kprobe` / `kretprobe` — Kernel function entry/exit
- `tracepoint` — Kernel tracepoints
- `usdt` — User-space statically defined tracing
The runtime agent writes observations in NDJSON format with fields:
- `node_hash` — SHA-256(PURL + normalized symbol)
- `function_name` — Observed function symbol
- `probe_type` — How it was observed
- `observed_at` — Timestamp
- `container_id`, `pod_name`, `namespace` — Context
### 3. Verify Observations Against the Map
After accumulating observations, verify coverage:
```bash
stella function-map verify \
--function-map function-map.json \
--from "2026-01-23T00:00:00Z" \
--to "2026-01-23T01:00:00Z" \
--format table
```
For offline verification with a bundled observations file:
```bash
stella function-map verify \
--function-map function-map.json \
--offline \
--observations observations.ndjson \
--format json
```
**Output includes:**
- `verified`: Whether observation rate meets the threshold
- `observation_rate`: Fraction of expected paths confirmed (0.0-1.0)
- `target_rate`: Required rate from the function map
- `per_path_breakdown`: Status of each declared call path
- `unexpected_symbols`: Functions observed but not in the map
- `missing_symbols`: Expected functions not yet observed
### 4. Upload to Platform (Optional)
Store function maps in the Platform for centralized management:
```bash
# Create via API
curl -X POST /api/v1/function-maps \
-H "Content-Type: application/json" \
-d @function-map.json
# Verify via API
curl -X POST /api/v1/function-maps/{id}/verify \
-H "Content-Type: application/json" \
-d '{"observations": [...]}'
# Check coverage dashboard
curl GET /api/v1/function-maps/{id}/coverage
```
---
## Predicate Schema
Function maps use the in-toto attestation framework with predicate type:
```
https://stella.ops/predicates/function-map/v1
```
See [Function Map V1 Contract](../../../contracts/function-map-v1.md) for the full schema specification.
---
## Integration with Air-Gap Bundles
Function maps, observations, and verification reports can be included in offline bundles:
```
bundle.stella.bundle.tgz
├── function-maps/
│ ├── {service}-function-map.json
│ └── {service}-function-map.dsse.json
├── observations/
│ └── {date-label}-observations.ndjson
└── verification/
├── verification-report.json
└── verification-report.dsse.json
```
See [Offline Bundle Format](../../airgap/guides/offline-bundle-format.md) for artifact type details.
---
## Troubleshooting
### Low Observation Rate
**Symptom:** Verification reports `observation_rate < target_rate`.
**Causes:**
- Observation window too short — increase `--window` or widen `--from`/`--to`
- Probes not attached — check runtime agent logs for attachment failures
- Application hasn't exercised the code paths — generate representative load
- Binary stripped or ASLR — provide `--binary-path` hints in the function map
**Resolution:**
1. Use `stella observations query --summary` to see what's been collected
2. Check per-path breakdown for which specific paths are unobserved
3. Extend the observation window or trigger relevant application behavior
### Unexpected Symbols
**Symptom:** Verification reports unexpected function calls not in the map.
**Causes:**
- Dynamic dispatch or reflection invoking functions not in static analysis
- Shared libraries loaded at runtime that weren't in the SBOM
- Hot functions pattern too narrow
**Resolution:**
1. Regenerate the function map with broader `--hot-functions` patterns
2. Add the unexpected symbols as optional paths if they're benign
3. Set `--fail-on-unexpected false` if unexpected calls should be informational only
### Node Hash Mismatch
**Symptom:** Observations exist but don't match expected node hashes.
**Causes:**
- PURL mismatch between SBOM and runtime (version drift)
- Symbol name normalization differences (C++ mangling, etc.)
**Resolution:**
1. Verify the PURL in observations matches the function map subject
2. Check that symbol names are normalized consistently (same demangling rules)
3. Regenerate the function map with the current deployed SBOM version

View File

@@ -0,0 +1,253 @@
# Unified Trust Score
> **Ownership:** Signals Guild / Platform Guild
> **Services:** `StellaOps.Signals.UnifiedScore`
> **API:** `POST /api/v1/score/evaluate`, `GET /api/v1/score/{id}/replay`
> **CLI:** `stella score compute|explain|replay|verify`, `stella gate score evaluate`
## Overview
The Unified Trust Score is a facade over existing EWS (Evidence-Weighted Score) and Determinization systems. It provides a single API for computing risk scores, uncertainty metrics, and score replay proofs without replacing any underlying scoring logic.
---
## How It Works
1. **Input** — Caller provides signal values (reachability, runtime, exploit, etc.) and optional context (CVE ID, PURL, SBOM ref)
2. **EWS computation** — The facade delegates to `IEvidenceWeightedScoreCalculator` using weights from a versioned manifest
3. **Entropy calculation**`IUncertaintyScoreCalculator` computes the unknowns fraction (U) from signal presence/absence
4. **Conflict detection**`IConflictDetector` identifies contradictory signals
5. **Delta calculation** — For missing signals, computes potential score impact ranges
6. **Result assembly** — Returns `UnifiedScoreResult` combining all outputs
---
## The Unknowns Fraction (U)
The `UnknownsFraction` exposes how much of the score depends on absent data:
```
U = 1 - (weighted_present_signals / total_weight)
```
### Unknowns Bands
| U Range | Band | Meaning | Recommended Action |
|---------|------|---------|-------------------|
| 0.0 0.2 | **Complete** | All signals present | Automated decisions safe |
| 0.2 0.4 | **Adequate** | Sufficient signal coverage | Automated decisions safe |
| 0.4 0.6 | **Sparse** | Signal gaps exist | Manual review recommended |
| 0.6 1.0 | **Insufficient** | Critical data missing | Block until more signals arrive |
Band thresholds align with Determinization configuration:
- `RefreshEntropyThreshold: 0.40` — triggers signal refresh attempt
- `ManualReviewEntropyThreshold: 0.60` — requires human review
---
## Delta-If-Present
When signals are absent, the facade calculates how the score would change if each missing signal were provided:
```json
{
"signal": "reachability",
"min_impact": -15,
"max_impact": 8,
"weight": 0.30,
"description": "If reachability confirmed as not-reachable, score decreases by up to 15"
}
```
This helps operators prioritize which signals to gather first.
---
## Weight Manifests
EWS weights are stored in versioned JSON files under `etc/weights/`:
```
etc/weights/v2026-01-22.weights.json
```
Manifests are:
- **Immutable** once published
- **Content-addressed** via SHA-256 hash
- **Pinnable** by policy rules via `weights_ref`
- **Auditable** — the manifest version and hash are included in every score result
See [Scoring Algebra §4](../../technical/scoring-algebra.md) for the manifest schema.
---
## API Endpoints
| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/api/v1/score/evaluate` | Compute unified score |
| `GET` | `/api/v1/score/{scoreId}` | Retrieve previously computed score |
| `GET` | `/api/v1/score/weights` | List weight manifest versions |
| `GET` | `/api/v1/score/weights/{version}` | Get specific manifest |
| `GET` | `/api/v1/score/weights/effective` | Get effective manifest for a date |
| `GET` | `/api/v1/score/{scoreId}/replay` | Fetch signed replay proof |
| `POST` | `/api/v1/score/verify` | Verify a replay log |
### Evaluate Request
```json
{
"cve_id": "CVE-2024-1234",
"purl": "pkg:npm/lodash@4.17.0",
"signals": {
"reachability": 0.9,
"runtime": 0.7,
"exploit": 0.3,
"backport": 0.0,
"source": 0.5,
"mitigation": 0.0
},
"options": {
"include_breakdown": true,
"include_delta": true,
"weight_set_id": "v2026-01-22"
}
}
```
### Evaluate Response (key fields)
```json
{
"score_id": "score_a1b2c3d4e5f67890",
"score_value": 72,
"bucket": "ScheduleNext",
"unknowns_fraction": 0.15,
"unknowns_band": "Complete",
"weight_manifest": {
"version": "v2026-01-22",
"content_hash": "sha256:..."
},
"ews_digest": "sha256:...",
"determinization_fingerprint": "sha256:...",
"computed_at": "2026-01-23T10:00:00Z"
}
```
---
## CLI Commands
### `stella score compute`
Compute a unified score from signal values:
```bash
stella score compute \
--finding-id CVE-2024-1234@pkg:npm/lodash@4.17.0 \
--cvss 7.5 --epss 0.15 \
--reachability 0.9 --runtime 0.7 \
--format table
```
### `stella score explain`
Show a detailed breakdown of a score:
```bash
stella score explain CVE-2024-1234@pkg:npm/lodash@4.17.0
```
### `stella score replay`
Fetch the signed replay proof for a previously computed score:
```bash
stella score replay score_a1b2c3d4e5f67890
```
### `stella score verify`
Re-execute the computation and verify it matches the original:
```bash
stella score verify score_a1b2c3d4e5f67890
```
### `stella gate score evaluate` (enhanced)
Existing gate command with new flags:
```bash
stella gate score evaluate \
--finding-id CVE-2024-1234@pkg:npm/lodash \
--cvss 7.5 --epss 0.15 \
--show-unknowns --show-deltas \
--weights-version v2026-01-22
```
### `stella gate score weights`
Manage weight manifests:
```bash
stella gate score weights list
stella gate score weights show v2026-01-22
stella gate score weights diff v2026-01-22 v2026-02-01
```
---
## Score Replay and Verification
Every computed score can produce a **replay proof** — a DSSE-signed attestation (payload type `application/vnd.stella.score+json`) that records:
1. Canonical input hashes (SBOM, VEX, etc.)
2. Transform versions applied (canonicalization, normalization, decay)
3. Step-by-step algebra decisions (signal × weight = contribution)
4. Final score and metadata
Replay proofs enable:
- **Independent verification** — auditors re-execute the computation
- **Transparency logging** — optional anchoring to Rekor for non-repudiation
- **OCI storage** — proofs stored as OCI referrers ("StellaBundle" pattern)
---
## Troubleshooting
### High Unknowns Fraction (U > 0.6)
**Symptom:** Score shows "Insufficient" band, decisions are blocked.
**Causes:**
- Missing reachability analysis (run `stella scan` with `--reachability`)
- No VEX data available (check VEX feed configuration)
- Runtime observations not collected (configure runtime agent)
**Resolution:**
1. Run `stella score explain <finding-id>` to see which signals are missing
2. Use `--show-deltas` to understand which signals would have the most impact
3. Prioritize gathering signals with the highest weight × delta
### Score Disagrees with CVSS
**Symptom:** EWS score is much lower than expected from CVSS alone.
**Explanation:** EWS incorporates reachability, runtime, backport, and mitigation signals that CVSS does not. A high-CVSS vulnerability that is not reachable or already mitigated will have a lower EWS score.
**Resolution:** Run `stella score explain` to see the per-dimension breakdown and understand which signals are reducing the score.
### Replay Verification Fails
**Symptom:** `stella score verify` reports `score_matches: false`.
**Causes:**
- Weight manifest version changed between compute and verify
- Signal inputs were modified after scoring
- Non-determinism in signal providers (check for time-dependent signals)
**Resolution:**
1. Pin the weight manifest version in the verify request
2. Ensure canonical inputs match (compare SHA-256 hashes)
3. Check the `differences` field in the verify response for specific mismatches

View File

@@ -503,3 +503,181 @@ webhooks:
- Health endpoints: `/health/liveness`, `/health/readiness`, `/status`, `/surface/fs/cache/status` (see runbook).
- Alert hints: deny spikes, latency > 800ms p99, cache freshness lag > 10m, any secrets failure.
---
## 17) Offline Witness Verification
> **Sprint:** SPRINT_20260122_038_Scanner_ebpf_probe_type (EBPF-004)
This section documents the deterministic replay verification algorithm for runtime witnesses, enabling air-gapped environments to independently verify witness attestations.
### 17.1 Input Canonicalization (RFC 8785 JCS)
All witness payloads MUST be canonicalized before hashing or signing using **JSON Canonicalization Scheme (JCS)** per RFC 8785:
1. **Property ordering**: Object properties are sorted lexicographically by key name (Unicode code point order).
2. **Number serialization**: Numbers are serialized without unnecessary precision; integers as integers, decimals with minimal representation.
3. **String encoding**: UTF-8 with no BOM; escape sequences normalized to `\uXXXX` form for control characters.
4. **Whitespace**: No insignificant whitespace between tokens.
5. **Null handling**: Explicit `null` values are preserved; absent keys are omitted.
**Canonicalization algorithm:**
```
function canonicalize(json_object):
if json_object is null:
return "null"
if json_object is boolean:
return "true" | "false"
if json_object is number:
return serialize_number(json_object) # RFC 8785 §3.2.2.3
if json_object is string:
return quote(escape(json_object))
if json_object is array:
return "[" + join(",", [canonicalize(elem) for elem in json_object]) + "]"
if json_object is object:
keys = sorted(json_object.keys(), key=unicode_codepoint_order)
pairs = [quote(key) + ":" + canonicalize(json_object[key]) for key in keys]
return "{" + join(",", pairs) + "}"
```
### 17.2 Observation Ordering Rules
When a witness contains multiple observations (e.g., from eBPF probes), they MUST be ordered deterministically before hashing:
1. **Primary sort**: By `observedAt` timestamp (UTC, ascending)
2. **Secondary sort**: By `nodeHash` (lexicographic ascending)
3. **Tertiary sort**: By `observationId` (lexicographic ascending, for tie-breaking)
**Observation hash computation:**
```
function compute_observations_hash(observations):
sorted_observations = sort(observations,
key=lambda o: (o.observedAt, o.nodeHash, o.observationId))
canonical_array = []
for obs in sorted_observations:
canonical_array.append({
"observedAt": obs.observedAt.toISOString(),
"nodeHash": obs.nodeHash,
"functionName": obs.functionName,
"probeType": obs.probeType, # EBPF-001: kprobe|uprobe|tracepoint|usdt|fentry|fexit
"containerHash": sha256(obs.containerId + obs.podName + obs.namespace)
})
return sha256(canonicalize(canonical_array))
```
### 17.3 Signature Verification Sequence
Offline verification MUST follow this exact sequence to ensure deterministic results:
1. **Parse DSSE envelope**: Extract `payloadType`, `payload` (base64-decoded), and `signatures[]`.
2. **Verify payload hash**:
```
expected_hash = sha256(payload_bytes)
assert envelope.payload_sha256 == expected_hash
```
3. **Verify DSSE signature(s)**: For each signature in `signatures[]`:
```
pae_string = "DSSEv1 " + len(payloadType) + " " + payloadType + " " + len(payload) + " " + payload
verify_signature(signature.sig, pae_string, get_public_key(signature.keyid))
```
4. **Verify Rekor inclusion** (if present):
```
fetch_or_load_checkpoint(rekor_log_id)
verify_merkle_inclusion(entry_hash, inclusion_proof, checkpoint.root_hash)
verify_checkpoint_signature(checkpoint, rekor_public_key)
```
5. **Verify timestamp** (if RFC 3161 TST present):
```
verify_tst_signature(tst, tsa_certificate)
assert tst.timestamp <= now() + allowed_skew
```
6. **Verify witness content**:
```
witness = parse_json(payload)
recomputed_observations_hash = compute_observations_hash(witness.observations)
assert witness.observationsDigest == recomputed_observations_hash
```
### 17.4 Offline Bundle Structure Requirements
A StellaBundle for offline witness verification MUST include:
```
bundle/
├── manifest.json # Bundle manifest v2.0.0
├── witnesses/
│ └── <claim_id>.witness.dsse.json # DSSE-signed witness
├── proofs/
│ ├── rekor-inclusion.json # Rekor inclusion proof
│ ├── checkpoint.json # Rekor checkpoint (signed)
│ └── rfc3161-tst.der # Optional RFC 3161 timestamp
├── observations/
│ └── observations.ndjson # Raw observations (for replay)
├── keys/
│ ├── signing-key.pub # Public key for DSSE verification
│ └── rekor-key.pub # Rekor log public key
└── trust/
└── trust-root.json # Trust anchors for key verification
```
**Manifest schema (witnesses section):**
```json
{
"schemaVersion": "2.0.0",
"artifacts": [
{
"type": "witness",
"path": "witnesses/<claim_id>.witness.dsse.json",
"digest": "sha256:...",
"predicateType": "https://stella.ops/predicates/runtime-witness/v1",
"proofs": {
"rekor": "proofs/rekor-inclusion.json",
"checkpoint": "proofs/checkpoint.json",
"tst": "proofs/rfc3161-tst.der"
},
"observationsRef": "observations/observations.ndjson"
}
]
}
```
### 17.5 Verification CLI Commands
```bash
# Verify a witness bundle offline
stella bundle verify --bundle witness-bundle.tar.gz --offline
# Verify with replay (recompute observations hash)
stella bundle verify --bundle witness-bundle.tar.gz --offline --replay
# Verify specific witness from bundle
stella witness verify --bundle witness-bundle.tar.gz --witness-id wit:sha256:abc123 --offline
# Export verification report
stella witness verify --bundle witness-bundle.tar.gz --offline --output report.json
```
### 17.6 Determinism Guarantees
The verification algorithm guarantees:
1. **Idempotent**: Running verification N times produces identical results.
2. **Reproducible**: Different systems with the same bundle produce identical verification outcomes.
3. **Isolated**: Verification requires no network access (fully air-gapped).
4. **Auditable**: Every step produces evidence that can be independently checked.
**Test criteria** (per advisory):
- Offline verifier reproduces the same mapping on 3 separate air-gapped runs.
- No randomness in canonicalization, ordering, or hash computation.
- Timestamps use UTC with fixed precision (milliseconds).