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

@@ -0,0 +1,237 @@
# Function Map V1 Contract
> **Predicate Type:** `https://stella.ops/predicates/function-map/v1`
> **DSSE Payload Type:** `application/vnd.stellaops.function-map.v1+json`
> **Schema Version:** `1.0.0`
## Overview
A function map predicate declares the expected call paths for a service component, enabling verification of runtime behavior against static analysis. It follows the [in-toto attestation](https://github.com/in-toto/attestation) framework.
---
## Predicate Schema
```json
{
"type": "https://stella.ops/predicates/function-map/v1",
"subject": {
"purl": "pkg:oci/my-service@sha256:abc123...",
"digest": { "sha256": "abc123..." }
},
"predicate": {
"service": "my-backend",
"build_id": "build-456",
"expected_paths": [...],
"coverage": {
"min_observation_rate": 0.95,
"window_seconds": 1800,
"fail_on_unexpected": false
},
"generated_at": "2026-01-23T10:00:00Z",
"generated_from": {
"sbom_ref": "oci://registry/sbom@sha256:...",
"static_analysis_ref": "oci://registry/analysis@sha256:..."
},
"generator": {
"name": "stella-cli",
"version": "2.0.0",
"commit": "abc123"
}
}
}
```
---
## Subject
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `purl` | string | Yes | Package URL of the subject artifact |
| `digest` | object | Yes | Content digest (sha256, sha512, etc.) |
---
## Predicate Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `service` | string | Yes | Service name for correlation |
| `build_id` | string | No | Build identifier for provenance correlation |
| `expected_paths` | array | Yes | List of expected call paths |
| `coverage` | object | Yes | Coverage thresholds for verification |
| `generated_at` | ISO 8601 | Yes | Generation timestamp |
| `generated_from` | object | No | Source references (SBOM, static analysis) |
| `generator` | object | No | Tool that generated the predicate |
---
## Expected Path
Each expected path represents a call chain starting from an entrypoint:
```json
{
"path_id": "path-001",
"entrypoint": {
"symbol": "handleRequest",
"node_hash": "sha256:..."
},
"expected_calls": [
{
"symbol": "crypto_sign",
"purl": "pkg:deb/libcrypto3@3.0.0",
"node_hash": "sha256:...",
"probe_types": ["uprobe"],
"optional": false,
"function_address": null,
"binary_path": "/usr/lib/libcrypto.so.3"
}
],
"path_hash": "sha256:...",
"optional": false,
"strict_ordering": false,
"tags": ["crypto"]
}
```
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path_id` | string | Yes | Unique path identifier |
| `entrypoint` | object | Yes | Path entry point (symbol + node_hash) |
| `expected_calls` | array | Yes | List of expected function calls |
| `path_hash` | string | Yes | SHA-256(entrypoint \|\| sorted calls) |
| `optional` | boolean | No | Whether this path is optional (default false) |
| `strict_ordering` | boolean | No | Ordered sequence vs unordered set (default false) |
| `tags` | array | No | Categorization tags (crypto, auth, network, etc.) |
---
## Expected Call
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `symbol` | string | Yes | Function name (demangled) |
| `purl` | string | Yes | Package URL of the component containing this function |
| `node_hash` | string | Yes | SHA-256(PURL + normalized symbol) |
| `probe_types` | array | Yes | Acceptable probe types for observation |
| `optional` | boolean | No | Whether this call is optional (default false) |
| `function_address` | string | No | Address hint for probe attachment |
| `binary_path` | string | No | Binary path for uprobe attachment |
### Probe Types
| Type | Description |
|------|-------------|
| `kprobe` | Kernel function entry |
| `kretprobe` | Kernel function return |
| `uprobe` | User-space function entry |
| `uretprobe` | User-space function return |
| `tracepoint` | Kernel tracepoint |
| `usdt` | User-space statically defined tracing |
---
## Coverage Thresholds
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `min_observation_rate` | double | 0.95 | Minimum fraction of paths that must be observed |
| `window_seconds` | integer | 1800 | Observation window duration |
| `fail_on_unexpected` | boolean | false | Whether unexpected symbols cause verification failure |
---
## Node Hash Recipe
Node hashes provide content-addressable identifiers for function calls, matching the [Witness V1](witness-v1.md) convention:
```
node_hash = SHA-256(PURL + ":" + normalize(symbol))
```
Where `normalize(symbol)`:
1. Demangle C++/Rust symbols
2. Strip leading underscores (platform convention)
3. Lowercase the result
4. Remove whitespace
### Path Hash Recipe
```
path_hash = SHA-256(entrypoint.node_hash + ":" + sort(calls.map(c => c.node_hash)).join(":"))
```
The path hash is independent of call ordering (sorted) unless `strict_ordering` is true, in which case calls are not sorted before hashing.
---
## Coverage Calculation Algorithm
```
total_required = count(paths where optional == false)
observed_required = count(paths where optional == false AND has_matching_observation)
observation_rate = observed_required / total_required
= 0.0 if total_required == 0
verified = observation_rate >= coverage.min_observation_rate
```
For each path, an observation "matches" when:
- At least one observation has a `node_hash` matching any call in the path
- The observation falls within the time window
- The probe type is in the call's `probe_types` list
---
## Verification Algorithm
```
VERIFY(predicate, observations, options):
1. Filter observations to time window [now - window_seconds, now]
2. For each required expected_path:
a. For each expected_call in path:
- Find observations matching node_hash AND probe_type
- Mark call as "observed" if any match found
b. Mark path as "covered" if entrypoint OR any call observed
3. Compute observation_rate = covered_paths / required_paths
4. Collect unexpected = observations not matching any expected call
5. Collect missing = required calls with no matching observation
6. verified = observation_rate >= min_observation_rate
AND (NOT fail_on_unexpected OR unexpected.count == 0)
7. Return result with breakdown, unexpected, missing
```
---
## Media Types
| Usage | Media Type |
|-------|-----------|
| Function map predicate | `application/vnd.stella.function-map+json` |
| DSSE-signed predicate | `application/vnd.dsse+json` |
| Observations | `application/x-ndjson` |
| Verification report | `application/vnd.stella.verification-report+json` |
---
## Observation Record (NDJSON)
Each line in an observations file:
```json
{
"observation_id": "obs-123",
"node_hash": "sha256:...",
"function_name": "crypto_sign",
"probe_type": "uprobe",
"observed_at": "2026-01-23T10:05:00Z",
"observation_count": 42,
"container_id": "abc123",
"pod_name": "my-service-pod-xyz",
"namespace": "production",
"duration_microseconds": 150
}
```

View File

@@ -0,0 +1,51 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SBOM Volatile Fields Contract",
"description": "Authoritative list of SBOM fields stripped before canonicalization to ensure deterministic hashes. Referenced by SbomNormalizer.",
"version": 1,
"cyclonedx": {
"strip": [
{
"path": "serialNumber",
"scope": "root",
"rationale": "UUID regenerated on every BOM creation; not content-derived."
},
{
"path": "metadata.timestamp",
"scope": "metadata",
"rationale": "Generation timestamp varies per run; not content-derived."
},
{
"path": "metadata.tools",
"scope": "metadata",
"rationale": "Tool name/version/vendor varies across scanner installs; does not reflect scanned content."
},
{
"path": "metadata.authors",
"scope": "metadata",
"rationale": "Author identity varies per operator; does not affect component inventory."
}
],
"specVersions": ["1.4", "1.5", "1.6", "1.7"]
},
"spdx": {
"strip": [
{
"path": "creationInfo.created",
"scope": "creationInfo",
"rationale": "Timestamp of SPDX document creation; varies per run."
},
{
"path": "creationInfo.creators",
"scope": "creationInfo",
"rationale": "Tool identifiers include version strings (e.g., 'Tool: syft-1.2.3'); varies across installs."
},
{
"path": "creationInfo.licenseListVersion",
"scope": "creationInfo",
"rationale": "Tracks upstream SPDX license list version available at scan time; not content-derived."
}
],
"specVersions": ["2.2", "2.3", "3.0", "3.0.1"]
}
}

View File

@@ -1,258 +0,0 @@
# Sprint 20260120-029 · Delta Delivery Attestation (Planning Only)
## Topic & Scope
- **Status:** PLANNING - Not committed to implementation
- **Origin:** Product advisory on delta-sig attestation for verifiable update delivery
- **Purpose:** Scope the work required to extend delta-sig from CVE detection to delta patch delivery
- Working directory: `src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.DeltaSig`
- This document captures analysis and proposed tasks; no implementation is scheduled.
## Advisory Summary
The advisory proposes extending Stella Ops' delta-sig capabilities to support **patch delivery and reconstruction verification**, similar to:
- **Chromium Courgette/Zucchini:** Instruction-aware binary diffing for smaller patches
- **Microsoft MSDelta/LZX-delta:** Windows Update delta compression
- **deltarpm:** RPM delta packages that rebuild full RPMs from installed content
- **zchunk:** Chunk-based delta format with HTTP range requests and independent verification
### Current vs. Proposed Use Cases
| Aspect | Current Implementation | Advisory Proposal |
|--------|------------------------|-------------------|
| **Purpose** | CVE detection via signature matching | Patch delivery and reconstruction |
| **Question answered** | "Does this binary have the security patch?" | "Can I apply this delta to reconstruct the target?" |
| **Data flow** | Signature DB → Match target → Verdict | Base + Delta → Apply → Reconstruct target |
| **Output** | `vulnerable`/`patched` verdict | Reconstructed binary + verification |
## Gap Analysis
### Already Covered (No Gap)
1. **Function-level signatures** - v1 and v2 predicates with `DeltaSignature`, `SymbolSignature`, chunk hashes
2. **Multiple hash algorithms** - SHA-256/384/512, CFG edge hash, semantic hash
3. **Normalization recipes** - `NormalizationRef` with recipe ID, version, steps
4. **Deterministic signature generation** - Fully implemented
5. **IR-level semantic analysis** - v2 has `IrDiffReferenceV2` with CAS storage
6. **DSSE envelope signing** - Implemented via `DeltaSigAttestorIntegration`
7. **Reproducible rebuild infrastructure** - `IRebuildService` exists (for full packages)
### Identified Gaps
#### GAP-1: Base Artifact Reference for Delta Application
**Advisory requirement:** "Base artifact reference: canonical artifact ID + digest(s) of the required base."
**Current state:** `DeltaSigPredicateV2.Subject` is a single artifact. No field to specify base for reconstruction.
**Proposed schema extension:**
```json
{
"baselineReference": {
"purl": "pkg:deb/debian/openssl@1.1.1k-1",
"digest": { "sha256": "abc123..." },
"buildId": "...",
"requiredExact": true
}
}
```
#### GAP-2: Reconstruction Algorithm Fingerprint
**Advisory requirement:** "Algorithm fingerprint: `{courgette|zucchini|msdelta|deltarpm|zchunk}@version`"
**Current state:** `MatchAlgorithm` tracks detection algorithms, not reconstruction algorithms.
**Proposed schema extension:**
```json
{
"reconstructionAlgorithm": {
"algorithm": "zchunk",
"version": "1.5.2",
"dictionaryDigest": "sha256:def456..."
}
}
```
#### GAP-3: Chunk/Segment Map for Stream Verification
**Advisory requirement:** "Chunk/segment map: offsets, sizes, per-chunk hashes to stream-verify during apply."
**Current state:** `ChunkHash` designed for matching, not reconstruction verification.
**Proposed schema extension:**
```json
{
"segmentMap": [
{ "offset": 0, "size": 4096, "status": "unchanged", "hash": "..." },
{ "offset": 4096, "size": 512, "status": "modified", "oldHash": "...", "newHash": "..." },
{ "offset": 4608, "size": 1024, "status": "new", "hash": "..." }
]
}
```
#### GAP-4: Proof Reference to Build Info
**Advisory requirement:** "Human/readable `proof_ref`: link to buildinfo or exact reproduce-instructions."
**Current state:** `IRebuildService` exists but not linked from predicates.
**Proposed schema extension:**
```json
{
"proofRef": {
"buildinfoUrl": "https://buildinfo.example.com/openssl_1.1.1k-1.buildinfo",
"buildinfoDigest": "sha256:...",
"reproducibilityBackend": "reproduce.debian.net"
}
}
```
#### GAP-5: Two-Part Trust (Content + Manifest Signing)
**Advisory requirement:** "Two-part trust: code-sign the post-image AND sign the update manifest."
**Current state:** Single DSSE envelope signs the predicate.
**Proposed new type:**
```json
{
"manifestType": "https://stella-ops.org/delta-manifest/v1",
"baseArtifact": { "purl": "...", "digest": {...} },
"deltaArtifact": { "url": "...", "digest": {...}, "algorithm": "zchunk@1.5" },
"targetArtifact": { "purl": "...", "digest": {...} },
"predicateRef": "sha256:...",
"manifestSignatures": [...]
}
```
#### GAP-6: Reconstruction Service
**Advisory requirement:** "Reconstruction-first: given base + delta, reassemble in a clean sandbox."
**Current state:** No `IDeltaApplicationService`.
**Proposed interface:**
```csharp
public interface IDeltaApplicationService
{
Task<DeltaApplicationResult> ApplyAsync(
Stream baseArtifact,
Stream deltaArtifact,
ReconstructionAlgorithm algorithm,
CancellationToken ct);
Task<bool> VerifyReconstructionAsync(
Stream reconstructedArtifact,
string expectedDigest,
CancellationToken ct);
}
```
#### GAP-7: Acceptance Test Harness
**Advisory requirement:** "Signature/manifest checks, byte-for-byte equality."
**Current state:** No reconstruction tests.
**Required:** Test harness for base + delta → reconstruction → verification.
## Dependencies & Concurrency
- **Upstream:** Existing delta-sig v2 predicates (SPRINT_20260119_004 - DONE)
- **Upstream:** Reproducible rebuild infrastructure (SPRINT_20260119_005)
- **New dependency:** zchunk library or native binding
- **Optional:** Courgette/Zucchini (Chrome's algorithm) if PE/ELF optimization needed
- **Parallel-safe:** Schema design can proceed independently of algorithm implementation
## Proposed Task Breakdown
### Phase 1: Schema Extensions
| Task ID | Description | Effort |
|---------|-------------|--------|
| DDS-001 | Extend `DeltaSigPredicateV2` with `BaselineReference` field | Small |
| DDS-002 | Add `ReconstructionAlgorithm` to predicate schema | Small |
| DDS-003 | Define `SegmentMap` model for stream verification | Medium |
| DDS-004 | Link predicate to `.buildinfo` via `ProofRef` | Small |
| DDS-005 | Define `DeltaManifest` type and signing flow | Medium |
### Phase 2: Service Implementation
| Task ID | Description | Effort |
|---------|-------------|--------|
| DDS-006 | Implement `IDeltaApplicationService` interface | Medium |
| DDS-007 | zchunk backend for delta application | Large |
| DDS-008 | Optional: Courgette/Zucchini backend for PE/ELF | Large |
| DDS-009 | Optional: MSDelta backend for Windows | Medium |
### Phase 3: Verification & Testing
| Task ID | Description | Effort |
|---------|-------------|--------|
| DDS-010 | Reconstruction test harness | Medium |
| DDS-011 | Byte-for-byte equality verification tests | Small |
| DDS-012 | Manifest signature verification tests | Small |
### Phase 4: Documentation
| Task ID | Description | Effort |
|---------|-------------|--------|
| DDS-013 | JSON schema for delta-manifest | Small |
| DDS-014 | Documentation updates for delta delivery | Medium |
| DDS-015 | CLI command updates (if applicable) | Medium |
## Decisions & Risks
### Key Decisions Needed
- **D1:** Which reconstruction algorithms to support initially? (zchunk recommended for cross-platform)
- **D2:** Is manifest signing required, or is predicate signing sufficient?
- **D3:** Should delta delivery be a separate predicate type or extension of v2?
- **D4:** Air-gap story: pre-bundle deltas or rely on CAS?
### Risks
- **R1:** zchunk library may require native bindings (no pure .NET implementation)
- **R2:** Courgette/Zucchini are C++ and require interop
- **R3:** Scope creep: this is orthogonal to CVE detection and could become a separate product area
- **R4:** Testing requires vendor binary pairs (base + patched) which may be hard to acquire
### Architectural Notes
Per the advisory:
> Prefer **zchunk-like chunk maps** + **trained zstd dictionaries** across package families to maximize reuse.
> For large PE/ELF apps, support **Zucchini/Courgette** paths for maximal shrink.
> Keep **MSDelta/LZX-delta** as a Windows-native backend for server components and agents.
> Treat **base availability as policy**: don't even queue a delta unless the precise base digest is present.
## Next Steps
1. **Product decision:** Prioritize delta delivery relative to other roadmap items
2. **Architecture review:** Validate proposed schema extensions with Attestor guild
3. **Prototype:** Spike zchunk integration to validate effort estimates
4. **Air-gap analysis:** Determine how deltas fit into offline deployment model
## References
- [Chromium Courgette/Zucchini](https://www.chromium.org/developers/design-documents/software-updates-courgette/)
- [Microsoft MSDelta](https://learn.microsoft.com/en-us/windows/win32/devnotes/msdelta)
- [deltarpm](https://www.novell.com/documentation/opensuse103/opensuse103_reference/data/sec_rpm_delta.html)
- [zchunk](https://github.com/zchunk/zchunk)
- [Apple Software Update Process](https://support.apple.com/guide/deployment/software-update-process-dep02c211f3e/web)
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-01-20 | Planning document created from product advisory gap analysis | Planning |
| 2026-01-20 | Kickoff: started decision capture for D1-D4 to move planning toward execution. | Planning |
## Sprint Status
**STATUS: PLANNING ONLY** - This document captures scope and analysis. No implementation is committed. Convert to active sprint when prioritized.

View File

@@ -1,488 +0,0 @@
# Sprint 037 Unified Trust Score Facade (B+C+D Approach)
## Topic & Scope
Implement a **facade layer** over existing EWS and Determinization systems to provide:
- **B: Unified API** - Single interface combining EWS scores + Determinization entropy
- **C: Versioned weight manifests** - Extract EWS weights to `etc/weights/*.json` files
- **D: Unknowns fraction (U)** - Expose Determinization entropy as unified metric
**Key principle:** Preserve existing guardrails, conflict detection, anchor verification, and decay mechanisms. No formula changes - only unification and better exposure.
**Working directory:** `src/Signals/`
**Secondary directories:** `src/Policy/`, `src/Cli/`, `src/Platform/`
**Expected evidence:** Unit tests, integration tests, CLI updates, API endpoints, updated documentation
---
## Dependencies & Concurrency
- **Upstream (existing, no changes to core logic):**
- EWS: `src/Signals/StellaOps.Signals/EvidenceWeightedScore/` - 6-dimension scoring with guardrails
- Determinization: `src/Policy/__Libraries/StellaOps.Policy.Determinization/` - entropy, decay, fingerprints
- CLI: `src/Cli/StellaOps.Cli/Commands/ScoreGateCommandGroup.cs` - existing `stella gate score`
- **Concurrency:** Safe to run in parallel with other sprints; no breaking changes
---
## Documentation Prerequisites
- [Policy architecture](../modules/policy/architecture.md) §3.1 Determinization Configuration
- [EWS migration](../modules/policy/design/confidence-to-ews-migration.md) - existing scoring
- [Score Proofs API](../api/scanner-score-proofs-api.md) - determinism patterns
---
## Delivery Tracker
### TSF-001 - Extract EWS Weights to Manifest Files
Status: TODO
Dependency: none
Owners: Signals Guild
Task description:
Extract existing EWS weight configuration from `EvidenceWeightPolicy` into versioned JSON manifest files. EWS continues to work exactly as before, but weights are now loaded from files.
**Implementation:**
- Create `etc/weights/` directory structure
- Create `WeightManifest` record matching existing `EvidenceWeights` structure
- Create `IWeightManifestLoader` interface + `FileBasedWeightManifestLoader`
- Update `EvidenceWeightPolicy` to load weights from manifest files (with fallback to defaults)
- Add SHA-256 content hash to manifests for audit trail
- Migrate existing default weights: `etc/weights/v2026-01-22.weights.json`
**Key constraint:** No change to scoring formula or behavior - just externalize configuration.
Completion criteria:
- [ ] `etc/weights/v2026-01-22.weights.json` with current EWS defaults
- [ ] `WeightManifest.cs` record with version, effectiveFrom, weights, hash
- [ ] `FileBasedWeightManifestLoader.cs` loading from `etc/weights/`
- [ ] `EvidenceWeightPolicy` updated to use loader
- [ ] Unit tests verifying identical scoring before/after extraction
- [ ] Existing determinism tests still pass
---
### TSF-002 - Unified Score Facade Service
Status: TODO
Dependency: TSF-001
Owners: Signals Guild
Task description:
Create `IUnifiedScoreService` facade that combines EWS computation with Determinization entropy in a single call. Returns unified result with score, U metric, breakdown, and evidence.
**Implementation:**
- Create `IUnifiedScoreService` interface in `src/Signals/StellaOps.Signals/UnifiedScore/`:
```csharp
Task<UnifiedScoreResult> ComputeAsync(UnifiedScoreRequest request, CancellationToken ct);
```
- Create `UnifiedScoreService` that internally:
1. Calls `IEvidenceWeightedScoreCalculator.Calculate()` for EWS score
2. Calls `IUncertaintyScoreCalculator.CalculateEntropy()` for entropy (U)
3. Calls `IConflictDetector.Detect()` for conflict information
4. Combines into `UnifiedScoreResult`
- `UnifiedScoreResult` includes:
- `Score` (0-100 from EWS)
- `Bucket` (ActNow/ScheduleNext/Investigate/Watchlist)
- `UnknownsFraction` (U from Determinization entropy)
- `UnknownsBand` (Complete/Adequate/Sparse/Insufficient)
- `Breakdown` (EWS dimension contributions)
- `Guardrails` (which caps/floors applied)
- `Conflicts` (from ConflictDetector)
- `WeightManifestRef` (version + hash)
- `EwsDigest` + `DeterminizationFingerprint` (for replay)
- Register in DI container
Completion criteria:
- [ ] `IUnifiedScoreService` interface defined
- [ ] `UnifiedScoreService` implementation composing EWS + Determinization
- [ ] `UnifiedScoreRequest` / `UnifiedScoreResult` DTOs
- [ ] DI registration in `ServiceCollectionExtensions`
- [ ] Unit tests for facade composition
- [ ] Verify identical EWS scores pass through unchanged
---
### TSF-003 - Unknowns Band Mapping
Status: TODO
Dependency: TSF-002
Owners: Signals Guild / Policy Guild
Task description:
Map Determinization entropy (0.0-1.0) to user-friendly unknowns bands with actionable thresholds.
**Implementation:**
- Create `UnknownsBandMapper` in `src/Signals/StellaOps.Signals/UnifiedScore/`:
```csharp
UnknownsBand MapEntropyToBand(double entropy);
string GetBandDescription(UnknownsBand band);
string GetBandAction(UnknownsBand band);
```
- Band definitions (matching existing Determinization thresholds):
| U Range | Band | Description | Action |
|---------|------|-------------|--------|
| 0.0-0.2 | Complete | Full signal coverage | Automated decisions |
| 0.2-0.4 | Adequate | Sufficient signals | Automated decisions |
| 0.4-0.6 | Sparse | Signal gaps exist | Manual review recommended |
| 0.6-1.0 | Insufficient | Critical gaps | Block pending more signals |
- Integrate with existing `ManualReviewEntropyThreshold` (0.60) and `RefreshEntropyThreshold` (0.40) from Determinization config
Completion criteria:
- [ ] `UnknownsBandMapper.cs` with configurable thresholds
- [ ] `UnknownsBand` enum (Complete, Adequate, Sparse, Insufficient)
- [ ] Configuration via `appsettings.json` aligned with Determinization
- [ ] Unit tests for threshold boundaries
- [ ] Integration with `UnifiedScoreResult`
---
### TSF-004 - Delta-If-Present Calculations
Status: TODO
Dependency: TSF-002
Owners: Signals Guild
Task description:
When signals are missing, calculate and include "delta if present" showing potential score impact. Uses existing Determinization `SignalGap` information.
**Implementation:**
- Extend `UnifiedScoreResult` with `DeltaIfPresent` list:
```csharp
IReadOnlyList<SignalDelta> DeltaIfPresent { get; }
```
- `SignalDelta` record:
```csharp
record SignalDelta(string Signal, double MinImpact, double MaxImpact, string Description);
```
- For each missing signal (from Determinization gaps):
- Calculate EWS contribution if signal were 0.0 vs 1.0
- Include weight from manifest
- Add descriptive text (e.g., "If reachability confirmed, score could change by -15 to +8")
- Use existing `SignalGap` from Determinization for missing signal list
Completion criteria:
- [ ] `SignalDelta` record defined
- [ ] Delta calculation logic in `UnifiedScoreService`
- [ ] Integration with `UnifiedScoreResult.DeltaIfPresent`
- [ ] Unit tests for delta calculation accuracy
- [ ] Test with various missing signal combinations
---
### TSF-005 - Platform API Endpoints (Score Evaluate)
Status: TODO
Dependency: TSF-002, TSF-003, TSF-004
Owners: Platform Guild
Task description:
Expose unified score via Platform service REST API endpoints.
**Implementation:**
- Add endpoints to `src/Platform/StellaOps.Platform.WebService/Endpoints/`:
- `POST /api/v1/score/evaluate` - Compute unified score (primary scoring endpoint)
- `GET /api/v1/score/weights` - List available weight manifests
- `GET /api/v1/score/weights/{version}` - Get specific manifest
- Request contract for `/score/evaluate`:
```json
{
"sbom_ref": "oci://registry/app@sha256:…",
"cvss_vector": "CVSS:3.1/…",
"vex_refs": ["oci://…/vex1"],
"rekor_receipts": ["BASE64-RECEIPT"],
"runtime_witnesses": [{"type":"process","data":"…"}],
"options": {"decay_lambda": 0.015, "weight_set_id": "v2026-01-22"}
}
```
- Response includes:
- `score_id` - unique identifier for replay lookup
- `score_value` - 0-100 score
- `unknowns` - list of unknown package refs
- `proof_ref` - OCI reference to score proof bundle
- Full `UnifiedScoreResult` structure (breakdown, U, band, deltas)
- Support `?include_delta=true` query param for delta calculations
- Add OpenAPI documentation
- Tenant-scoped via Authority
Completion criteria:
- [ ] `POST /api/v1/score/evaluate` endpoint implemented
- [ ] `/api/v1/score/weights` endpoints implemented
- [ ] Request/response contracts match advisory spec
- [ ] OpenAPI spec generated
- [ ] Authentication/authorization configured
- [ ] Integration tests for each endpoint
---
### TSF-006 - CLI `stella gate score` Enhancement
Status: TODO
Dependency: TSF-005
Owners: CLI Guild
Task description:
Enhance existing `stella gate score evaluate` command to show unified metrics (U, bands, deltas).
**Implementation:**
- Update `ScoreGateCommandGroup.cs`:
- Add `--show-unknowns` flag to include U metric and band
- Add `--show-deltas` flag to include delta-if-present
- Add `--weights-version` option to pin specific manifest
- Update table output to show U and band when requested
- Update JSON output to include full unified result
- Add new subcommand `stella gate score weights`:
- `list` - Show available weight manifest versions
- `show <version>` - Display manifest details
- `diff <v1> <v2>` - Compare two manifests
Completion criteria:
- [ ] `--show-unknowns` flag showing U and band
- [ ] `--show-deltas` flag showing delta-if-present
- [ ] `--weights-version` option for pinning
- [ ] `stella gate score weights list|show|diff` commands
- [ ] Updated help text and examples
- [ ] CLI tests for new options
---
### TSF-007 - CLI `stella score` Top-Level Command
Status: TODO
Dependency: TSF-005, TSF-011
Owners: CLI Guild
Task description:
Add new top-level `stella score` command group for direct scoring operations (complementing existing `stella gate score` which is gate-focused).
**Implementation:**
- Create `ScoreCommandGroup.cs` in `src/Cli/StellaOps.Cli/Commands/`:
- `stella score compute` - Compute unified score from signals (similar inputs to gate evaluate)
- `stella score explain <finding-id>` - Detailed explanation with breakdown
- `stella score history <finding-id>` - Score history over time
- `stella score compare <finding-id-1> <finding-id-2>` - Compare two findings
- `stella score replay <score-id>` - Fetch and display replay proof (depends on TSF-011)
- `stella score verify <score-id>` - Verify score by replaying computation locally
- Support `--format json|table|markdown` output
- Support `--offline` mode using bundled weights
- Replay/verify commands output:
- Canonical input hashes
- Step-by-step algebra decisions
- Rekor inclusion proof (if anchored)
- Verification status (pass/fail with diff if mismatch)
Completion criteria:
- [ ] `stella score compute` command
- [ ] `stella score explain` command
- [ ] `stella score history` command (if backend supports)
- [ ] `stella score compare` command
- [ ] `stella score replay` command
- [ ] `stella score verify` command
- [ ] Multiple output formats
- [ ] Offline mode support
- [ ] CLI tests
---
### TSF-008 - Console UI Score Display Enhancement
Status: TODO
Dependency: TSF-005
Owners: FE Guild
Task description:
Update Console UI components that display scores to include unknowns fraction and band.
**Implementation:**
- Update finding detail views to show:
- Score with bucket (existing)
- Unknowns fraction (U) with visual indicator
- Unknowns band with color coding
- Delta-if-present for missing signals
- Weight manifest version used
- Add tooltip/popover explaining U and what it means
- Update score trend charts to optionally show U over time
- Update findings list to show U indicator for high-uncertainty findings
Completion criteria:
- [ ] Finding detail view shows U metric and band
- [ ] Color-coded band indicator (green/yellow/orange/red)
- [ ] Delta-if-present display for missing signals
- [ ] Tooltip explaining unknowns
- [ ] Findings list shows high-U indicator
- [ ] Score trend chart option for U
---
### TSF-009 - Determinism & Replay Tests
Status: TODO
Dependency: TSF-002
Owners: QA / Signals Guild
Task description:
Verify that the unified facade maintains determinism guarantees from underlying EWS and Determinization systems.
**Implementation:**
- Create `UnifiedScoreDeterminismTests.cs`:
- Same inputs → same unified result (100+ iterations)
- EWS score unchanged through facade
- Determinization entropy unchanged through facade
- Weight manifest hash stable
- Delta calculations deterministic
- Create golden test fixtures:
- Known inputs with expected unified outputs
- Fixtures for various U bands
- Fixtures for delta calculations
- Verify existing EWS determinism tests still pass
Completion criteria:
- [ ] `UnifiedScoreDeterminismTests.cs` with iteration tests
- [ ] Golden fixtures in `__Tests/Fixtures/UnifiedScore/`
- [ ] EWS pass-through verification
- [ ] Determinization pass-through verification
- [ ] CI gate for determinism regression
- [ ] Existing EWS/Determinization tests unaffected
---
### TSF-010 - Documentation Updates
Status: TODO
Dependency: TSF-001 through TSF-009
Owners: Documentation
Task description:
Update documentation to reflect the unified scoring facade.
**Implementation:**
- Update `docs/technical/scoring-algebra.md` to describe facade approach (not rewrite)
- Update `docs/modules/policy/architecture.md` §3.1 to reference weight manifests
- Create `docs/modules/signals/unified-score.md` explaining:
- What the facade provides
- How U metric works
- How to interpret bands
- CLI command reference
- Update `docs/modules/cli/guides/commands/reference.md` with new commands
- Add troubleshooting section for common U-related issues
Completion criteria:
- [ ] `docs/technical/scoring-algebra.md` updated for facade approach
- [ ] Policy architecture doc updated
- [ ] `docs/modules/signals/unified-score.md` guide created
- [ ] CLI reference updated
- [ ] Troubleshooting guide for U issues
---
### TSF-011 - Score Replay & Verification Endpoint
Status: TODO
Dependency: TSF-005
Owners: Platform Guild / Signals Guild
Task description:
Add explicit replay endpoint that returns a signed replay log, enabling external auditors to independently verify any score computation.
**Implementation:**
- Add endpoint to `src/Platform/StellaOps.Platform.WebService/Endpoints/`:
- `GET /api/v1/score/{id}/replay` - Fetch signed replay proof for a score
- Response contract:
```json
{
"signed_replay_log_dsse": "BASE64",
"rekor_inclusion": {"logIndex": 12345, "rootHash": "…"},
"canonical_inputs": [
{"name": "sbom.json", "sha256": "…"},
{"name": "vex.json", "sha256": "…"},
{"name": "kev.snapshot", "sha256": "…"}
],
"transforms": [
{"name": "canonicalize_spdx", "version": "1.1"},
{"name": "normalize_cvss_v4", "version": "1.0"},
{"name": "age_decay", "params": {"lambda": 0.02}}
],
"algebra_steps": [
{"signal": "cvss_v4_base_norm", "w": 0.30, "value": 0.78, "term": 0.234},
{"signal": "kev_flag", "w": 0.25, "value": 1, "term": 0.25}
],
"final_score": 85,
"computed_at": "2026-01-22T12:00:00Z"
}
```
- DSSE attestation format:
- Payload type: `application/vnd.stella.score+json`
- Sign with Authority key
- Store replay log as OCI referrer ("StellaBundle" pattern):
- Reference: `oci://registry/score-proofs@sha256:`
- Attach to original artifact via OCI referrers API
- Create `IReplayLogBuilder` service:
- Collects canonical input hashes during scoring
- Records transform versions and parameters
- Captures step-by-step algebra decisions
- Generates DSSE-signed attestation
- Create `IReplayVerifier` service:
- Takes replay log + original inputs
- Re-executes scoring with pinned versions
- Returns verification result (pass/fail with diff)
Completion criteria:
- [ ] `GET /api/v1/score/{id}/replay` endpoint implemented
- [ ] `IReplayLogBuilder` service capturing full computation trace
- [ ] `IReplayVerifier` service for independent verification
- [ ] DSSE signing with `application/vnd.stella.score+json` payload type
- [ ] OCI referrer storage for replay proofs
- [ ] Rekor anchoring integration (optional, configurable)
- [ ] OpenAPI spec for replay endpoint
- [ ] Integration tests for replay/verify flow
- [ ] Golden corpus test: score → replay → verify round-trip
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2026-01-22 | Sprint created from product advisory | Planning |
| 2026-01-22 | Revised to B+C+D facade approach after deep analysis of existing systems | Planning |
| 2026-01-22 | Added TSF-011 (replay endpoint) per second advisory; renamed `/score/unified` to `/score/evaluate`; added `stella score replay|verify` CLI commands | Planning |
---
## Decisions & Risks
### Decisions Made
1. **Facade over rewrite** - Preserve existing EWS guardrails, conflict detection, anchor verification
2. **Weight manifest format** - JSON with SHA-256 hash, stored in `etc/weights/`
3. **U band thresholds** - Aligned with existing Determinization config (0.40/0.60 thresholds)
4. **No formula changes** - EWS scoring logic unchanged; only exposed differently
5. **Endpoint naming** - Use `/score/evaluate` (per second advisory) instead of `/score/unified` for industry alignment
6. **Explicit replay endpoint** - Add `/score/{id}/replay` returning signed DSSE attestation for auditor verification
7. **DSSE payload type** - Use `application/vnd.stella.score+json` for score attestations
8. **OCI referrer pattern** - Store replay proofs as OCI referrers ("StellaBundle") attached to scored artifacts
### Risks
1. **Performance** - Facade adds overhead calling two services
- Mitigation: Both services are fast (<100μs); combined still sub-millisecond
2. **Backward compatibility** - Existing CLI/API consumers expect current format
- Mitigation: New fields are additive; existing fields unchanged
3. **Configuration drift** - Weight manifest vs Determinization config could diverge
- Mitigation: Single source of truth via weight manifest; Determinization references it
### What We're NOT Doing
- Replacing EWS formula
- Replacing Determinization entropy calculation
- Changing guardrail logic
- Changing conflict detection
- Breaking existing CLI commands
- Breaking existing API contracts
---
## Next Checkpoints
- [ ] TSF-001 complete - Weights externalized
- [ ] TSF-002, TSF-003, TSF-004 complete - Facade functional
- [ ] TSF-005 complete - Score evaluate API endpoint
- [ ] TSF-011 complete - Replay/verification endpoint + DSSE attestation
- [ ] TSF-006, TSF-007 complete - CLI updated (including replay/verify commands)
- [ ] TSF-008 complete - UI updated
- [ ] TSF-009 complete - Determinism verified
- [ ] TSF-010 complete - Documentation finalized

View File

@@ -1,115 +0,0 @@
# Sprint 038 - eBPF Probe Type Enhancement
## Topic & Scope
- Add probe-type categorization to runtime observation models for eBPF sources
- Enable finer-grained filtering and policy evaluation based on probe type
- Document offline replay verification algorithm
- Working directory: `src/RuntimeInstrumentation/StellaOps.RuntimeInstrumentation.Tetragon/`
- Secondary directories: `src/Cli/StellaOps.Cli/Commands/`, `docs/modules/zastava/`
- Expected evidence: unit tests, updated CLI, architecture docs
## Dependencies & Concurrency
- Upstream: None (backwards-compatible enhancement)
- Can run in parallel with other sprints
- Uses existing `runtimeWitness@v1` predicate type (no new type needed)
## Documentation Prerequisites
- Archive manifest: `docs-archived/product/advisories/2026-01-22-ebpf-witness-contract/ARCHIVE_MANIFEST.md`
- Tetragon bridge: `src/RuntimeInstrumentation/StellaOps.RuntimeInstrumentation.Tetragon/TetragonWitnessBridge.cs`
- Zastava architecture: `docs/modules/zastava/architecture.md`
## Delivery Tracker
### EBPF-001 - Add ProbeType field to RuntimeObservation
Status: TODO
Dependency: none
Owners: Developer
Task description:
Extend the `RuntimeObservation` record in `TetragonWitnessBridge.cs` to include an optional `ProbeType` field. This allows distinguishing between kprobe, uprobe, tracepoint, and USDT observations while remaining backwards compatible.
Add enum and field:
```csharp
public enum EbpfProbeType
{
Kprobe,
Kretprobe,
Uprobe,
Uretprobe,
Tracepoint,
Usdt,
Fentry,
Fexit
}
// Add to RuntimeObservation record:
public EbpfProbeType? ProbeType { get; init; }
public string? FunctionName { get; init; }
public long? FunctionAddress { get; init; }
```
Completion criteria:
- [ ] `EbpfProbeType` enum added
- [ ] `ProbeType`, `FunctionName`, `FunctionAddress` fields added to `RuntimeObservation`
- [ ] Existing code continues to work (fields are optional)
- [ ] Unit tests for new fields
### EBPF-002 - Update Tetragon event parser to populate ProbeType
Status: TODO
Dependency: EBPF-001
Owners: Developer
Task description:
Update the Tetragon event parsing logic to extract and populate the `ProbeType` field from Tetragon events. Tetragon events include probe type information that should be mapped to the new enum.
Completion criteria:
- [ ] Tetragon event parser extracts probe type
- [ ] Mapping from Tetragon probe types to `EbpfProbeType` enum
- [ ] Integration tests with sample Tetragon events
### EBPF-003 - Add --probe-type filter to witness list CLI
Status: TODO
Dependency: EBPF-001
Owners: Developer
Task description:
Extend the `witness list` CLI command to support filtering by probe type. Add a `--probe-type` option that accepts: kprobe, uprobe, tracepoint, usdt.
Location: `src/Cli/StellaOps.Cli/Commands/WitnessCommandGroup.cs`
Completion criteria:
- [ ] `--probe-type` option added to `witness list` command
- [ ] Filtering logic implemented in handler
- [ ] Help text updated
- [ ] CLI test coverage added
### EBPF-004 - Document offline replay verification algorithm
Status: TODO
Dependency: none
Owners: Documentation author
Task description:
Add a section to `docs/modules/zastava/architecture.md` documenting the deterministic replay verification algorithm for runtime witnesses. This should specify:
- Input canonicalization steps (RFC 8785 JCS)
- Observation ordering rules for deterministic hashing
- Signature verification sequence
- Offline bundle structure requirements for witness verification
Completion criteria:
- [ ] New section "Offline Witness Verification" added to Zastava architecture
- [ ] Canonicalization steps documented
- [ ] Observation ordering rules specified
- [ ] Offline bundle requirements defined
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-01-22 | Sprint created from eBPF witness advisory. Simplified approach: extend existing model rather than new predicate type. | Planning |
## Decisions & Risks
- **Decision**: Extend existing `RuntimeObservation` with optional `ProbeType` field rather than creating new `ebpfWitness@v1` predicate type. Rationale: simpler, backwards compatible, `SourceType=Tetragon` already identifies eBPF source.
- **Risk**: None significant - all new fields are optional, existing witnesses remain valid.
## Next Checkpoints
- EBPF-001 and EBPF-004 can start immediately (no dependencies)
- EBPF-002 and EBPF-003 depend on EBPF-001

View File

@@ -1,886 +0,0 @@
# Sprint 039 Runtime→Static Linkage Verification
## Topic & Scope
Implement the **proof layer** that connects runtime eBPF observations to static analysis claims, enabling users to:
- Declare expected call-paths via a **function_map predicate** derived from SBOM
- Verify that runtime observations match declared expectations
- Complete the offline trust chain with **checkpoint signature verification**
- Query historical observations for compliance reporting
This sprint delivers the missing "contract" and "proof" layers identified in the eBPF witness advisory gap analysis.
**Working directory:** `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/`
**Secondary directories:**
- `src/Attestor/` (checkpoint signature fix)
- `src/Cli/StellaOps.Cli/Commands/` (CLI commands)
- `src/RuntimeInstrumentation/` (observation persistence)
- `src/Platform/` (API endpoints)
- `src/Web/` (UI components)
**Expected evidence:** Unit tests, integration tests, CLI commands, API endpoints, UI components, updated documentation
---
## User Stories
### US-1: Security Engineer declares expected call-paths
> "As a security engineer, I want to declare which functions my service is expected to call, so I can detect unexpected runtime behavior."
### US-2: DevOps verifies runtime matches expectations
> "As a DevOps engineer, I want to verify that runtime observations match our declared function map, so I can prove our services behave as expected."
### US-3: Auditor verifies offline
> "As an auditor, I want to verify runtime-to-static linkage in an air-gapped environment with full cryptographic proof."
### US-4: SOC analyst queries observation history
> "As a SOC analyst, I want to query historical observations for a specific function to investigate anomalies."
---
## Dependencies & Concurrency
- **Upstream (required before starting):**
- Sprint 038 EBPF-001: `ProbeType` field in `RuntimeObservation` (for richer verification)
- Existing `PathWitness` model in `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/Witnesses/`
- Existing `ClaimIdGenerator` for claim linking
- Existing `TetragonWitnessBridge` for observation buffering
- **Upstream (no changes needed):**
- `HttpRekorClient` - will be patched for checkpoint signatures
- `BundleManifest` v2.0.0 - function_map will be added as artifact type
- **Concurrency:**
- Safe to run in parallel with Sprint 037 (trust score)
- Depends on Sprint 038 EBPF-001 completing first
---
## Documentation Prerequisites
- [Witness contract v1](../contracts/witness-v1.md) - Node hash and path hash recipes
- [Zastava architecture](../modules/zastava/architecture.md) - Runtime signal flow
- [Attestor offline verification](../modules/attestor/guides/offline-verification.md) - Bundle verification
- Sprint 038 EBPF-004 output (offline replay algorithm docs)
---
## Delivery Tracker
### RLV-001 - Define function_map Predicate Schema
Status: TODO
Dependency: none
Owners: Scanner Guild / Attestor Guild
Task description:
Define the `function_map` predicate schema that declares expected call-paths for a service. This is the "contract" that runtime observations will be verified against.
**Schema design:**
```json
{
"_type": "https://stella.ops/predicates/function-map/v1",
"subject": {
"purl": "pkg:oci/myservice@sha256:abc123...",
"digest": { "sha256": "abc123..." }
},
"predicate": {
"schemaVersion": "1.0.0",
"service": "myservice",
"buildId": "abc123def456...",
"generatedFrom": {
"sbomRef": "sha256:...",
"staticAnalysisRef": "sha256:..."
},
"expectedPaths": [
{
"pathId": "path-001",
"description": "TLS handshake via OpenSSL",
"entrypoint": {
"symbol": "myservice::handle_request",
"nodeHash": "sha256:..."
},
"expectedCalls": [
{
"symbol": "SSL_connect",
"purl": "pkg:deb/debian/openssl@3.0.11",
"nodeHash": "sha256:...",
"probeTypes": ["uprobe", "uretprobe"],
"optional": false
},
{
"symbol": "SSL_read",
"purl": "pkg:deb/debian/openssl@3.0.11",
"nodeHash": "sha256:...",
"probeTypes": ["uprobe"],
"optional": false
}
],
"pathHash": "sha256:..."
}
],
"coverage": {
"minObservationRate": 0.95,
"windowSeconds": 1800
},
"generatedAt": "2026-01-22T12:00:00Z"
}
}
```
**Key design decisions:**
- Uses existing `nodeHash` recipe from witness-v1 contract for consistency
- `expectedCalls` array defines the "hot functions" from the advisory
- `probeTypes` specifies which probe types are acceptable for each function
- `coverage.minObservationRate` maps to advisory's "≥ 95% of calls witnessed"
- `optional` flag allows for conditional paths (feature flags, error handlers)
**Implementation:**
- Create `FunctionMapPredicate.cs` record in `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/FunctionMap/`
- Create `ExpectedPath.cs` and `ExpectedCall.cs` supporting records
- Add JSON schema to `docs/schemas/function-map-v1.schema.json`
- Register predicate type with Attestor predicate router
Completion criteria:
- [ ] `FunctionMapPredicate.cs` with full schema
- [ ] JSON schema in `docs/schemas/`
- [ ] Predicate type registered: `https://stella.ops/predicates/function-map/v1`
- [ ] Unit tests for serialization/deserialization
- [ ] Schema validation tests
---
### RLV-002 - Implement FunctionMapGenerator
Status: TODO
Dependency: RLV-001
Owners: Scanner Guild
Task description:
Implement a generator that produces a `function_map` predicate from SBOM + static analysis results. This enables users to declare expected paths without manually authoring JSON.
**Implementation:**
- Create `IFunctionMapGenerator` interface:
```csharp
public interface IFunctionMapGenerator
{
Task<FunctionMapPredicate> GenerateAsync(
FunctionMapGenerationRequest request,
CancellationToken ct);
}
```
- Create `FunctionMapGenerationRequest`:
```csharp
public record FunctionMapGenerationRequest
{
public required string SbomPath { get; init; }
public required string ServiceName { get; init; }
public string? StaticAnalysisPath { get; init; }
public IReadOnlyList<string>? HotFunctionPatterns { get; init; }
public double MinObservationRate { get; init; } = 0.95;
public int WindowSeconds { get; init; } = 1800;
}
```
- Create `FunctionMapGenerator` implementation:
1. Parse SBOM to extract components with PURLs
2. If static analysis provided, extract call paths
3. If hot function patterns provided, filter to matching symbols
4. Generate node hashes using existing `NodeHashRecipe`
5. Compute path hashes using existing `PathHashRecipe`
6. Return populated `FunctionMapPredicate`
**Location:** `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/FunctionMap/`
Completion criteria:
- [ ] `IFunctionMapGenerator` interface
- [ ] `FunctionMapGenerator` implementation
- [ ] Integration with existing SBOM parser
- [ ] Support for hot function pattern matching (glob/regex)
- [ ] Unit tests with sample SBOM
- [ ] Integration test: SBOM → function_map → valid predicate
---
### RLV-003 - Implement IClaimVerifier
Status: TODO
Dependency: RLV-001, Sprint 038 EBPF-001
Owners: Scanner Guild
Task description:
Implement the claim verification logic that proves runtime observations match a declared function_map. This is the core "proof" step.
**Implementation:**
- Create `IClaimVerifier` interface in `src/Scanner/__Libraries/StellaOps.Scanner.Reachability/Verification/`:
```csharp
public interface IClaimVerifier
{
Task<ClaimVerificationResult> VerifyAsync(
FunctionMapPredicate functionMap,
IReadOnlyList<RuntimeObservation> observations,
ClaimVerificationOptions options,
CancellationToken ct);
}
```
- Create `ClaimVerificationResult`:
```csharp
public record ClaimVerificationResult
{
public required bool Verified { get; init; }
public required double ObservationRate { get; init; }
public required IReadOnlyList<PathVerificationResult> Paths { get; init; }
public required IReadOnlyList<string> UnexpectedSymbols { get; init; }
public required IReadOnlyList<string> MissingExpectedSymbols { get; init; }
public required ClaimVerificationEvidence Evidence { get; init; }
}
public record PathVerificationResult
{
public required string PathId { get; init; }
public required bool Observed { get; init; }
public required int ObservationCount { get; init; }
public required IReadOnlyList<string> MatchedNodeHashes { get; init; }
public required IReadOnlyList<string> MissingNodeHashes { get; init; }
}
public record ClaimVerificationEvidence
{
public required string FunctionMapDigest { get; init; }
public required string ObservationsDigest { get; init; }
public required DateTimeOffset VerifiedAt { get; init; }
public required string VerifierVersion { get; init; }
}
```
- Create `ClaimVerifier` implementation:
1. Group observations by node hash
2. For each expected path in function_map:
- Check if all required node hashes were observed
- Check if probe types match expectations
- Calculate observation rate
3. Detect unexpected symbols (observed but not in function_map)
4. Calculate overall observation rate
5. Compare against `coverage.minObservationRate`
6. Build evidence record for audit trail
**Verification algorithm:**
```
For each path in functionMap.expectedPaths:
matched = 0
for each call in path.expectedCalls:
if observations.any(o => o.nodeHash == call.nodeHash && call.probeTypes.contains(o.probeType)):
matched++
path.observationRate = matched / path.expectedCalls.count
overallRate = observedPaths / totalPaths
verified = overallRate >= functionMap.coverage.minObservationRate
```
Completion criteria:
- [ ] `IClaimVerifier` interface defined
- [ ] `ClaimVerifier` implementation with verification algorithm
- [ ] `ClaimVerificationResult` with detailed breakdown
- [ ] Evidence record for audit trail
- [ ] Detection of unexpected symbols
- [ ] Unit tests for various scenarios (full match, partial, no match)
- [ ] Integration test with real observations
---
### RLV-004 - Fix Checkpoint Signature Verification
Status: TODO
Dependency: none
Owners: Attestor Guild
Task description:
Complete the Rekor checkpoint signature verification that currently returns `false` unconditionally. This is required for full offline trust chain.
**Current state (HttpRekorClient.cs:282-289):**
```csharp
_logger.LogDebug(
"Checkpoint signature verification is unavailable for UUID {Uuid}; treating checkpoint as unverified",
rekorUuid);
// ...
return RekorInclusionVerificationResult.Success(
logIndex.Value,
computedRootHex,
proof.Checkpoint.RootHash,
checkpointSignatureValid: false); // Always false
```
**Implementation:**
- Update `HttpRekorClient.VerifyInclusionAsync()` to:
1. Extract checkpoint note from response
2. Parse note format: body + signature lines
3. Verify signature using `CheckpointSignatureVerifier` (already exists)
4. Return actual verification result
- Add `RekorPublicKey` configuration option for pinned verification
- Support both online (fetch from Rekor) and offline (pinned key) modes
**Location:** `src/Attestor/__Libraries/StellaOps.Attestor.Infrastructure/Rekor/HttpRekorClient.cs`
**Testing:**
- Verify against real Rekor checkpoint
- Verify with pinned public key (offline mode)
- Verify rejection of tampered checkpoint
Completion criteria:
- [ ] Checkpoint signature verification implemented
- [ ] `checkpointSignatureValid` returns actual result
- [ ] Support for pinned public key (air-gap mode)
- [ ] Unit tests with test vectors
- [ ] Integration test against Rekor staging
---
### RLV-005 - Implement Runtime Observation Store
Status: TODO
Dependency: Sprint 038 EBPF-001
Owners: Signals Guild
Task description:
Implement persistent storage for runtime observations to support historical queries and compliance reporting.
**Implementation:**
- Create `IRuntimeObservationStore` interface (if not exists) in `src/RuntimeInstrumentation/`:
```csharp
public interface IRuntimeObservationStore
{
Task StoreAsync(RuntimeObservation observation, CancellationToken ct);
Task StoreBatchAsync(IReadOnlyList<RuntimeObservation> observations, CancellationToken ct);
Task<IReadOnlyList<RuntimeObservation>> QueryBySymbolAsync(
string nodeHash,
DateTimeOffset from,
DateTimeOffset to,
CancellationToken ct);
Task<IReadOnlyList<RuntimeObservation>> QueryByContainerAsync(
string containerId,
DateTimeOffset from,
DateTimeOffset to,
CancellationToken ct);
Task<ObservationSummary> GetSummaryAsync(
string nodeHash,
DateTimeOffset from,
DateTimeOffset to,
CancellationToken ct);
Task PruneOlderThanAsync(TimeSpan retention, CancellationToken ct);
}
```
- Create `PostgresRuntimeObservationStore` implementation:
- Table: `runtime_observations` with indexes on `node_hash`, `container_id`, `observed_at`
- Batch insert with conflict handling (dedup by observation_id)
- Efficient time-range queries using BRIN index on `observed_at`
- Configurable retention policy (default: 7 days)
- Create migration: `src/RuntimeInstrumentation/.../Migrations/001_runtime_observations.sql`
- Wire into `TetragonWitnessBridge` to persist observations as they arrive
**Schema:**
```sql
CREATE TABLE runtime_observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
observation_id TEXT NOT NULL UNIQUE,
node_hash TEXT NOT NULL,
symbol_name TEXT,
container_id TEXT NOT NULL,
pod_name TEXT,
namespace TEXT,
probe_type TEXT,
function_address BIGINT,
stack_sample_hash TEXT,
observation_count INTEGER DEFAULT 1,
duration_us BIGINT,
observed_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_observations_node_hash ON runtime_observations (node_hash);
CREATE INDEX idx_observations_container ON runtime_observations (container_id);
CREATE INDEX idx_observations_time USING BRIN ON runtime_observations (observed_at);
```
Completion criteria:
- [ ] `IRuntimeObservationStore` interface
- [ ] `PostgresRuntimeObservationStore` implementation
- [ ] Database migration
- [ ] Integration with `TetragonWitnessBridge`
- [ ] Configurable retention policy
- [ ] Unit tests for store operations
- [ ] Integration tests with real Postgres
---
### RLV-006 - CLI: `stella function-map generate`
Status: TODO
Dependency: RLV-002
Owners: CLI Guild
Task description:
Add CLI command to generate a function_map predicate from SBOM.
**Implementation:**
- Create `FunctionMapCommandGroup.cs` in `src/Cli/StellaOps.Cli/Commands/`
- Add command: `stella function-map generate`
**Command spec:**
```
stella function-map generate [options]
Options:
--sbom <path> Path to SBOM file (CycloneDX/SPDX) [required]
--service <name> Service name for the function map [required]
--static-analysis <path> Path to static analysis results (optional)
--hot-functions <pattern> Glob pattern for hot functions (can repeat)
Example: --hot-functions "SSL_*" --hot-functions "crypto_*"
--min-rate <0.0-1.0> Minimum observation rate (default: 0.95)
--window <seconds> Observation window in seconds (default: 1800)
--output <path> Output path (default: stdout)
--format <json|yaml> Output format (default: json)
--sign Sign the predicate with configured key
--attest Create DSSE envelope and push to Rekor
Examples:
# Generate from SBOM with default hot functions
stella function-map generate --sbom sbom.json --service myservice
# Generate with specific hot functions
stella function-map generate --sbom sbom.json --service myservice \
--hot-functions "SSL_*" --hot-functions "EVP_*" --hot-functions "connect"
# Generate, sign, and attest
stella function-map generate --sbom sbom.json --service myservice \
--sign --attest --output function-map.json
```
Completion criteria:
- [ ] `stella function-map generate` command implemented
- [ ] All options working
- [ ] DSSE signing integration (--sign)
- [ ] Rekor attestation integration (--attest)
- [ ] JSON and YAML output formats
- [ ] Help text and examples
- [ ] CLI tests
---
### RLV-007 - CLI: `stella function-map verify`
Status: TODO
Dependency: RLV-003, RLV-005
Owners: CLI Guild
Task description:
Add CLI command to verify runtime observations against a function_map.
**Command spec:**
```
stella function-map verify [options]
Options:
--function-map <path|ref> Path or OCI reference to function_map predicate [required]
--container <id> Container ID to verify (optional, default: all)
--from <timestamp> Start of observation window (default: 30 minutes ago)
--to <timestamp> End of observation window (default: now)
--output <path> Output verification report (default: stdout)
--format <json|table|md> Output format (default: table)
--strict Fail on any unexpected symbols
--sign Sign the verification report
--offline Offline mode (use bundled observations)
--observations <path> Path to observations file (for offline mode)
Output:
Verified: true/false
Observation Rate: 97.2% (target: 95.0%)
Path Coverage:
┌──────────────┬──────────┬───────────┬─────────────┐
│ Path ID │ Status │ Rate │ Missing │
├──────────────┼──────────┼───────────┼─────────────┤
│ path-001 │ ✓ │ 100% │ - │
│ path-002 │ ✓ │ 95.5% │ - │
│ path-003 │ ✗ │ 80.0% │ SSL_write │
└──────────────┴──────────┴───────────┴─────────────┘
Unexpected Symbols: none
Evidence:
Function Map Digest: sha256:abc123...
Observations Digest: sha256:def456...
Verified At: 2026-01-22T12:00:00Z
Examples:
# Verify against stored observations
stella function-map verify --function-map function-map.json
# Verify specific container
stella function-map verify --function-map function-map.json \
--container abc123 --from "2026-01-22T11:30:00Z"
# Offline verification with bundled observations
stella function-map verify --function-map function-map.json \
--offline --observations observations.ndjson
# Sign verification report for audit
stella function-map verify --function-map function-map.json \
--sign --output verification-report.json
```
Completion criteria:
- [ ] `stella function-map verify` command implemented
- [ ] Query observations from store
- [ ] Offline mode with file input
- [ ] Table, JSON, and Markdown output formats
- [ ] Signed verification report option
- [ ] CLI tests
---
### RLV-008 - CLI: `stella observations query`
Status: TODO
Dependency: RLV-005
Owners: CLI Guild
Task description:
Add CLI command to query historical runtime observations.
**Command spec:**
```
stella observations query [options]
Options:
--symbol <name> Filter by symbol name (glob pattern)
--node-hash <hash> Filter by exact node hash
--container <id> Filter by container ID
--pod <name> Filter by pod name
--namespace <ns> Filter by Kubernetes namespace
--probe-type <type> Filter by probe type (kprobe|uprobe|tracepoint|usdt)
--from <timestamp> Start time (default: 1 hour ago)
--to <timestamp> End time (default: now)
--limit <n> Maximum results (default: 100)
--format <json|table|csv> Output format (default: table)
--summary Show summary statistics instead of individual observations
Examples:
# Query all SSL_connect observations in last hour
stella observations query --symbol "SSL_connect"
# Query by container
stella observations query --container abc123 --from "2026-01-22T11:00:00Z"
# Get summary statistics
stella observations query --symbol "SSL_*" --summary
# Export to CSV for analysis
stella observations query --namespace production --format csv > observations.csv
```
Completion criteria:
- [ ] `stella observations query` command implemented
- [ ] All filter options working
- [ ] Summary statistics mode
- [ ] CSV export for external analysis
- [ ] CLI tests
---
### RLV-009 - Platform API: Function Map Endpoints
Status: TODO
Dependency: RLV-002, RLV-003
Owners: Platform Guild
Task description:
Expose function_map operations via Platform service REST API.
**Endpoints:**
```
POST /api/v1/function-maps Create/store function map
GET /api/v1/function-maps List function maps
GET /api/v1/function-maps/{id} Get function map by ID
DELETE /api/v1/function-maps/{id} Delete function map
POST /api/v1/function-maps/{id}/verify Verify observations against map
GET /api/v1/function-maps/{id}/coverage Get current coverage statistics
```
**Request/Response contracts:**
`POST /api/v1/function-maps`:
```json
{
"sbomRef": "oci://registry/app@sha256:...",
"serviceName": "myservice",
"hotFunctions": ["SSL_*", "EVP_*"],
"options": {
"minObservationRate": 0.95,
"windowSeconds": 1800
}
}
```
`POST /api/v1/function-maps/{id}/verify`:
```json
{
"containerId": "abc123",
"from": "2026-01-22T11:00:00Z",
"to": "2026-01-22T12:00:00Z",
"strict": false
}
```
Response:
```json
{
"verified": true,
"observationRate": 0.972,
"targetRate": 0.95,
"paths": [...],
"unexpectedSymbols": [],
"evidence": {
"functionMapDigest": "sha256:...",
"observationsDigest": "sha256:...",
"verifiedAt": "2026-01-22T12:00:00Z"
}
}
```
Completion criteria:
- [ ] All endpoints implemented
- [ ] OpenAPI spec generated
- [ ] Tenant-scoped authorization
- [ ] Integration tests
- [ ] Rate limiting configured
---
### RLV-010 - UI: Function Map Management
Status: TODO
Dependency: RLV-009
Owners: FE Guild
Task description:
Add UI components for managing function maps and viewing verification results.
**Components:**
1. **Function Map List View** (`/settings/function-maps`)
- Table showing all function maps for tenant
- Columns: Service, Created, Last Verified, Coverage Status
- Actions: View, Verify Now, Delete
2. **Function Map Detail View** (`/settings/function-maps/{id}`)
- Service info and generation metadata
- Expected paths table with symbols
- Coverage thresholds configuration
- Recent verification history
3. **Function Map Generator Wizard** (`/settings/function-maps/new`)
- Step 1: Select SBOM source (file upload or OCI reference)
- Step 2: Configure hot function patterns (with suggestions)
- Step 3: Set coverage thresholds
- Step 4: Review and create
4. **Verification Results Panel** (embedded in service detail)
- Current verification status (verified/not verified)
- Observation rate gauge with threshold indicator
- Path coverage breakdown (expandable)
- Unexpected symbols warning (if any)
- Link to full verification report
5. **Observation Timeline** (`/services/{id}/observations`)
- Time-series chart of observation counts
- Filter by symbol/probe type
- Drill-down to individual observations
Completion criteria:
- [ ] Function map list view
- [ ] Function map detail view
- [ ] Generator wizard
- [ ] Verification results panel
- [ ] Observation timeline chart
- [ ] Responsive design
- [ ] Loading states and error handling
- [ ] E2E tests
---
### RLV-011 - Bundle Integration: function_map Artifact Type
Status: TODO
Dependency: RLV-001
Owners: AirGap Guild
Task description:
Add `function_map` as a supported artifact type in StellaBundle for offline verification.
**Implementation:**
- Update `BundleArtifactType` enum to include `FunctionMap`
- Update `BundleBuilder` to package function_map predicates
- Update `BundleValidator` to validate function_map artifacts
- Update `BundleVerifyCommand` to verify function_map signatures
**Bundle structure addition:**
```
bundle/
├── manifest.json
├── function-maps/
│ └── myservice-function-map.json
├── observations/
│ └── observations-2026-01-22.ndjson
└── verification/
└── verification-report.dsse.json
```
Completion criteria:
- [ ] `FunctionMap` artifact type added
- [ ] Bundle export includes function maps
- [ ] Bundle verify validates function map signatures
- [ ] Offline verification includes function map checking
- [ ] Documentation updated
---
### RLV-012 - Documentation: Runtime Linkage Verification Guide
Status: TODO
Dependency: RLV-001 through RLV-011
Owners: Documentation
Task description:
Create comprehensive documentation for the runtime→static linkage verification feature.
**Documents to create/update:**
1. **New: `docs/modules/scanner/guides/runtime-linkage.md`**
- What is runtime→static linkage verification?
- When to use function maps
- Step-by-step guide: generate → deploy probes → verify
- Troubleshooting common issues
2. **New: `docs/contracts/function-map-v1.md`**
- Predicate schema specification
- Node hash and path hash recipes (reference witness-v1)
- Coverage calculation algorithm
- Verification algorithm
3. **Update: `docs/modules/cli/guides/commands/reference.md`**
- Add `stella function-map` command group
- Add `stella observations` command group
4. **Update: `docs/modules/airgap/guides/offline-bundle-format.md`**
- Add function_map artifact type documentation
5. **New: `docs/runbooks/runtime-linkage-ops.md`**
- Operational runbook for production deployment
- Probe selection guidance
- Performance tuning
- Alert configuration
Completion criteria:
- [ ] Runtime linkage guide created
- [ ] function_map contract documented
- [ ] CLI reference updated
- [ ] Bundle format docs updated
- [ ] Operational runbook created
---
### RLV-013 - Acceptance Tests: 90-Day Pilot Criteria
Status: TODO
Dependency: All above tasks
Owners: QA Guild
Task description:
Implement acceptance tests matching the advisory's success criteria:
**Advisory acceptance criteria:**
1. **Coverage:** ≥ 95% of calls to the 6 hot funcs are witnessed over a steady-state 30-min window
2. **Integrity:** 100% DSSE sig verify + valid Rekor inclusion + valid TST
3. **Replayability:** Offline verifier reproduces the same mapping on 3 separate air-gapped runs
4. **Perf:** < 2% CPU overhead, < 50 MB RSS for collector under target load
5. **Privacy:** No raw args; only hashes and minimal context
**Test implementation:**
1. **Coverage test:**
- Generate function_map with 6 hot functions
- Run load generator for 30 minutes
- Verify observation rate 95%
2. **Integrity test:**
- Generate function_map with signing
- Create DSSE envelope
- Post to Rekor
- Add RFC-3161 timestamp
- Verify all signatures and proofs
3. **Replayability test:**
- Export StellaBundle with function_map + observations
- Run offline verification 3 times in isolated environments
- Assert identical results
4. **Performance test (if feasible in CI):**
- Measure CPU overhead with/without probes
- Measure collector memory usage
- Assert within thresholds
5. **Privacy test:**
- Inspect all observation payloads
- Assert no raw arguments present
- Assert only hashes and minimal context
Completion criteria:
- [ ] Coverage acceptance test
- [ ] Integrity acceptance test
- [ ] Replayability acceptance test (3 runs)
- [ ] Performance benchmark (manual or CI)
- [ ] Privacy audit test
- [ ] All tests passing in CI
---
## Execution Log
| Date (UTC) | Update | Owner |
|------------|--------|-------|
| 2026-01-22 | Sprint created from eBPF witness advisory gap analysis | Planning |
---
## Decisions & Risks
### Decisions Made
1. **function_map as separate predicate** - Not extending witness-v1, cleaner separation of concerns
2. **Reuse existing hash recipes** - NodeHash and PathHash from witness-v1 contract for consistency
3. **Postgres for observation storage** - Leverages existing infrastructure, supports time-range queries
4. **CLI-first verification** - Offline verification via CLI before UI for air-gap users
### Risks
1. **Observation volume** - High-traffic services may generate many observations
- Mitigation: Configurable sampling, aggregation, retention policy
2. **Clock skew** - Distributed observations may have timestamp drift
- Mitigation: Use server-side timestamps, configurable tolerance
3. **Symbol resolution accuracy** - Different runtimes have different symbol formats
- Mitigation: Use node hashes (PURL + normalized symbol) for matching
4. **Performance impact of persistence** - Writing every observation could be costly
- Mitigation: Batch writes, async persistence, sampling option
### Open Questions
1. Should function_map support version ranges for expected components, or exact versions only?
2. Should we support "learning mode" that auto-generates function_map from observations?
3. How to handle function maps for services with feature flags (conditional paths)?
---
## Next Checkpoints
- [ ] RLV-001 complete - Schema defined
- [ ] RLV-002, RLV-003 complete - Core verification logic works
- [ ] RLV-004 complete - Checkpoint signatures verified (trust chain complete)
- [ ] RLV-005 complete - Observations persisted
- [ ] RLV-006, RLV-007, RLV-008 complete - CLI fully functional
- [ ] RLV-009, RLV-010 complete - API and UI ready
- [ ] RLV-011 complete - Bundle integration for offline
- [ ] RLV-012 complete - Documentation finalized
- [ ] RLV-013 complete - Acceptance criteria met

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).

View File

@@ -0,0 +1,232 @@
# Runtime Linkage Verification - Operational Runbook
> **Audience:** Platform operators, SREs, security engineers
> **Related:** [Runtime Linkage Guide](../modules/scanner/guides/runtime-linkage.md), [Function Map V1 Contract](../contracts/function-map-v1.md)
## Overview
This runbook covers production deployment and operation of the runtime linkage verification system. The system uses eBPF probes to observe function calls and verifies them against declared function maps.
---
## Prerequisites
- Linux kernel 5.8+ (for eBPF CO-RE support)
- `CAP_BPF` and `CAP_PERFMON` capabilities for the runtime agent
- BTF (BPF Type Format) enabled in kernel config
- Stella runtime agent deployed as a DaemonSet or sidecar
---
## Deployment
### Runtime Agent Configuration
The Stella runtime agent (`stella-runtime-agent`) attaches eBPF probes based on function map predicates. Configuration via environment or YAML:
```yaml
runtime_agent:
observation_store:
type: "memory" # or "postgres", "valkey"
retention_hours: 72
max_batch_size: 1000
probes:
max_concurrent: 256
attach_timeout_ms: 5000
default_types: ["uprobe", "kprobe"]
export:
format: "ndjson"
flush_interval_ms: 5000
output_path: "/var/stella/observations/"
```
### Probe Selection Guidance
| Category | Probe Type | Use Case |
|----------|-----------|----------|
| Crypto functions | `uprobe` | OpenSSL/BoringSSL/libsodium calls |
| Network I/O | `kprobe` | connect/sendto/recvfrom syscalls |
| Auth flows | `uprobe` | PAM/LDAP/OAuth library calls |
| File access | `kprobe` | open/read/write on sensitive paths |
| TLS handshake | `uprobe` | SSL_do_handshake, TLS negotiation |
**Prioritization:**
1. Start with crypto and auth paths (highest security relevance)
2. Add network I/O for service mesh verification
3. Expand to file access for compliance requirements
### Resource Overhead
Expected overhead per probe:
- CPU: ~0.1-0.5% per active uprobe (per-call overhead ~100ns)
- Memory: ~2KB per attached probe + observation buffer
- Disk: ~100 bytes per observation record (NDJSON)
**Recommended limits:**
- Max 256 concurrent probes per node
- Observation buffer: 64MB
- Flush interval: 5 seconds
- Retention: 72 hours (configurable)
---
## Operations
### Generating Function Maps
Run generation as part of CI/CD pipeline after SBOM generation:
```bash
# In CI after SBOM generation
stella function-map generate \
--sbom ${BUILD_DIR}/sbom.cdx.json \
--service ${SERVICE_NAME} \
--hot-functions "crypto/*" --hot-functions "net/*" --hot-functions "auth/*" \
--min-rate 0.95 \
--window 1800 \
--build-id ${CI_BUILD_ID} \
--output ${BUILD_DIR}/function-map.json
```
Store the function map alongside the container image (OCI referrer or artifact registry).
### Continuous Verification
Set up periodic verification (cron or controller loop):
```bash
# Every 30 minutes, verify the last hour of observations
stella function-map verify \
--function-map /etc/stella/function-map.json \
--from "$(date -d '1 hour ago' -Iseconds)" \
--to "$(date -Iseconds)" \
--format json --output /var/stella/verification/latest.json
```
### Monitoring
Key metrics to alert on:
| Metric | Threshold | Action |
|--------|-----------|--------|
| `observation_rate` | < 0.80 | Warning: coverage dropping |
| `observation_rate` | < 0.50 | Critical: significant coverage loss |
| `unexpected_symbols_count` | > 0 | Investigate: undeclared functions executing |
| `probe_attach_failures` | > 5% | Warning: probe attachment issues |
| `observation_buffer_full` | true | Critical: observations being dropped |
### Alert Configuration
```yaml
alerts:
- name: "function-map-coverage-low"
condition: observation_rate < 0.80
severity: warning
description: "Function map coverage below 80% for {service}"
runbook: "Check probe attachment, verify no binary update without map regeneration"
- name: "function-map-unexpected-calls"
condition: unexpected_symbols_count > 0
severity: info
description: "Unexpected function calls detected in {service}"
runbook: "Review unexpected symbols, regenerate function map if benign"
- name: "function-map-probe-failures"
condition: probe_attach_failure_rate > 0.05
severity: warning
description: "Probe attachment failure rate above 5%"
runbook: "Check kernel version, verify BTF availability, check CAP_BPF"
```
---
## Performance Tuning
### High-Traffic Services
For services with >10K calls/second on probed functions:
1. **Sampling:** Configure observation sampling rate:
```yaml
probes:
sampling_rate: 0.01 # 1% of calls
```
2. **Aggregation:** Use count-based observations instead of per-call:
```yaml
export:
aggregation_window_ms: 1000 # Aggregate per second
```
3. **Selective probing:** Use `--hot-functions` to limit to critical paths only
### Large Function Maps
For maps with >100 expected paths:
1. Tag paths by priority: `crypto` > `auth` > `network` > `general`
2. Mark low-priority paths as `optional: true`
3. Set per-tag minimum rates if needed
### Storage Optimization
For long-term observation storage:
1. Enable retention pruning: `pruneOlderThanAsync(72h)`
2. Compress archived observations (gzip NDJSON)
3. Use dedicated Postgres partitions by date for query performance
---
## Incident Response
### Coverage Dropped After Deployment
1. Check if binary was updated without regenerating the function map
2. Verify probes are still attached: `stella observations query --summary`
3. Check for symbol changes (ASLR, different build)
4. Regenerate function map from new SBOM and redeploy
### Unexpected Symbols Detected
1. Identify the unexpected functions from the verification report
2. Determine if they are:
- **Benign:** Dynamic dispatch, plugins, lazy-loaded libraries → add to map
- **Suspicious:** Unexpected crypto usage, network calls → escalate to security team
3. If benign, regenerate function map with broader patterns
4. If suspicious, correlate with vulnerability findings and open incident
### Probe Attachment Failures
1. Check kernel version: `uname -r` (need 5.8+)
2. Verify BTF: `ls /sys/kernel/btf/vmlinux`
3. Check capabilities: `capsh --print | grep bpf`
4. Check binary paths: verify `binary_path` in function map matches deployed binary
5. Check for SELinux/AppArmor blocking BPF operations
---
## Air-Gap Considerations
For air-gapped environments:
1. **Bundle generation** (connected side):
```bash
stella function-map generate --sbom app.cdx.json --service my-service --output fm.json
# Package with observations
tar czf linkage-bundle.tgz fm.json observations/*.ndjson
```
2. **Transfer** via approved media to air-gapped environment
3. **Offline verification** (air-gapped side):
```bash
stella function-map verify --function-map fm.json --offline --observations obs.ndjson
```
4. **Result export** for compliance reporting:
```bash
stella function-map verify ... --format json --output report.json
# Sign the report
stella attest sign --input report.json --output report.dsse.json
```

Binary file not shown.

View File

@@ -0,0 +1,10 @@
{
"bundle_id": "evidence-bundle-m0",
"version": "1.0.0",
"tenant": "demo",
"scope": "vex",
"aoc": {
"guardrails": true,
"details": ["schema:frozen:1.0"]
}
}

View File

@@ -0,0 +1,3 @@
{
"skip_reason": "offline"
}

View File

@@ -0,0 +1,285 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://stellaops.org/schemas/function-map-v1.schema.json",
"title": "StellaOps Function Map v1",
"description": "Predicate schema for declaring expected call-paths for runtime→static linkage verification",
"type": "object",
"required": ["_type", "subject", "predicate"],
"properties": {
"_type": {
"type": "string",
"enum": [
"https://stella.ops/predicates/function-map/v1",
"stella.ops/functionMap@v1"
],
"description": "Predicate type URI"
},
"subject": {
"$ref": "#/definitions/subject",
"description": "Subject artifact that this function map applies to"
},
"predicate": {
"$ref": "#/definitions/predicatePayload",
"description": "The predicate payload containing the function map definition"
}
},
"additionalProperties": false,
"definitions": {
"subject": {
"type": "object",
"required": ["purl", "digest"],
"properties": {
"purl": {
"type": "string",
"description": "Package URL of the subject artifact",
"pattern": "^pkg:[a-z]+/.+"
},
"digest": {
"type": "object",
"description": "Digest(s) of the subject artifact",
"additionalProperties": { "type": "string" },
"minProperties": 1
},
"name": {
"type": ["string", "null"],
"description": "Optional artifact name"
}
},
"additionalProperties": false
},
"predicatePayload": {
"type": "object",
"required": ["schemaVersion", "service", "expectedPaths", "coverage", "generatedAt"],
"properties": {
"schemaVersion": {
"type": "string",
"const": "1.0.0",
"description": "Schema version of this predicate"
},
"service": {
"type": "string",
"description": "Service name that this function map applies to",
"minLength": 1
},
"buildId": {
"type": ["string", "null"],
"description": "Build ID or version of the service"
},
"generatedFrom": {
"$ref": "#/definitions/generatedFrom",
"description": "References to source materials used to generate this function map"
},
"expectedPaths": {
"type": "array",
"description": "Expected call-paths that should be observed at runtime",
"items": { "$ref": "#/definitions/expectedPath" },
"minItems": 1
},
"coverage": {
"$ref": "#/definitions/coverageThresholds",
"description": "Coverage thresholds for verification"
},
"generatedAt": {
"type": "string",
"format": "date-time",
"description": "When this function map was generated"
},
"generator": {
"$ref": "#/definitions/generatorInfo",
"description": "Optional generator tool information"
},
"metadata": {
"type": ["object", "null"],
"description": "Optional metadata for extensions",
"additionalProperties": true
}
},
"additionalProperties": false
},
"generatedFrom": {
"type": ["object", "null"],
"properties": {
"sbomRef": {
"type": ["string", "null"],
"description": "SHA256 digest of the SBOM used"
},
"staticAnalysisRef": {
"type": ["string", "null"],
"description": "SHA256 digest of the static analysis results used"
},
"binaryAnalysisRef": {
"type": ["string", "null"],
"description": "SHA256 digest of the binary analysis results used"
},
"hotFunctionPatterns": {
"type": ["array", "null"],
"description": "Hot function patterns used for filtering",
"items": { "type": "string" }
}
},
"additionalProperties": false
},
"expectedPath": {
"type": "object",
"required": ["pathId", "entrypoint", "expectedCalls", "pathHash"],
"properties": {
"pathId": {
"type": "string",
"description": "Unique identifier for this path within the function map",
"minLength": 1
},
"description": {
"type": ["string", "null"],
"description": "Human-readable description of this call path"
},
"entrypoint": {
"$ref": "#/definitions/pathEntrypoint",
"description": "Entrypoint function that initiates this call path"
},
"expectedCalls": {
"type": "array",
"description": "Expected function calls within this path",
"items": { "$ref": "#/definitions/expectedCall" },
"minItems": 1
},
"pathHash": {
"type": "string",
"description": "Hash of the canonical path representation",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"optional": {
"type": "boolean",
"default": false,
"description": "Whether this entire path is optional"
},
"strictOrdering": {
"type": "boolean",
"default": false,
"description": "Whether strict ordering of expected calls should be verified"
},
"tags": {
"type": ["array", "null"],
"description": "Optional tags for categorizing paths",
"items": { "type": "string" }
}
},
"additionalProperties": false
},
"pathEntrypoint": {
"type": "object",
"required": ["symbol", "nodeHash"],
"properties": {
"symbol": {
"type": "string",
"description": "Symbol name of the entrypoint function",
"minLength": 1
},
"nodeHash": {
"type": "string",
"description": "Node hash for this entrypoint (PURL + normalized symbol)",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"purl": {
"type": ["string", "null"],
"description": "Optional PURL of the component containing this entrypoint"
}
},
"additionalProperties": false
},
"expectedCall": {
"type": "object",
"required": ["symbol", "purl", "nodeHash", "probeTypes"],
"properties": {
"symbol": {
"type": "string",
"description": "Symbol name of the expected function call",
"minLength": 1
},
"purl": {
"type": "string",
"description": "Package URL (PURL) of the component containing this function",
"pattern": "^pkg:[a-z]+/.+"
},
"nodeHash": {
"type": "string",
"description": "Node hash for this function (PURL + normalized symbol)",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"probeTypes": {
"type": "array",
"description": "Acceptable probe types for observing this function",
"items": {
"type": "string",
"enum": ["kprobe", "kretprobe", "uprobe", "uretprobe", "tracepoint", "usdt"]
},
"minItems": 1
},
"optional": {
"type": "boolean",
"default": false,
"description": "Whether this function call is optional"
},
"description": {
"type": ["string", "null"],
"description": "Optional human-readable description"
},
"functionAddress": {
"type": ["integer", "null"],
"description": "Optional function address hint for performance optimization"
},
"binaryPath": {
"type": ["string", "null"],
"description": "Optional binary path where this function is located"
}
},
"additionalProperties": false
},
"coverageThresholds": {
"type": "object",
"properties": {
"minObservationRate": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.95,
"description": "Minimum observation rate required for verification to pass"
},
"windowSeconds": {
"type": "integer",
"minimum": 1,
"default": 1800,
"description": "Observation window in seconds"
},
"minObservationCount": {
"type": ["integer", "null"],
"minimum": 1,
"description": "Minimum number of observations required before verification can succeed"
},
"failOnUnexpected": {
"type": "boolean",
"default": false,
"description": "Whether to fail on unexpected symbols (not in the function map)"
}
},
"additionalProperties": false
},
"generatorInfo": {
"type": ["object", "null"],
"properties": {
"name": {
"type": ["string", "null"],
"description": "Name of the generator tool"
},
"version": {
"type": ["string", "null"],
"description": "Version of the generator tool"
},
"commit": {
"type": ["string", "null"],
"description": "Optional commit hash of the generator tool"
}
},
"additionalProperties": false
}
}
}

View File

@@ -0,0 +1,273 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://stella-ops.org/schemas/policy-pack-v2.schema.json",
"title": "Stella Ops PolicyPack v2",
"description": "Canonical policy pack format supporting bidirectional JSON/Rego interop with structured remediation hints.",
"type": "object",
"required": ["apiVersion", "kind", "metadata", "spec"],
"properties": {
"apiVersion": {
"type": "string",
"const": "policy.stellaops.io/v2",
"description": "Schema version identifier."
},
"kind": {
"type": "string",
"enum": ["PolicyPack", "PolicyOverride"],
"description": "Document kind."
},
"metadata": { "$ref": "#/$defs/PolicyPackMetadata" },
"spec": { "$ref": "#/$defs/PolicyPackSpec" }
},
"additionalProperties": false,
"$defs": {
"PolicyPackMetadata": {
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
"description": "Unique name (DNS-label format)."
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+",
"description": "Semantic version."
},
"description": {
"type": "string",
"maxLength": 500,
"description": "Human-readable description."
},
"digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$",
"description": "SHA-256 digest of canonical content."
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Creation timestamp (ISO 8601 UTC)."
},
"exportedFrom": { "$ref": "#/$defs/PolicyExportProvenance" },
"parent": {
"type": "string",
"description": "Parent policy pack name (for PolicyOverride)."
},
"environment": {
"type": "string",
"description": "Target environment (for PolicyOverride)."
}
},
"additionalProperties": false
},
"PolicyExportProvenance": {
"type": "object",
"required": ["engine", "engineVersion"],
"properties": {
"engine": {
"type": "string",
"description": "Exporting engine name."
},
"engineVersion": {
"type": "string",
"description": "Engine version."
},
"exportedAt": {
"type": "string",
"format": "date-time",
"description": "Export timestamp."
}
},
"additionalProperties": false
},
"PolicyPackSpec": {
"type": "object",
"required": ["settings"],
"properties": {
"settings": { "$ref": "#/$defs/PolicyPackSettings" },
"gates": {
"type": "array",
"items": { "$ref": "#/$defs/PolicyGateDefinition" },
"description": "Gate definitions with typed configurations."
},
"rules": {
"type": "array",
"items": { "$ref": "#/$defs/PolicyRuleDefinition" },
"description": "Rule definitions with match conditions."
}
},
"additionalProperties": false
},
"PolicyPackSettings": {
"type": "object",
"required": ["defaultAction"],
"properties": {
"defaultAction": {
"type": "string",
"enum": ["allow", "warn", "block"],
"description": "Default action when no rule matches."
},
"unknownsThreshold": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.6,
"description": "Threshold for unknowns budget."
},
"stopOnFirstFailure": {
"type": "boolean",
"default": true,
"description": "Stop evaluation on first failure."
},
"deterministicMode": {
"type": "boolean",
"default": true,
"description": "Enforce deterministic evaluation."
}
},
"additionalProperties": false
},
"PolicyGateDefinition": {
"type": "object",
"required": ["id", "type"],
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
"description": "Unique gate identifier."
},
"type": {
"type": "string",
"description": "Gate type (C# gate class name)."
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Whether this gate is active."
},
"config": {
"type": "object",
"description": "Gate-specific configuration.",
"additionalProperties": true
},
"environments": {
"type": "object",
"description": "Per-environment config overrides.",
"additionalProperties": {
"type": "object",
"additionalProperties": true
}
},
"remediation": { "$ref": "#/$defs/RemediationHint" }
},
"additionalProperties": false
},
"PolicyRuleDefinition": {
"type": "object",
"required": ["name", "action"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]{0,62}$",
"description": "Unique rule name."
},
"action": {
"type": "string",
"enum": ["allow", "warn", "block"],
"description": "Action when matched."
},
"priority": {
"type": "integer",
"minimum": 0,
"default": 0,
"description": "Evaluation priority (lower = first)."
},
"match": {
"type": "object",
"description": "Match conditions (dot-notation keys, typed values).",
"additionalProperties": true
},
"remediation": { "$ref": "#/$defs/RemediationHint" }
},
"additionalProperties": false
},
"RemediationHint": {
"type": "object",
"required": ["code", "title", "severity"],
"properties": {
"code": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9_]{1,30}$",
"description": "Machine-readable remediation code."
},
"title": {
"type": "string",
"maxLength": 200,
"description": "Human-readable title."
},
"description": {
"type": "string",
"maxLength": 1000,
"description": "Detailed explanation."
},
"actions": {
"type": "array",
"items": { "$ref": "#/$defs/RemediationAction" },
"description": "Ordered remediation actions."
},
"references": {
"type": "array",
"items": { "$ref": "#/$defs/RemediationReference" },
"description": "External references."
},
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low"],
"description": "Issue severity."
}
},
"additionalProperties": false
},
"RemediationAction": {
"type": "object",
"required": ["type", "description"],
"properties": {
"type": {
"type": "string",
"enum": ["upgrade", "patch", "vex", "sign", "anchor", "generate", "override", "investigate", "mitigate"],
"description": "Action type."
},
"description": {
"type": "string",
"maxLength": 500,
"description": "What this action does."
},
"command": {
"type": "string",
"maxLength": 500,
"description": "CLI command template with {placeholders}."
}
},
"additionalProperties": false
},
"RemediationReference": {
"type": "object",
"required": ["title", "url"],
"properties": {
"title": {
"type": "string",
"maxLength": 200,
"description": "Display title."
},
"url": {
"type": "string",
"format": "uri",
"description": "Reference URL."
}
},
"additionalProperties": false
}
}
}

View File

@@ -58,6 +58,16 @@
"type": "object",
"additionalProperties": true,
"description": "Additional metadata"
},
"sbomDigest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$",
"description": "SHA-256 digest of the associated SBOM document"
},
"largeBlobs": {
"type": "array",
"items": { "$ref": "#/$defs/largeBlobReference" },
"description": "References to large binary blobs stored out-of-band (by digest)"
}
},
"$defs": {
@@ -346,6 +356,31 @@
"description": "Total size of IR diffs stored in CAS"
}
}
},
"largeBlobReference": {
"type": "object",
"required": ["kind", "digest"],
"properties": {
"kind": {
"type": "string",
"enum": ["preBinary", "postBinary", "debugSymbols", "irDiff"],
"description": "Blob kind: preBinary, postBinary, debugSymbols, etc."
},
"digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$",
"description": "Content-addressable digest (e.g., sha256:abc123...)"
},
"mediaType": {
"type": "string",
"description": "Media type of the blob"
},
"sizeBytes": {
"type": "integer",
"minimum": 0,
"description": "Size in bytes (for transfer planning)"
}
}
}
}
}