feat(cli): Implement crypto plugin CLI architecture with regional compliance

Sprint: SPRINT_4100_0006_0001
Status: COMPLETED

Implemented plugin-based crypto command architecture for regional compliance
with build-time distribution selection (GOST/eIDAS/SM) and runtime validation.

## New Commands

- `stella crypto sign` - Sign artifacts with regional crypto providers
- `stella crypto verify` - Verify signatures with trust policy support
- `stella crypto profiles` - List available crypto providers & capabilities

## Build-Time Distribution Selection

```bash
# International (default - BouncyCastle)
dotnet build src/Cli/StellaOps.Cli/StellaOps.Cli.csproj

# Russia distribution (GOST R 34.10-2012)
dotnet build -p:StellaOpsEnableGOST=true

# EU distribution (eIDAS Regulation 910/2014)
dotnet build -p:StellaOpsEnableEIDAS=true

# China distribution (SM2/SM3/SM4)
dotnet build -p:StellaOpsEnableSM=true
```

## Key Features

- Build-time conditional compilation prevents export control violations
- Runtime crypto profile validation on CLI startup
- 8 predefined profiles (international, russia-prod/dev, eu-prod/dev, china-prod/dev)
- Comprehensive configuration with environment variable substitution
- Integration tests with distribution-specific assertions
- Full migration path from deprecated `cryptoru` CLI

## Files Added

- src/Cli/StellaOps.Cli/Commands/CryptoCommandGroup.cs
- src/Cli/StellaOps.Cli/Commands/CommandHandlers.Crypto.cs
- src/Cli/StellaOps.Cli/Services/CryptoProfileValidator.cs
- src/Cli/StellaOps.Cli/appsettings.crypto.yaml.example
- src/Cli/__Tests/StellaOps.Cli.Tests/CryptoCommandTests.cs
- docs/cli/crypto-commands.md
- docs/implplan/SPRINT_4100_0006_0001_COMPLETION_SUMMARY.md

## Files Modified

- src/Cli/StellaOps.Cli/StellaOps.Cli.csproj (conditional plugin refs)
- src/Cli/StellaOps.Cli/Program.cs (plugin registration + validation)
- src/Cli/StellaOps.Cli/Commands/CommandFactory.cs (command wiring)
- src/Scanner/__Libraries/StellaOps.Scanner.Core/Configuration/PoEConfiguration.cs (fix)

## Compliance

- GOST (Russia): GOST R 34.10-2012, FSB certified
- eIDAS (EU): Regulation (EU) No 910/2014, QES/AES/AdES
- SM (China): GM/T 0003-2012 (SM2), OSCCA certified

## Migration

`cryptoru` CLI deprecated → sunset date: 2025-07-01
- `cryptoru providers` → `stella crypto profiles`
- `cryptoru sign` → `stella crypto sign`

## Testing

 All crypto code compiles successfully
 Integration tests pass
 Build verification for all distributions (international/GOST/eIDAS/SM)

Next: SPRINT_4100_0006_0002 (eIDAS plugin implementation)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
master
2025-12-23 13:13:00 +02:00
parent c8a871dd30
commit ef933db0d8
97 changed files with 17455 additions and 52 deletions

View File

@@ -0,0 +1,103 @@
Heres a simple, practical way to make vulnerability “reachability” auditable and offlineverifiable in StellaOps without adding a lot of UI or runtime cost.
![diagram of call graph to subgraph proof flow](https://dummyimage.com/1200x400/ededed/333\&text=Call+graph+%E2%86%92+Resolved+subgraph+%E2%86%92+Proof+of+Exposure)
# What this is (plain English)
* **Callstack subgraph:** when we say a vuln is “reachable,” we really mean *some* functions in your code can eventually call the risky function. That tiny slice of the big call graph is the **subgraph**.
* **Proof of exposure (PoE):** a compact bundle (think: a few kilobytes) that cryptographically proves *which* functions and edges make the vuln reachable in a specific build.
* **Offlineverifiable:** auditors can check the proof later, in an airgapped setting, using only hashes and your reproducible build IDs.
# The minimal data model
* **BuildID:** deterministic identifier (e.g., ELF BuildID or sourceoftruth content hash).
* **Nodes:** function identifiers `(module, symbol, debugaddr, source:line?)`.
* **Edges:** caller → callee (with optional guard predicates like feature flags).
* **Entry set:** the function(s)/handlers reachable from runtime entrypoints (HTTP handlers, cron, CLI).
* **Sink set:** vulnerable API(s)/function(s) tied to a CVE.
* **Reachability proof:** `{BuildID, nodes[N], edges[E], entryRefs, sinkRefs, policyContext, toolVersions}` + DSSE signature.
# How it fits the StellaOps ledger
* Store each **resolved callstack** as a **subgraph object** keyed by `(BuildID, vulnID, package@version)`.
* Link it to:
* SBOM component node (CycloneDX/SPDX ref).
* VEX claim (affected/notaffected/underinvestigation).
* Scan recipe (so anyone can replay the result).
* Emit one **PoE artifact** per “(vuln, component) with reachability=true”.
# Why this helps
* **Binary precision + explainability:** even if you only have a container image, the PoE explains *why* its reachable.
* **Auditorfriendly:** tiny artifact, DSSEsigned, replayable with a known scanner build.
* **Noise control:** store reachability as firstclass evidence; triage focuses on subgraphs, not global graphs.
# Implementation guide (short and concrete)
**1) Extraction (per build)**
* Prefer sourcelevel graphs when available; otherwise:
* ELF/PE/MachO symbol harvest + debug info (DWARF/PDB) if present.
* Lightweight static calledge inference (import tables, PLT/GOT, relocation targets).
* Optional dynamic trace sampling (eBPF hooks) to confirm hot edges.
**2) Resolution pipeline**
* Normalize function IDs: `ModuleHash:Symbol@Addr[:File:Line]`.
* Compute **entry set** (framework adapters know HTTP/GRPC/CLI entrypoints).
* Compute **sink set** via rulepack mapping CVEs → {module:function(s)}.
* Run bounded graph search with **policy guards** (feature flags, platform, build tags).
* Persist the **subgraph** + metadata.
**3) PoE artifact (OCIattached attestation)**
* Canonical JSON (stable sort, normalized IDs).
* Include: BuildID, tool versions, policy digest, SBOM refs, VEX claim link, subgraph nodes/edges, minimal repro steps.
* Sign via DSSE; attach as OCI ref to the image digest.
**4) Offline verification (auditor)**
* Inputs: PoE, image digest, SBOM slice.
* Steps: verify DSSE → check BuildID ↔ image digest → confirm nodes/edges hashes → reevaluate policy (optional) → show minimal path(s) entry→sink.
# UI: keep it small
* **Evidence tab → “Proof of exposure”** pill on any reachable vuln row.
* Click opens a tiny **path viewer** (entry→…→sink) with:
* path count, shortest path, guarded edges (badges for feature flags).
* “Copy PoE JSON” and “Verify offline” instructions.
* No separate heavy UI needed; reuse the existing vulnerability details drawer.
# C# shape (sketch)
```csharp
record FunctionId(string ModuleHash, string Symbol, ulong Addr, string? File, int? Line);
record Edge(FunctionId Caller, FunctionId Callee, string[] Guards);
record Subgraph(string BuildId, string ComponentRef, string VulnId,
IReadOnlyList<FunctionId> Nodes, IReadOnlyList<Edge> Edges,
string[] EntryRefs, string[] SinkRefs,
string PolicyDigest, string ToolchainDigest);
interface IReachabilityResolver {
Subgraph Resolve(string buildId, string componentRef, string vulnId, ResolverOptions opts);
}
interface IProofEmitter {
byte[] EmitPoE(Subgraph g, PoeMeta meta); // canonical JSON bytes
}
```
# Policy hooks youll want from day one
* `fail_if_unknown_edges > N` in prod.
* `require_guard_evidence` for claims like “feature off”.
* `max_paths`/`max_depth` to keep proofs compact.
* `source-first-but-fallback-binary` selection.
# Rollout plan (2 sprints)
* **Sprint A (MVP):** static graph, percomponent sinks, shortest path only, PoE JSON + DSSE sign, attach to image, verifycli.
* **Sprint B (Hardening):** guard predicates, multiple paths with cap, eBPF confirmation toggle, UI path viewer, policy gates wired to release checks.

View File

@@ -0,0 +1,439 @@
# Implementation Summary: Competitor Scanner UI Gap Closure
> **Date:** 2025-12-23
> **Status:** Planning Complete, Implementation Ready
> **Related Advisory:** [23-Dec-2026 - Competitor Scanner UI Breakdown.md](./23-Dec-2026%20-%20Competitor%20Scanner%20UI%20Breakdown.md)
---
## Executive Summary
This document summarizes the comprehensive planning and design work completed to close competitive gaps identified in the "Competitor Scanner UI Breakdown" advisory. We've created **5 sprint plans**, **2 JSON schemas**, and **2 comprehensive documentation guides** that establish Stella Ops differentiators against Snyk Container, Anchore Enterprise, and Prisma Cloud.
---
## Gap Analysis Results
### Gaps Identified Against src/**/*.md
| Feature | Advisory Source | Stella Ops Status | Priority | Artifacts Created |
|---------|----------------|-------------------|----------|-------------------|
| **Signed Delta-Verdicts** | N/A (differentiator) | Foundation exists, integration missing | **HIGH** | Sprint, Schema, Docs |
| **Replayable Evidence Packs** | N/A (differentiator) | Primitives exist, bundle missing | **HIGH** | Sprint, Schema, Docs |
| **Reachability Proof Panels** | N/A (differentiator) | Backend exists, UI missing | **MEDIUM** | Sprint |
| **UI Vulnerability Annotation** | Anchore Enterprise | API exists, UI workflow missing | **MEDIUM** | Sprint |
| **Base Image Detection/Recs** | Snyk Container | Missing | **MEDIUM** | Sprint |
| **Admin Audit Trail UI** | Prisma Cloud | Partial (decisions only) | **LOW** | *Deferred* |
---
## Deliverables Created
### Sprint Plans (5 Total)
| Sprint ID | Title | Priority | Components | Status |
|-----------|-------|----------|------------|--------|
| **SPRINT_3000_0100_0001** | Signed Delta-Verdicts | HIGH | Policy Engine, Attestor, Evidence Locker | Planning |
| **SPRINT_3000_0100_0002** | Replayable Evidence Packs | HIGH | Evidence Locker, Policy Engine, Replay | Planning |
| **SPRINT_4000_0100_0001** | Reachability Proof Panels UI | MEDIUM | Web (Angular), Policy APIs | Planning |
| **SPRINT_4000_0100_0002** | UI-Driven Vulnerability Annotation | MEDIUM | Web, Findings Ledger, Excititor | Planning |
| **SPRINT_3000_0100_0003** | Base Image Detection & Recommendations | MEDIUM | Scanner, Policy Engine | Planning |
**Location:** `docs/implplan/SPRINT_3000_0100_*.md`, `docs/implplan/SPRINT_4000_0100_*.md`
### JSON Schemas (2 Total)
| Schema | URI | Purpose | Status |
|--------|-----|---------|--------|
| **Policy Verdict Predicate** | `https://stellaops.dev/predicates/policy-verdict@v1` | DSSE predicate for signed verdicts | Complete |
| **Evidence Pack Manifest** | `https://stellaops.dev/evidence-pack@v1` | Replayable bundle manifest | Complete |
**Location:** `docs/schemas/stellaops-policy-verdict.v1.schema.json`, `docs/schemas/stellaops-evidence-pack.v1.schema.json`
### Documentation (2 Comprehensive Guides)
| Document | Scope | Status |
|----------|-------|--------|
| **[Policy Verdict Attestations](../policy/verdict-attestations.md)** | API, CLI, implementation guide, troubleshooting | Complete |
| **[Evidence Pack Schema](../evidence-locker/evidence-pack-schema.md)** | Pack format, replay workflow, air-gap transfer | Complete |
---
## Competitive Differentiation Matrix
### Stella Ops vs Competitors
| Feature | Snyk | Anchore | Prisma | **Stella Ops** |
|---------|------|---------|--------|---------------|
| **Signed Verdicts** | ❌ No | ❌ No | ❌ No | **✅ DSSE-wrapped, Rekor-anchored** |
| **Evidence Packs** | ❌ Separate exports | ❌ Separate exports | ❌ Separate exports | **✅ Single signed bundle** |
| **Replayable Policy** | ❌ No | ❌ No | ❌ No | **✅ Deterministic re-evaluation** |
| **Proof Visualization** | Basic | Basic | Basic | **✅ Cryptographic verification UI** |
| **VEX Auto-Generation** | ❌ Manual | ⚠️ UI annotation | ❌ Manual | **✅ Smart-Diff auto-emit + approval** |
| **Base Image Detection** | ✅ Yes | ❌ No | ⚠️ Limited | **🚧 Planned (Sprint 3000_0100_0003)** |
| **Custom Base Recs** | ✅ Yes | ❌ No | ❌ No | **🚧 Policy-driven from internal registry** |
| **Admin Audit Trail** | ⚠️ Limited | ⚠️ Limited | ✅ Yes | **⚠️ Decisions only (future enhancement)** |
| **Offline Support** | ❌ Cloud-only | ⚠️ Limited | ❌ Cloud-only | **✅ Air-gap first** |
**Legend:**
- ✅ Full support
- ⚠️ Partial support
- Basic/limited
- ❌ Not supported
- 🚧 Planned/in progress
---
## Architecture Overview
### Signed Delta-Verdicts Flow
```
Policy Engine Evaluation
PolicyExplainTrace (per finding)
VerdictPredicateBuilder
VerdictAttestation Request → Attestor Service
DSSE Signing + Optional Rekor Anchoring
Evidence Locker Storage (PostgreSQL + Object Store)
API: GET /api/v1/verdicts/{verdictId}
```
**Key Differentiators:**
- **Cryptographic Binding:** Each verdict cryptographically signed, tamper-evident
- **Granular Attestations:** One attestation per finding (not batch)
- **Transparency Option:** Rekor anchoring for public auditability
- **Offline Verification:** Signature verification without network
### Replayable Evidence Packs Flow
```
Policy Run Completion
Pack Assembly Trigger
Collect: Policy + SBOMs + Advisories + VEX + Verdicts + Reachability
Generate Manifest (content index + determinism hash)
Sign Manifest (DSSE)
Compress to Tarball (.tar.gz)
Store in Object Store + Index in PostgreSQL
Download → Transfer → Replay on Any Environment
```
**Key Differentiators:**
- **Complete Context:** Single bundle contains all evaluation inputs
- **Deterministic Replay:** Bit-for-bit reproducible verdicts
- **Queryable Offline:** Inspect without external APIs
- **Air-Gap Transfer:** Sign once, verify anywhere
---
## Implementation Roadmap
### Phase 1: HIGH Priority (Weeks 1-4)
**SPRINT_3000_0100_0001 — Signed Delta-Verdicts**
*Week 1-2:*
- [ ] Implement `VerdictPredicateBuilder` in Policy Engine
- [ ] Wire Policy Engine → Attestor integration
- [ ] Implement `VerdictAttestationHandler` in Attestor
- [ ] Evidence Locker storage schema + API endpoints
*Week 3-4:*
- [ ] CLI commands (`stella verdict get/verify/list`)
- [ ] Integration tests (Policy Run → Verdict Attestation → Retrieval → Verification)
- [ ] Rekor anchoring integration (optional)
- [ ] Documentation finalization
**SPRINT_3000_0100_0002 — Replayable Evidence Packs**
*Week 1-2:*
- [ ] Implement `EvidencePackAssembler` in Evidence Locker
- [ ] Pack assembly workflow (collect artifacts, generate manifest, sign, compress)
- [ ] Pack storage (object store + PostgreSQL index)
- [ ] API endpoints (`POST /runs/{id}/evidence-pack`, `GET /packs/{id}`, etc.)
*Week 3-4:*
- [ ] Implement `ReplayService` in Policy Engine
- [ ] Replay workflow (extract → deserialize → re-evaluate → compare)
- [ ] CLI commands (`stella pack create/download/inspect/verify/replay`)
- [ ] Integration tests (determinism validation, air-gap transfer)
**Success Metrics:**
- ✅ Every policy run produces signed verdicts
- ✅ Evidence packs replay with 100% determinism
- ✅ CLI can verify signatures offline
- ✅ API endpoints documented and tested
### Phase 2: MEDIUM Priority (Weeks 5-8)
**SPRINT_4000_0100_0001 — Reachability Proof Panels UI**
*Dependencies:* SPRINT_3000_0100_0001 (verdict attestation API)
- [ ] Design UI mockups for proof panel
- [ ] Implement `VerdictProofPanelComponent` (Angular)
- [ ] Integrate with verdict API + signature verification
- [ ] Render evidence chain (advisory → SBOM → VEX → reachability → verdict)
- [ ] Storybook stories + E2E tests
**SPRINT_4000_0100_0002 — UI-Driven Vulnerability Annotation**
- [ ] Define vulnerability state machine in Findings Ledger
- [ ] Implement triage dashboard UI (Angular)
- [ ] VEX candidate review/approval workflow
- [ ] API: `PATCH /findings/{id}/state`, `POST /vex-candidates/{id}/approve`
- [ ] E2E tests for annotation workflow
**SPRINT_3000_0100_0003 — Base Image Detection & Recommendations**
- [ ] Create `StellaOps.Scanner.BaseImage` library
- [ ] Implement Dockerfile parser + OCI manifest analyzer
- [ ] Build approved image registry schema
- [ ] Recommendation engine logic
- [ ] API endpoints + CLI commands
**Success Metrics:**
- ✅ Proof panel visualizes verdict evidence chain
- ✅ Triage dashboard enables UI-driven VEX approval
- ✅ Base image recommendations from internal registry
### Phase 3: Enhancements (Weeks 9+)
- [ ] Admin audit trail UI (comprehensive activity logging)
- [ ] Pack retention policies + automated archiving
- [ ] Batch verdict signing optimization
- [ ] Evidence pack incremental updates
- [ ] Policy replay diff visualization
---
## Technical Specifications
### Verdict Attestation Predicate
**Type:** `https://stellaops.dev/predicates/policy-verdict@v1`
**Key Fields:**
- `verdict.status`: passed | warned | blocked | quieted | ignored
- `ruleChain`: Ordered policy rule execution trace
- `evidence`: Advisory, VEX, reachability, SBOM evidence with digests
- `reachability.status`: confirmed | likely | present | unreachable | unknown
- `metadata.determinismHash`: SHA256 of verdict computation
**Signing:** DSSE envelope, Ed25519/ECDSA/RSA-PSS
**Storage:** PostgreSQL (verdict_attestations table) + optional Rekor
### Evidence Pack Manifest
**Type:** `https://stellaops.dev/evidence-pack@v1`
**Structure:**
- `contents.policy`: Policy definition + run metadata
- `contents.sbom`: SPDX/CycloneDX SBOMs
- `contents.advisories`: Timestamped CVE snapshots
- `contents.vex`: OpenVEX statements
- `contents.verdicts`: DSSE-wrapped verdict attestations
- `contents.reachability`: Drift + slice analysis
**Determinism:** `determinismHash` = SHA256(sorted content digests)
**Signing:** Manifest signature covers all content digests
**Format:** Compressed tarball (`.tar.gz`)
---
## API Summary
### Verdict Attestation APIs
| Method | Endpoint | Purpose |
|--------|----------|---------|
| `GET` | `/api/v1/verdicts/{verdictId}` | Retrieve verdict attestation (DSSE envelope) |
| `GET` | `/api/v1/runs/{runId}/verdicts` | List verdicts for policy run (filters: status, severity) |
| `POST` | `/api/v1/verdicts/{verdictId}/verify` | Verify signature + optional Rekor inclusion proof |
### Evidence Pack APIs
| Method | Endpoint | Purpose |
|--------|----------|---------|
| `POST` | `/api/v1/runs/{runId}/evidence-pack` | Create evidence pack for policy run |
| `GET` | `/api/v1/evidence-packs/{packId}` | Download pack tarball |
| `GET` | `/api/v1/evidence-packs/{packId}/manifest` | Inspect manifest (no download) |
| `POST` | `/api/v1/evidence-packs/{packId}/replay` | Replay policy evaluation from pack |
| `POST` | `/api/v1/evidence-packs/{packId}/verify` | Verify pack signature + content integrity |
---
## CLI Summary
### Verdict Commands
```bash
stella verdict get <verdictId> # Retrieve verdict
stella verdict verify <verdict.json> --public-key <key> # Verify signature (offline)
stella verdict list --run <runId> --status blocked # List verdicts for run
stella verdict download <verdictId> --output <file> # Download DSSE envelope
```
### Evidence Pack Commands
```bash
stella pack create <runId> # Create pack
stella pack download <packId> --output <file> # Download pack tarball
stella pack inspect <pack.tar.gz> # Display manifest + stats
stella pack list <pack.tar.gz> # List all files in pack
stella pack export <pack.tar.gz> --artifact <path> # Extract single artifact
stella pack verify <pack.tar.gz> # Verify signature + integrity
stella pack replay <pack.tar.gz> # Replay policy evaluation
```
---
## Testing Strategy
### Unit Tests
- [ ] Verdict predicate builder (canonical JSON, determinism)
- [ ] Evidence pack assembler (manifest generation, determinism hash)
- [ ] Replay service (deserialization, comparison logic)
- [ ] Schema validation (JSON schema compliance)
### Integration Tests
- [ ] End-to-end: Policy Run → Verdict Attestation → Storage → Retrieval → Verification
- [ ] End-to-end: Pack Assembly → Storage → Download → Replay → Determinism Validation
- [ ] Cross-environment: Create pack on env A, replay on env B (air-gap scenario)
- [ ] Signature verification (offline, no network)
### Performance Tests
- [ ] Verdict attestation throughput (1M verdicts/hour target)
- [ ] Pack assembly time (< 2min for 10K findings)
- [ ] Replay time (< 60s for 10K verdicts)
- [ ] Storage scaling (100K packs, query performance)
---
## Security Considerations
### Cryptographic Guarantees
- **Verdict Attestations:** DSSE-signed, tamper-evident, optional Rekor transparency
- **Evidence Packs:** Manifest signature, per-file digests, determinism hash
- **Key Management:** KMS, CryptoPro (GOST), offline signing ceremonies
- **Verification:** Offline-capable, no network required
### Access Control
- **RBAC Scopes:**
- `policy:verdict:read` View verdict attestations
- `policy:pack:create` Create evidence packs
- `policy:pack:read` Download evidence packs
- `policy:replay` Replay policy evaluations
- **Tenant Isolation:** All artifacts scoped by `tenantId`, cross-tenant queries blocked
### Audit Trail
- All verdict retrievals logged with actor, timestamp, tenant
- Pack creation/download/replay logged for compliance
- Signature verification failures alerted
---
## Determinism Validation
### Verdict Determinism
**Inputs:**
- Policy definition (P-7 v4)
- SBOM (sbom:S-42)
- Advisory snapshot (at cursor timestamp)
- VEX snapshot (at cursor timestamp)
- Environment context
**Output:** Verdict with `determinismHash` = SHA256(sorted evidence digests)
**Validation:** Re-evaluate with identical inputs same `determinismHash`
### Pack Replay Determinism
**Inputs:** Evidence pack manifest (all artifacts bundled)
**Output:** Replay verdicts
**Validation:** Compare replay verdicts to original verdicts:
- Count match: 234 original, 234 replay
- Status match: All verdicts have identical status
- Hash match: All `determinismHash` values identical
---
## Next Steps
### Immediate Actions (This Week)
1. **Review & Approve:** Technical designs for SPRINT_3000_0100_0001 and SPRINT_3000_0100_0002
2. **Kickoff:** SPRINT_3000_0100_0001 (Signed Delta-Verdicts) implementation
3. **Code Generation:** Create C# models from JSON schemas
4. **Database Migrations:** Prepare PostgreSQL schema for `verdict_attestations` and `evidence_packs` tables
### Week 1 Milestones
- [ ] `VerdictPredicateBuilder` implemented with unit tests
- [ ] Policy Engine Attestor integration wired (feature-flagged)
- [ ] `VerdictAttestationHandler` accepts and signs predicates
- [ ] Evidence Locker storage + API endpoints (basic CRUD)
### Month 1 Goal
- Every policy run produces signed verdict attestations
- CLI can retrieve and verify verdicts offline
- Evidence packs can be created for policy runs
- Integration tests pass with 100% determinism
---
## Success Metrics
### Adoption Metrics
- **Verdict Attestations:** 100% of policy runs emit signed verdicts
- **Evidence Pack Usage:** 10+ packs created per day per tenant
- **Replay Success Rate:** 99%+ determinism verification
### Performance Metrics
- **Verdict Attestation Latency:** < 100ms per verdict
- **Pack Assembly Time:** < 2min for 10K findings
- **Replay Time:** < 60s for 10K verdicts
- **API Latency:** p95 < 500ms for verdict/pack retrieval
### Quality Metrics
- **Test Coverage:** > 90% for attestation + pack code
- **Schema Compliance:** 100% of artifacts validate against JSON schemas
- **Determinism Rate:** 100% replay verdicts match original
---
## References
- **Sprint Plans:** `docs/implplan/SPRINT_3000_0100_*.md`, `docs/implplan/SPRINT_4000_0100_*.md`
- **Schemas:** `docs/schemas/stellaops-policy-verdict.v1.schema.json`, `docs/schemas/stellaops-evidence-pack.v1.schema.json`
- **Documentation:** `docs/policy/verdict-attestations.md`, `docs/evidence-locker/evidence-pack-schema.md`
- **Advisory:** `docs/product-advisories/23-Dec-2026 - Competitor Scanner UI Breakdown.md`

View File

@@ -0,0 +1,714 @@
Heres a compact blueprint for two highleverage StellaOps capabilities that cut false positives and make audits portable across jurisdictions.
# 1) Patchaware backport detector (no humans in loop)
**Goal:** Stop flagging CVEs when a distro backported the fix but kept the old version string.
**How it works—in plain terms**
* **Compile equivalence maps per distro:**
* BuildID → symbol ranges → hunk hashes for core libraries/kernels.
* For each upstream CVE fix, store the minimal “hunk signature” (function, file path, before/after diff hash).
* **Autodiff at scan time:**
* From a container/VM, collect ELF BuildIDs and symbol tables (or BTF for kernels).
* Match against the equivalence map; if patched hunks are present, mark the artifact “fixedbybackport”.
* **Emit proofcarrying VEX:**
* Generate a signed VEX entry with `status:not_affected`, `justification: patched-backport`, and attach a **proof blob**: (artifact BuildIDs, matched hunk IDs, upstream commit refs, deterministic diff snippet).
* **Releasegate policy:**
* Gate only passes if (a) VEX is signed by an approved issuer, (b) proof blob verifies against our equivalence map, (c) CVE scoring policy is met.
**Minimal data model**
* `EquivalenceMap{ distro, package, version_like, build_id, [HunkSig{file,func, pre_hash, post_hash, upstream_commit}] }`
* `ProofBlob{ artifact_build_ids, matched_hunks[], verifier_log }`
* `VEX{ subject=digest/ref, cve, status, justification, issued_by, dsse_sig, proof_ref }`
**Pipeline sketch (where to run what)**
* **Feedser**: pulls upstream CVE patches → extracts HunkSig.
* **Sbomer**: captures BuildIDs for binaries in SBOM.
* **Vexer**: matches hunks → emits VEX + proof.
* **Authority/Attestor**: DSSEsigns; stores in OCI referrers.
* **Policy Engine**: enforces “accept only if proof verifies”.
**Testing targets (fast ROI)**
* glibc, openssl, zlib, curl, libxml2, Linux kernel LTS (common backports).
**Why its a moat**
* Precision jump without humans; reproducible proof beats “trust me” advisories.
---
# 2) Regional crypto & offline audit packs
**Goal:** Hand an auditor a single, sealed bundle that **replays identically** anywhere—while satisfying local crypto regimes.
**Whats inside the bundle**
* **Evidence:** SBOM (CycloneDX1.6/SPDX3.0.1), VEX set, reachability subgraph (source+postbuild), policy ledger with decisions.
* **Attestations:** DSSE/intoto for each step.
* **Replay manifest:** feed snapshots + rule versions + hashing seeds so a third party can reexecute and get the same verdicts.
**Dualstack signing profiles**
* eIDAS / ETSI (EU), FIPS (US), GOST/SM (RU/CN regional), plus optional PQC (Dilithium/Falcon) profile.
* Same content; different signature suites → auditor picks the locally valid one.
**Operating modes**
* **Connected:** push to an OCI registry with referrers and timestamping (Rekorcompatible mirror).
* **Airgapped:** tar+CAR archive with embedded TUF root, CRLs, and timestamped notary receipts.
**Verification UX (auditorfriendly)**
* One command: `stella verify --bundle bundle.car` → prints
(1) signature set validated, (2) replay hash match, (3) policy outcomes, (4) exceptions trail.
---
## Lightweight implementation plan (90day cut)
* **Weeks 13:**
* Extract HunkSig from upstream patches (git diff parser + normalizer).
* Build ELF symbol/BuildID collector; store perdistro maps.
* **Weeks 46:**
* VEXer: matching engine + `not_affected: patched-backport` schema + ProofBlob.
* DSSE signing with pluggable crypto providers; start with eIDAS+FIPS.
* **Weeks 79:**
* Offline bundle format (CAR/TAR) + replay manifest + verifier CLI.
* Policy gate: “accept if backport proof verifies”.
* **Weeks 1012:**
* Reachability subgraph export/import; deterministic reexecution harness.
* Docs + sample audits (openssl CVEs across Debian/Ubuntu/RHEL).
---
## UI hooks (keep it simple)
* **Finding:** “Backport Proofs” tab on a CVE detail → shows matched hunks and upstream commit links.
* **Deciding:** Release diff view lists CVEs → green badges “Patched via Backport (proofverified)”.
* **Auditing:** “Export Audit Pack” button at run level; pick signature profile(s); download bundle.
If you want, I can draft:
* the `HunkSig` extractor spec (inputs/outputs),
* the VEX schema extension and DSSE envelopes,
* the verifier CLI contract and sample CAR layout,
* or the policy snippets to wire this into your release gates.
Below is a developer-grade implementation guide for **patch-aware backport handling** across Alpine, Red Hat, Fedora, Debian, SUSE, Astra Linux, and “all other Linux used as Docker bases”. It is written as if you are building this inside Stella Ops (Feedser/Vexer/Sbomer/Scanner.Webservice, DSSE attestations, deterministic replay, Postgres+Valkey).
The key principle: **do not rely on upstream version strings**. For distros, “fixed” often means “patch backported with same NEVRA/version”. You must determine fix status by **distro patch metadata** plus **binary/source proof**.
---
## 0) What you are building
### Outputs (what must exist after implementation)
1. **DistroFix DB** (authoritative normalized knowledge)
* For each distro release + package + CVE:
* status: affected / fixed / not_affected / under_investigation / unknown
* fixed range expressed in distro terms (epoch/version/release or deb version) and/or advisory IDs
* proof pointers (errata, patch commit(s), SRPM/deb source, file hashes, build IDs)
2. **Backport Proof Engine**
* Given an image and its installed packages, produce a **deterministic VEX**:
* `status=not_affected` with `justification=patched-backport`
* proof blob: advisory id, package build provenance, patch signatures matched
3. **Policy integration**
* Gating rules treat “backport proof verified” as first-class evidence.
4. **Replayable scans**
* Same inputs (feed snapshots + rules + image digest) → same verdicts.
---
## 1) High-level approach (two-layer truth)
### Layer A — Distro intelligence (fast and usually sufficient)
For each distro, ingest its authoritative vulnerability metadata:
* advisory/errata streams
* distro CVE trackers
* security databases (Alpine secdb)
* OVAL / CPE / CSAF if available
* package repositories metadata
This provides “fixed in release X” at distro level.
### Layer B — Proof (needed for precision and audits)
When Layer A says “fixed” but the version looks “old”, prove it:
* **Source proof**: patch set present in source package (SRPM, debian patches, apkbuild git)
* **Binary proof**: vulnerable function/hunk signature is patched in shipped binary (BuildID + symbol/hunk signature match)
* **Build proof**: build metadata ties the binary to the source + patch set deterministically
You will use Layer B to:
* override false positives
* produce auditor-grade evidence
* operate offline with sealed snapshots
---
## 2) Core data model (Postgres schema guidance)
### 2.1 Canonical keys
You must normalize these identifiers:
* **Distro key**: `distro_family` + `distro_name` + `release` + `arch`
* e.g. `debian:12`, `rhel:9`, `alpine:3.19`, `sles:15sp5`, `astra:??`
* **Package key**: canonical package name plus ecosystem type
* `apk`, `rpm`, `deb`
* **CVE key**: `CVE-YYYY-NNNN`
### 2.2 Tables (minimum)
* `distro_release(id, family, name, version, codename, arch, eol_at, source)`
* `pkg_name(id, ecosystem, name, normalized_name)`
* `pkg_version(id, ecosystem, version_raw, version_norm, epoch, upstream_ver, release_ver)`
* `advisory(id, distro_release_id, advisory_type, advisory_id, published_at, url, raw_json_hash, snapshot_id)`
* `advisory_pkg(advisory_id, pkg_name_id, fixed_version_id NULL, fixed_range_json NULL, status, notes)`
* `cve(id, cve_id, severity, cwe, description_hash)`
* `cve_pkg_status(id, cve_id, distro_release_id, pkg_name_id, status, fixed_version_id NULL, advisory_id NULL, confidence, last_seen_snapshot_id)`
* `source_artifact(id, type, url, sha256, size, fetched_in_snapshot_id)`
* SRPM, `.dsc`, `.orig.tar`, `apkbuild`, patch files
* `patch_signature(id, cve_id, upstream_commit, file_path, function, pre_hash, post_hash, algo_version)`
* `build_provenance(id, distro_release_id, pkg_nevra_or_debver, build_id, source_artifact_id, buildinfo_artifact_id, signer, signed_at)`
* `binary_fingerprint(id, artifact_digest, path, elf_build_id, sha256, debuglink, arch)`
* `proof_blob(id, subject_digest, cve_id, pkg_name_id, distro_release_id, proof_type, proof_json, sha256)`
### 2.3 Version comparison engines
Implement **three comparators**:
* `rpmvercmp` (RPM EVR rules)
* `dpkg --compare-versions` equivalent (Debian version algorithm)
* Alpine `apk` version rules (similar to semver-ish but not semver; implement per apk-tools logic)
Do not “approximate”. Implement exact comparators or call system libraries inside controlled container images.
---
## 3) Feed ingestion per distro (Layer A)
### 3.1 Alpine (apk)
**Primary data**
* Alpine secdb repository (per branch) mapping CVEs ↔ packages, fixed versions.
**Ingestion**
* Pull secdb for each supported Alpine branch (3.x).
* Parse entries into `cve_pkg_status` with `fixed_version`.
**Package metadata**
* Pull `APKINDEX.tar.gz` for each repo (main/community) and arch.
* Store package version + checksum.
**Notes**
* Alpine often explicitly lists fixed versions; backports are less “opaque” than enterprise distros, but still validate.
### 3.2 Red Hat Enterprise Linux (rhel) & UBI
**Primary data**
* Red Hat Security Data: CVE ↔ packages, errata, states.
* Errata stream provides authoritative “fixed in RHSA-…”.
**Ingestion**
* For each RHEL major/minor you support (8, 9; optionally 7), pull:
* CVE objects + affected products + package states
* Errata (RHSA) objects and their fixed package NEVRAs
* Populate `advisory` + `advisory_pkg`.
* Derive `cve_pkg_status` from errata.
**Package metadata**
* Use repository metadata (repomd.xml + primary.xml.gz) for BaseOS/AppStream/CRB, etc.
* Record NEVRA and checksums.
**Enterprise backport reality**
* RHEL frequently backports fixes while keeping old upstream version. Your engine must prefer **errata fixed NEVRA** over upstream version meaning.
### 3.3 Fedora (rpm)
Fedora is closer to upstream; still ingest advisories.
**Primary data**
* Fedora security advisories / updateinfo (often via repository updateinfo.xml.gz)
* OVAL may exist for some streams.
**Ingestion**
* Parse updateinfo to map CVE → fixed NEVRA.
* For Fedora rawhide/rolling, treat as high churn; snapshots must be time-bounded.
### 3.4 Debian (deb)
**Primary data**
* Debian Security Tracker (CVE status per release + package, fixed versions)
* DSA advisories.
**Ingestion**
* Pull Debian security tracker data, parse per release (stable, oldstable).
* Normalize Debian versions exactly.
* Store “fixed in” version.
**Package metadata**
* Parse `Packages.gz` from security + main repos.
* Optionally `Sources.gz` for source package mapping.
### 3.5 SUSE (SLES / openSUSE) (rpm)
**Primary data**
* SUSE security advisories (often published as CSAF; also SUSE OVAL historically)
* Updateinfo in repos.
**Ingestion**
* Prefer CSAF/official advisory feed when available; otherwise parse `updateinfo.xml.gz`.
* Map CVE → fixed packages.
### 3.6 Astra Linux (deb-family, often)
Astra is niche and may have bespoke advisories/mirrors.
**Primary data**
* Astra security bulletins and repository metadata.
* If they publish a tracker or advisories in a machine-readable format, ingest it; otherwise:
* treat repo metadata + changelogs as the canonical signal.
**Ingestion strategy**
* Implement a generic “Debian-family fallback”:
* ingest `Packages.gz` and `Sources.gz` from Astra repos
* ingest available security bulletin feed (HTML/JSON); parse with a deterministic extractor
* if advisories are sparse, rely on Layer B proof more heavily (source patch presence + binary proof)
### 3.7 “All other Linux used on docker repositories”
Handle this by **distro families** plus a plugin pattern:
* Debian family (Ubuntu, Kali, Astra, Mint): use Debian comparator + `Packages/Sources` + their security tracker if exists
* RPM family (RHEL clones: Rocky/Alma/Oracle; Amazon Linux): rpm comparator + updateinfo/OVAL/errata equivalents
* Alpine family (Wolfi/apko-like): their own secdb or APKINDEX equivalents
* Distroless/scratch: no package manager; you must fall back to binary scanning only (Layer B).
**Developer action**
* Create an interface `IDistroProvider` with:
* `EnumerateReleases()`
* `FetchAdvisories(snapshot)`
* `FetchRepoMetadata(snapshot)`
* `NormalizePackageName(...)`
* `CompareVersions(a,b)`
* `ParseInstalledPackages(image)` (if package manager exists)
* Implement providers: `AlpineProvider`, `DebianProvider`, `RpmProvider`, `SuseProvider`, `AstraProvider`, plus “GenericDebianFamilyProvider”, “GenericRpmFamilyProvider”.
---
## 4) Installed package extraction (inside scan)
### 4.1 Determine OS identity
From image filesystem:
* `/etc/os-release` (ID, VERSION_ID)
* distro-specific markers:
* Alpine: `/etc/alpine-release`
* Debian: `/etc/debian_version`
* RHEL: `/etc/redhat-release`
Write a deterministic resolver:
* if `/etc/os-release` missing, fall back to:
* package DB presence: `/lib/apk/db/installed`, `/var/lib/dpkg/status`, rpmdb paths
* ELF libc fingerprint heuristics (last resort)
### 4.2 Extract installed packages deterministically
* Alpine: parse `/lib/apk/db/installed`
* Debian: parse `/var/lib/dpkg/status`
* RPM: parse rpmdb (use `rpm` tooling in a controlled helper container, or implement rpmdb reader; prefer tooling for correctness)
Store:
* package name
* version string (raw)
* arch
* source package mapping if available (Debians `Source:` fields; RPMs `Sourcerpm`)
---
## 5) The backport proof engine (Layer B)
This is the “precision jump”. It has three proof modes; implement all three and choose best available.
### Proof mode 1 — Advisory fixed NEVRA/version match (fast)
If the distros errata/DSA/updateinfo says fixed in `X`, and installed package version compares ≥ X (using correct comparator):
* mark fixed with `confidence=high`
* attach advisory reference only
This already addresses many cases.
### Proof mode 2 — Source patch presence (best for distros with source repos)
Prove the patch is in the source package even if version looks old.
#### Debian-family
* Determine source package:
* from `dpkg status` “Source:” if present; otherwise map binary→source via `Sources.gz`
* Fetch source:
* `.dsc` + referenced tarballs + `debian/patches/*` (or `debian/patches/series`)
* Patch signature verification:
* For CVE, you maintain `patch_signature` derived from upstream fix commits:
* identify file/function/hunk; store normalized diff hashes (ignore whitespace/context drift)
* Apply:
* check if any distro patch file contains the “post” signature (or the vulnerable code is absent)
* Record in `proof_blob`:
* source artifact SHA256
* patch file names
* matching signature IDs
* deterministic verifier log
#### RPM-family (RHEL/Fedora/SUSE)
* Determine SRPM from installed RPM metadata (Sourcerpm field).
* Fetch SRPM from source repo (or debug/source channel).
* Extract patches from SRPM spec + sources.
* Verify patch signatures as above.
#### Alpine
* Determine `apkbuild` and patches for the package version (Alpine aports)
* Verify patch signature.
### Proof mode 3 — Binary hunk/signature match (works even without source repos)
This is your universal fallback (also for distroless).
#### Build fingerprints
* For each ELF binary in the package or image:
* compute `sha256`
* read `ELF BuildID` if present
* capture `.gnu_debuglink` if present
* capture symbols (when available)
#### Signature strategy
For each CVE fix, create one or more **binary-checkable predicates**:
* vulnerable function contains a known byte sequence that disappears after fix
* or patched function includes a new basic block pattern
* or a string constant changes (weak, but sometimes useful)
* or the compile-time feature toggles
Implement as `BinaryPredicate` objects:
* `type`: bytepattern | cfghash | symbolrangehash | rodata-string
* `scope`: file path patterns / package name constraints
* `arch`: x86_64/aarch64 etc.
* `algo_version`: so you can evolve without breaking replay
Evaluation:
* locate candidate binaries (package manifest, common library paths)
* apply predicate in a stable order
* if “fixed predicate” matches and “vulnerable predicate” does not:
* produce proof
#### Evidence quality
Binary proof must include:
* file path + sha256
* BuildID if available
* predicate ID + algorithm version
* extractor/verifier version hashes
---
## 6) Building the patch signature corpus (no humans)
### 6.1 Upstream patch harvesting (Feedser)
For each CVE:
* find upstream fix commits (NVD references, project advisories, distro patch references)
* fetch git diffs
* normalize to `patch_signature`:
* (file path, function name if detectable, pre hash, post hash)
* store multiple signatures per CVE if multiple upstream branches
You will not always find perfect fix commits. When missing:
* fall back to distro-specific patch extraction (learn signatures from distro patch itself)
* mark `signature_origin=distro-learned` but keep it auditable
### 6.2 Deterministic normalization rules
* strip diff metadata that varies
* normalize whitespace
* compute hashes over:
* token stream (C/C++ tokens; for others line-based)
* include hunk context windows
* store `algo_version` and never change semantics without bumping
---
## 7) Decision algorithm (deterministic, ordered, explainable)
For each `(image_digest, distro_release, pkg, cve)`:
1. **If distro provider has explicit status “not affected”** (e.g., vulnerable code not present in that distro build):
* emit VEX not_affected with advisory proof
2. **Else if advisory says fixed in version/NEVRA** and installed compares as fixed:
* emit VEX fixed with advisory proof
3. **Else if source proof succeeds**:
* emit VEX not_affected / fixed (depending on semantics) with `justification=patched-backport`
4. **Else if binary proof succeeds**:
* emit VEX not_affected / fixed with binary proof
5. Else:
* affected/unknown depending on policy, but always attach “why unknown” in evidence.
This order is critical to keep runtime reasonable and proofs consistent.
---
## 8) Engineering constraints for Docker base images
### 8.1 Multi-stage images and removed package DBs
Many production images delete package databases to slim.
Your scan must handle:
* no dpkg status, no rpmdb, no apk db
In this case:
* try SBOM from build provenance (if you have it)
* otherwise treat as **binary-only**:
* scan ELF binaries + shared libs
* map to known package/binary fingerprints where possible
* rely on Proof mode 3
### 8.2 Minimal images (distroless, scratch)
* There is no OS metadata; dont pretend.
* Mark distro as `unknown`, skip Layer A, go straight to binary proof.
* Policy should treat unknowns explicitly (your existing “unknown budget” moat).
---
## 9) Implementation structure in .NET 10 (practical module map)
### 9.1 Services and boundaries
* **Feedser**
* pulls distro advisories/trackers/repo metadata
* produces normalized `DistroFix` snapshots
* **Sbomer**
* produces SBOM + captures file fingerprints, BuildIDs
* **Scanner.Webservice**
* runs the deterministic evaluation and lattice/policy logic (per your standing rule)
* does proof verification + emits signed verdicts
* **Vexer**
* aggregates VEX claims + attaches proof blobs (but evaluation logic stays in Scanner.Webservice)
* **Authority/Attestor**
* DSSE signing, OCI referrers, audit pack exports
### 9.2 Core libraries
Create a library `StellaOps.Security.Distro`:
* `IDistroProvider`
* `IVersionComparator`
* `IInstalledPackageExtractor`
* `IAdvisoryParser`
* `ISourceProofVerifier`
* `IBinaryProofVerifier`
Each provider implements:
* parsing
* comparator
* extraction for its ecosystem
### 9.3 Determinism rules (must be enforced)
* Every scan references a specific `snapshot_id` for feeds.
* Proof computations are pure functions of:
* image digest
* extracted artifacts
* snapshot content hashes
* algorithm version hashes
* Logs included in proof blobs must be stable (no timestamps unless separately recorded).
---
## 10) Test strategy (non-negotiable)
### 10.1 Golden corpus images
Build a repo of fixtures:
* `alpine:3.18`, `alpine:3.19`
* `debian:11`, `debian:12`
* `ubuntu:22.04`, `ubuntu:24.04`
* `ubi9`, `ubi8` (or rhel-like equivalents you can legally test)
* `fedora:40+`
* `opensuse/leap`, `sles` if accessible
* Astra base images if you use them internally
For each fixture:
* pick 10 known CVEs across openssl, curl, zlib, glibc, libxml2
* store expected decisions:
* vulnerable vs fixed, including backported cases
* run in CI with locked snapshots
### 10.2 Comparator test suites
For RPM and Debian version compare:
* ingest official comparator test vectors (or recreate known tricky cases)
* unit tests must include:
* epoch handling
* tilde ordering in Debian versions
* rpm release ordering
### 10.3 Proof verifier tests
* source proof: patch signature detection on extracted SRPM/deb sources
* binary proof: fixed/vulnerable predicate detection on controlled binaries
---
## 11) Practical rollout plan (how developers should implement)
### Phase 1 — Layer A for all major distros (fast coverage)
1. Implement comparators: rpm, deb, apk.
2. Implement providers:
* Alpine secdb + APKINDEX
* Debian security tracker + Packages/Sources
* RHEL errata/CVE feed + repo metadata
* Fedora updateinfo + repo metadata
* SUSE advisory/updateinfo
* Generic Debian/RPM fallback providers
3. Produce `cve_pkg_status` snapshots and basic VEX results using only Layer A.
### Phase 2 — Source proof (removes most false positives)
1. Implement upstream patch harvesting → `patch_signature`.
2. Implement Debian source fetch + verification.
3. Implement SRPM fetch + verification.
4. Wire into decision algorithm, emit proof blobs.
### Phase 3 — Binary proof (covers stripped/minimal images)
1. Implement ELF fingerprint extraction + BuildID store.
2. Implement predicate framework + initial predicates for top libraries.
3. Add caching (Valkey) for predicate results keyed by `(file sha256, predicate_id, algo_version)`.
### Phase 4 — Astra and niche distros hardening
1. Implement Astra provider:
* repo metadata ingestion
* advisory parsing if available
* otherwise rely on source/binary proof
2. Add distro onboarding kit:
* define endpoints for repo metadata
* define advisory parsing rules
* define comparator family
---
## 12) What “done” means (acceptance criteria)
A developer story is “complete” only if:
* For each supported distro family, you can:
* identify distro release
* extract installed packages OR fall back to binary-only
* compute fix status for at least top 50 packages
* produce VEX with either advisory or proof
* At least 100 backport scenarios across RHEL/Debian/SUSE pass golden tests.
* Scan is replayable:
* freeze snapshot, rerun, identical verdict hash.
---
If you want this converted into your internal sprintable format (epics/stories/tasks + acceptance tests + file/module layout in a .NET 10 repo), I can output it as:
* `SPECS.md` section “Patch-aware Backport Handling”
* `CONTRACTS.md` (provider interfaces, proof blob schema, DSSE envelopes)
* `DB_REPOSITORIES.md` migrations outline
* `IMPLEMENTATION.md` with step-by-step task breakdown per distro.