finish off sprint advisories and sprints
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
223
docs/modules/cli/guides/delta-attestation-workflow.md
Normal file
223
docs/modules/cli/guides/delta-attestation-workflow.md
Normal 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)
|
||||
@@ -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).*
|
||||
|
||||
219
docs/modules/policy/guides/policy-import-export.md
Normal file
219
docs/modules/policy/guides/policy-import-export.md
Normal 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` |
|
||||
198
docs/modules/scanner/guides/runtime-linkage.md
Normal file
198
docs/modules/scanner/guides/runtime-linkage.md
Normal 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
|
||||
253
docs/modules/signals/unified-score.md
Normal file
253
docs/modules/signals/unified-score.md
Normal 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
|
||||
@@ -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).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user