diff --git a/docs/product-advisories/29-Nov-2025 - Acceptance Tests Pack for StellaOps Guardrails.md b/docs/product-advisories/29-Nov-2025 - Acceptance Tests Pack for StellaOps Guardrails.md new file mode 100644 index 000000000..1adb5ee16 --- /dev/null +++ b/docs/product-advisories/29-Nov-2025 - Acceptance Tests Pack for StellaOps Guardrails.md @@ -0,0 +1,792 @@ +Here’s a tight, drop-in acceptance-tests pack for Stella Ops that turns common failure modes into concrete guardrails you can ship this sprint. + +--- + +# 1) Feed outages & integrity drift (e.g., Grype DB / CDN hiccups) + +**Lesson:** Never couple scans to a single live feed; pin, verify, and cache. + +**Add to acceptance tests** + +* **Rollback-safe updaters** + + * If a feed update fails checksum or signature, the system keeps using the last “good” bundle. + * On restart, the updater falls back to the last verified bundle without network access. +* **Signed offline bundles** + + * Every feed bundle (SBOM catalogs, CVE DB shards, rules) must be DSSE-signed; verification blocks ingestion on mismatch. + * Bundle manifest lists SHA-256 for each file; any deviation = reject. + +**Test cases (CI)** + +* Simulate 404/timeout from feed URL → scanner still produces results from cached bundle. +* Serve a tampered bundle (wrong hash) → updater logs failure; no swap; previous bundle remains active. +* Air-gap mode: no network → scanner loads from `/var/lib/stellaops/offline-bundles/*` and passes verification. + +--- + +# 2) SBOM quality & schema drift + +**Lesson:** Garbage in = garbage VEX. Gate on schema, completeness, and provenance. + +**Add to acceptance tests** + +* **SBOM schema gating** + + * Reject SBOMs not valid CycloneDX 1.6 / SPDX 2.3 (your chosen set). + * Require: component `bom-ref`, supplier, version, hashes, and build provenance (SLSA/in-toto attestation) if provided. +* **Minimum completeness** + + * Thresholds: ≥95% components with cryptographic hashes; no unknown package ecosystem fields for top 20 deps. + +**Test cases** + +* Feed malformed CycloneDX → `400 SBOM_VALIDATION_FAILED` with pointer to failing JSON path. +* SBOM missing hashes for >5% of components → blocked from graph ingestion; actionable error. +* SBOM with unsigned provenance when policy="RequireAttestation" → rejected. + +--- + +# 3) DB/data corruption or operator error + +**Lesson:** Snapshots save releases. + +**Add to acceptance tests** + +* **DB snapshot cadence** + + * Postgres: base backup nightly + WAL archiving; RPO ≤ 15 min; automated restore rehearsals. + * Mongo (while still in use): per-collection dumps until conversion completes; checksum each artifact. +* **Deterministic replay** + + * Any graph view must be reproducible from snapshot + bundle manifest (same revision hash). + +**Test cases** + +* Run chaos test that deletes last 24h tables → PITR restore to T-15m succeeds; graph revision IDs match pre-failure. +* Restore rehearsal produces identical VEX verdict counts for a pinned revision. + +--- + +# 4) Reachability engines & graph evaluation flakiness + +**Lesson:** When reachability is uncertain, degrade gracefully and be explicit. + +**Add to acceptance tests** + +* **Reachability fallbacks** + + * If call-graph build fails or language analyzer missing, verdict moves to “Potentially Affected (Unproven Reach)” with a reason code. + * Policies must allow “conservative mode” (assume reachable) vs “lenient mode” (assume not-reachable) toggled per environment. +* **Stable graph IDs** + + * Graph revision ID is a content hash of inputs (SBOM set + rules + feed versions); identical inputs → identical ID. + +**Test cases** + +* Remove a language analyzer container at runtime → status flips to fallback code; no 500s; policy evaluation still completes. +* Re-ingest same inputs → same graph revision ID and same verdict distribution. + +--- + +# 5) Update pipelines & job routing + +**Lesson:** No single point of truth; isolate, audit, and prove swaps. + +**Add to acceptance tests** + +* **Two-phase bundle swaps** + + * Stage → verify → atomic symlink/label swap; all scanners pick up new label within 1 minute, or roll back. +* **Authority-gated policy changes** + + * Any policy change (severity threshold, allowlist) is a signed request via Authority; audit trail must include signer and DSSE envelope hash. + +**Test cases** + +* Introduce a new CVE ruleset; verification passes → atomic swap; running scans continue; new scans use N+1 bundle. +* Attempt policy change with invalid signature → rejected; audit log entry created; unchanged policy in effect. + +--- + +## How to wire this in Stella Ops (quick pointers) + +* **Offline bundle format** + + * `bundle.json` (manifest: file list + SHA-256 + DSSE signature), `/sboms/*.json`, `/feeds/cve/*.sqlite` (or shards), `/rules/*.yaml`, `/provenance/*.intoto.jsonl`. + * Verification entrypoint in .NET 10: `StellaOps.Bundle.VerifyAsync(manifest, keyring)` before any ingestion. + +* **Authority integration** + + * Define `PolicyChangeRequest` (subject, diff, reason, expiry, DSSE envelope). + * Gate `PUT /policies/*` behind `Authority.Verify(envelope) == true` and `envelope.subject == computed_diff_hash`. + +* **Graph determinism** + + * `GraphRevisionId = SHA256(Sort(JSON([SBOMRefs, RulesetVersion, FeedBundleIds, LatticeConfig, NormalizationVersion])))`. + +* **Postgres snapshots (until full conversion)** + + * Use `pg_basebackup` nightly + `wal-g` for WAL; GitLab job runs restore rehearsal weekly into `stellaops-restore` namespace and asserts revision parity against prod. + +--- + +## Minimal developer checklist (copy to your sprint board) + +* [ ] Add `BundleVerifier` to scanner startup; block if verification fails. +* [ ] Implement `CacheLastGoodBundle()` and atomic label swap (`/current -> /v-YYYYMMDDHHmm`). +* [ ] Add `SbomGate` with JSON-Schema validation + completeness thresholds. +* [ ] Emit reasoned fallbacks: `REACH_FALLBACK_NO_ANALYZER`, `REACH_FALLBACK_TIMEOUT`. +* [ ] Compute and display `GraphRevisionId` everywhere (API + UI + logs). +* [ ] Configure nightly PG backups + weekly restore rehearsal that asserts revision parity. +* [ ] Route all policy mutations through Authority DSSE verification + auditable ledger entry. + +If you want, I can turn this into ready-to-merge .NET test fixtures (xUnit) and a GitLab CI job that runs the feed-tamper/air-gap simulations automatically. +I’ll take the 5 “miss” areas and turn them into concrete, implementable test plans, with suggested projects, fixtures, and key cases your team can start coding. + +I’ll keep names aligned to .NET 10/xUnit and your Stella Ops modules. + +--- + +## 0. Test layout proposal + +**Solution structure (tests)** + +```text +/tests + /StellaOps.Bundle.Tests + BundleVerificationTests.cs + CachedBundleFallbackTests.cs + /StellaOps.SbomGate.Tests + SbomSchemaValidationTests.cs + SbomCompletenessTests.cs + /StellaOps.Scanner.Tests + ScannerOfflineBundleTests.cs + ReachabilityFallbackTests.cs + GraphRevisionDeterminismTests.cs + /StellaOps.DataRecoverability.Tests + PostgresSnapshotRestoreTests.cs + GraphReplayParityTests.cs + /StellaOps.Authority.Tests + PolicyChangeSignatureTests.cs + /StellaOps.System.Acceptance + FeedOutageEndToEndTests.cs + AirGapModeEndToEndTests.cs + BundleSwapEndToEndTests.cs +/testdata + /bundles + /sboms + /graphs + /db +``` + +Use xUnit + FluentAssertions, plus Testcontainers for Postgres. + +--- + +## 1) Feed outages & integrity drift + +### Objectives + +1. Scanner never “goes dark” because the CDN/feed is down. +2. Only **verified** bundles are used; tampered bundles are never ingested. +3. Offline/air-gap mode is a first-class, tested behavior. + +### Components under test + +* `StellaOps.BundleVerifier` (core library) +* `StellaOps.Scanner.Webservice` (scanner, bundle loader) +* Bundle filesystem layout: + `/opt/stellaops/bundles/v-/*` + `/opt/stellaops/bundles/current` symlink + +### Test dimensions + +* Network: OK / timeout / 404 / TLS failure / DNS failure. +* Remote bundle: correct / tampered (hash mismatch) / wrong signature / truncated. +* Local cache: last-good present / absent / corrupted. +* Mode: online / offline (air-gap). + +### Detailed test suites + +#### 1.1 Bundle verification unit tests + +**Project:** `StellaOps.Bundle.Tests` + +**Fixtures:** + +* `testdata/bundles/good-bundle/` +* `testdata/bundles/hash-mismatch-bundle/` +* `testdata/bundles/bad-signature-bundle/` +* `testdata/bundles/missing-file-bundle/` + +**Key tests:** + +1. `VerifyAsync_ValidBundle_ReturnsSuccess` + + * Arrange: Load `good-bundle` manifest + DSSE signature. + * Act: `BundleVerifier.VerifyAsync(manifest, keyring)` + * Assert: + + * `result.IsValid == true` + * `result.Files.All(f => f.Status == Verified)` + +2. `VerifyAsync_HashMismatch_FailsFast` + + * Use `hash-mismatch-bundle` where one file’s SHA256 differs. + * Assert: + + * `IsValid == false` + * `Errors` contains `BUNDLE_FILE_HASH_MISMATCH` and the offending path. + +3. `VerifyAsync_InvalidSignature_RejectsBundle` + + * DSSE envelope signed with unknown key. + * Assert: + + * `IsValid == false` + * `Errors` contains `BUNDLE_SIGNATURE_INVALID`. + +4. `VerifyAsync_MissingFile_RejectsBundle` + + * Manifest lists file that does not exist on disk. + * Assert: + + * `IsValid == false` + * `Errors` contains `BUNDLE_FILE_MISSING`. + +#### 1.2 Cached bundle fallback logic + +**Class under test:** `BundleManager` + +Simplified interface: + +```csharp +public interface IBundleManager { + Task GetActiveBundleAsync(); + Task UpdateFromRemoteAsync(CancellationToken ct); +} +``` + +**Key tests:** + +1. `UpdateFromRemoteAsync_RemoteUnavailable_KeepsLastGoodBundle` + + * Arrange: + + * `lastGood` bundle exists and is marked verified. + * Remote HTTP client always throws `TaskCanceledException` (simulated timeout). + * Act: `UpdateFromRemoteAsync`. + * Assert: + + * Returned bundle ID equals `lastGood.Id`. + * No changes to `current` symlink. + +2. `UpdateFromRemoteAsync_RemoteTampered_DoesNotReplaceCurrent` + + * Remote returns bundle `temp-bundle` which fails `BundleVerifier`. + * Assert: + + * `current` still points to `lastGood`. + * An error metric is emitted (e.g. `stellaops_bundle_update_failures_total++`). + +3. `GetActiveBundle_NoVerifiedBundle_ThrowsDomainError` + + * No bundle is verified on disk. + * `GetActiveBundleAsync` throws a domain exception with code `NO_VERIFIED_BUNDLE_AVAILABLE`. + * Consumption pattern in Scanner: scanner fails fast on startup with clear log. + +#### 1.3 Scanner behavior with outages (integration) + +**Project:** `StellaOps.Scanner.Tests` + +Use in-memory host (`WebApplicationFactory`). + +**Scenarios:** + +* F1: CDN timeout, last-good present. +* F2: CDN 404, last-good present. +* F3: CDN returns tampered bundle; verification fails. +* F4: Air-gap: network disabled, last-good present. +* F5: Air-gap + no last-good: scanner must refuse to start. + +Example test: + +```csharp +[Fact] +public async Task Scanner_UsesLastGoodBundle_WhenCdnTimesOut() { + // Arrange: put good bundle under /bundles/v-1, symlink /bundles/current -> v-1 + using var host = TestScannerHost.WithBundle("v-1", good: true, simulateCdnTimeout: true); + + // Act: call /api/scan with small fixture image + var response = await host.Client.PostAsJsonAsync("/api/scan", scanRequest); + + // Assert: + response.StatusCode.Should().Be(HttpStatusCode.OK); + var content = await response.Content.ReadFromJsonAsync(); + content.BundleId.Should().Be("v-1"); + host.Logs.Should().Contain("Falling back to last verified bundle"); +} +``` + +#### 1.4 System acceptance (GitLab CI) + +**Job idea:** `acceptance:feed-resilience` + +Steps: + +1. Spin up `scanner` + stub `feedser` container. +2. Phase A: feed OK → run baseline scan; capture `bundleId` and `graphRevisionId`. +3. Phase B: re-run with feed stub configured to: + + * timeout, + * 404, + * return tampered bundle. +4. For each phase: + + * Assert `bundleId` remains the baseline one. + * Assert `graphRevisionId` unchanged. + +Failure of any assertion should break the pipeline. + +--- + +## 2) SBOM quality & schema drift + +### Objectives + +1. Only syntactically valid SBOMs are ingested into the graph. +2. Enforce minimum completeness (hash coverage, supplier etc.). +3. Clear, machine-readable error responses from SBOM ingestion API. + +### Components + +* `StellaOps.SbomGate` (validation service) +* SBOM ingestion endpoint in Scanner/Concelier: `POST /api/sboms` + +### Schema validation tests + +**Project:** `StellaOps.SbomGate.Tests` + +**Fixtures:** + +* `sbom-cdx-1.6-valid.json` +* `sbom-cdx-1.6-malformed.json` +* `sbom-spdx-2.3-valid.json` +* `sbom-unsupported-schema.json` +* `sbom-missing-hashes-10percent.json` +* `sbom-no-supplier.json` + +**Key tests:** + +1. `Validate_ValidCycloneDx16_Succeeds` + + * Assert type `SbomValidationResult.Success`. + * Ensure `DetectedSchema == CycloneDx16`. + +2. `Validate_MalformedJson_FailsWithSyntaxError` + + * Malformed JSON. + * Assert: + + * `IsValid == false` + * `Errors` contains `SBOM_JSON_SYNTAX_ERROR` with path info. + +3. `Validate_UnsupportedSchemaVersion_Fails` + + * SPDX 2.1 (if you only allow 2.3). + * Expect `SBOM_SCHEMA_UNSUPPORTED` with `schemaUri` echo. + +4. `Validate_MissingHashesOverThreshold_Fails` + + * SBOM where >5% components lack hashes. + * Policy: `MinHashCoverage = 0.95`. + * Assert: + + * `IsValid == false` + * `Errors` contains `SBOM_HASH_COVERAGE_BELOW_THRESHOLD` with actual ratio. + +5. `Validate_MissingSupplier_Fails` + + * Critical components missing supplier info. + * Expect `SBOM_REQUIRED_FIELD_MISSING` with `component.supplier`. + +### API-level tests + +**Project:** `StellaOps.Scanner.Tests` (or `StellaOps.Concelier.Tests` depending where SBOM ingestion lives). + +Key scenarios: + +1. `POST /api/sboms` with malformed JSON + + * Request body: `sbom-cdx-1.6-malformed.json`. + * Expected: + + * HTTP 400. + * Body: `{ "code": "SBOM_VALIDATION_FAILED", "details": [ ... ], "correlationId": "..." }`. + * At least one detail contains `SBOM_JSON_SYNTAX_ERROR`. + +2. `POST /api/sboms` with missing hashes + + * Body: `sbom-missing-hashes-10percent.json`. + * HTTP 400 with `SBOM_HASH_COVERAGE_BELOW_THRESHOLD`. + +3. `POST /api/sboms` with unsupported schema + + * Body: `sbom-unsupported-schema.json`. + * HTTP 400 with `SBOM_SCHEMA_UNSUPPORTED`. + +4. `POST /api/sboms` valid + + * Body: `sbom-cdx-1.6-valid.json`. + * HTTP 202 or 201 (depending on design). + * Response contains SBOM ID; subsequent graph build sees that SBOM. + +--- + +## 3) DB/data corruption & operator error + +### Objectives + +1. You can restore Postgres to a point in time and reproduce previous graph results. +2. Graphs are deterministic given bundle + SBOM + rules. +3. Obvious corruptions are detected and surfaced, not silently masked. + +### Components + +* Postgres cluster (new canonical store) +* `StellaOps.Scanner.Webservice` (graph builder, persistence) +* `GraphRevisionId` computation + +### 3.1 Postgres snapshot / WAL tests + +**Project:** `StellaOps.DataRecoverability.Tests` + +Use Testcontainers to spin up Postgres. + +Scenarios: + +1. `PITR_Restore_ReplaysGraphsWithSameRevisionIds` + + * Arrange: + + * Spin DB container with WAL archiving enabled. + * Apply schema migrations. + * Ingest fixed set of SBOMs + bundle refs + rules. + * Trigger graph build → record `graphRevisionIds` from API. + * Take base backup snapshot (simulate daily snapshot). + * Act: + + * Destroy container. + * Start new container from base backup + replay WAL up to a specific LSN. + * Start Scanner against restored DB. + * Query graphs again. + * Assert: + + * For each known graph: `revisionId_restored == revisionId_original`. + * Number of nodes/edges is identical. + +2. `PartialDataLoss_DetectedByHealthCheck` + + * After initial load, deliberately delete some rows (e.g. all edges for a given graph). + * Run health check endpoint, e.g. `/health/graph`. + * Expect: + + * HTTP 503. + * Body indicates `GRAPH_INTEGRITY_FAILED` with details of missing edges. + +This test forces a discipline to implement a basic graph integrity check (e.g. counts by state vs expected). + +### 3.2 Deterministic replay tests + +**Project:** `StellaOps.Scanner.Tests` → `GraphRevisionDeterminismTests.cs` + +**Precondition:** Graph revision ID computed as: + +```csharp +GraphRevisionId = SHA256( + Normalize([ + BundleId, + OrderedSbomIds, + RulesetVersion, + FeedBundleIds, + LatticeConfigVersion, + NormalizationVersion + ]) +); +``` + +**Scenarios:** + +1. `SameInputs_SameRevisionId` + + * Run graph build twice for same inputs. + * Assert identical `GraphRevisionId`. + +2. `DifferentBundle_DifferentRevisionId` + + * Same SBOMs & rules; change vulnerability bundle ID. + * Assert `GraphRevisionId` changes. + +3. `DifferentRuleset_DifferentRevisionId` + + * Same SBOM & bundle; change ruleset version. + * Assert `GraphRevisionId` changes. + +4. `OrderingIrrelevant_StableRevision` + + * Provide SBOMs in different order. + * Assert ` GraphRevisionId` same (because of internal sorting). + +--- + +## 4) Reachability engine & graph evaluation flakiness + +### Objectives + +1. If reachability cannot be computed, you do not break; you downgrade verdicts with explicit reason codes. +2. Deterministic reachability for “golden fixtures”. +3. Graph evaluation remains stable even when analyzers come and go. + +### Components + +* `StellaOps.Scanner.Webservice` (lattice / reachability engine) +* Language analyzers (sidecar or gRPC microservices) +* Verdict representation, e.g.: + +```csharp +public sealed record VulnerabilityVerdict( + string Status, // "NotAffected", "Affected", "PotentiallyAffected" + string ReasonCode, // "REACH_CONFIRMED", "REACH_FALLBACK_NO_ANALYZER", ... + string? AnalyzerId +); +``` + +### 4.1 Golden reachability fixtures + +**Project:** `StellaOps.Scanner.Tests` → `GoldenReachabilityTests.cs` +**Fixtures directory:** `/testdata/reachability/fixture-*/` + +Each fixture: + +```text +/testdata/reachability/fixture-01-log4j/ + sbom.json + code-snippets/... + expected-vex.json + config.json # language, entrypoints, etc. +``` + +**Test pattern:** + +For each fixture: + +1. Load SBOM + configuration. +2. Trigger reachability analysis. +3. Collect raw reachability graph + final VEX verdicts. +4. Compare to `expected-vex.json` (status + reason codes). +5. Store the `GraphRevisionId` and set it as golden as well. + +Key cases: + +* R1: simple direct call → reachability confirmed → `Status = "Affected", ReasonCode = "REACH_CONFIRMED"`. +* R2: library present but not called → `Status = "NotAffected", ReasonCode = "REACH_ANALYZED_UNREACHABLE"`. +* R3: language analyzer missing → `Status = "PotentiallyAffected", ReasonCode = "REACH_FALLBACK_NO_ANALYZER"`. +* R4: analysis timeout → `Status = "PotentiallyAffected", ReasonCode = "REACH_FALLBACK_TIMEOUT"`. + +### 4.2 Analyzer unavailability / fallback behavior + +**Project:** `StellaOps.Scanner.Tests` → `ReachabilityFallbackTests.cs` + +Scenarios: + +1. `NoAnalyzerRegistered_ForLanguage_UsesFallback` + + * Scanner config lists a component in language “go” but no analyzer registered. + * Expect: + + * No 500 error from `/api/graphs/...`. + * All applicable vulnerabilities for that component have `Status = "PotentiallyAffected"` and `ReasonCode = "REACH_FALLBACK_NO_ANALYZER"`. + +2. `AnalyzerRpcFailure_UsesFallback` + + * Analyzer responds with gRPC error or HTTP 500. + * Scanner logs error and keeps going. + * Same semantics as missing analyzer, but with `AnalyzerId` populated and optional `ReasonDetails` (e.g. `RPC_UNAVAILABLE`). + +3. `AnalyzerTimeout_UsesTimeoutFallback` + + * Force analyzer calls to time out. + * `ReasonCode = "REACH_FALLBACK_TIMEOUT"`. + +### 4.3 Concurrency & determinism + +Add a test that: + +1. Triggers N parallel graph builds for the same inputs. +2. Asserts that: + + * All builds succeed. + * All `GraphRevisionId` are identical. + * All reachability reason codes are identical. + +This is important for concurrent scanners and ensures lack of race conditions in graph construction. + +--- + +## 5) Update pipelines & job routing + +### Objectives + +1. Bundle swaps are atomic: scanners see either old or new, never partially written bundles. +2. Policy changes are always signed via Authority; unsigned/invalid changes never apply. +3. Job routing changes (if/when you move to direct microservice pools) remain stateless and testable. + +### 5.1 Two-phase bundle swap tests + +**Bundle layout:** + +* `/opt/stellaops/bundles/current` → symlink to `v-YYYYMMDDHHmmss` +* New bundle: + + * Download to `/opt/stellaops/bundles/staging/` + * Verify + * Atomic `ln -s v-new current.tmp && mv -T current.tmp current` + +**Project:** `StellaOps.Bundle.Tests` → `BundleSwapTests.cs` + +Scenarios: + +1. `Swap_Success_IsAtomic` + + * Simulate swap in a temp directory. + * During swap, spawn parallel tasks that repeatedly read `current` and open `manifest.json`. + * Assert: + + * Readers never fail with “file not found” / partial manifest. + * Readers only see either `v-old` or `v-new`, no mixed state. + +2. `Swap_VerificationFails_NoChangeToCurrent` + + * Stage bundle which fails `BundleVerifier`. + * After attempted swap: + + * `current` still points to `v-old`. + * No new directory with the name expected for `v-new` is referenced by `current`. + +3. `Swap_CrashBetweenVerifyAndMv_LeavesSystemConsistent` + + * Simulate crash after creating `current.tmp` but before `mv -T`. + * On “restart”: + + * Cleanup code must detect `current.tmp` and remove it. + * Ensure `current` still points to last good. + +### 5.2 Authority-gated policy changes + +**Component:** `StellaOps.Authority` + any service that exposes `/policies`. + +Policy change flow: + +1. Client sends DSSE-signed `PolicyChangeRequest` to `/authority/verify`. +2. Authority validates signature, subject hash. +3. Service applies change only if Authority approves. + +**Project:** `StellaOps.Authority.Tests` + `StellaOps.Scanner.Tests` (or wherever policies live). + +Key tests: + +1. `PolicyChange_WithValidSignature_Applies` + + * Signed request’s `subject` hash matches computed diff of old->new policy. + * Authority returns `Approved`. + * Policy service updates policy; audit log entry recorded. + +2. `PolicyChange_InvalidSignature_Rejected` + + * Signature verifiable with no trusted key, or corrupted payload. + * Expect: + + * HTTP 403 or 400 from policy endpoint. + * No policy change in DB. + * Audit log entry with reason `SIGNATURE_INVALID`. + +3. `PolicyChange_SubjectHashMismatch_Rejected` + + * Attacker changes policy body but not DSSE subject. + * On verification, recomputed diff doesn’t match subject hash. + * Authority rejects with `SUBJECT_MISMATCH`. + +4. `PolicyChange_ExpiredEnvelope_Rejected` + + * Envelope contains `expiry` in past. + * Authority rejects with `ENVELOPE_EXPIRED`. + +5. `PolicyChange_AuditTrail_Complete` + + * After valid change: + + * Audit log contains: `policyName`, `oldHash`, `newHash`, `signerId`, `envelopeId`, `timestamp`. + +### 5.3 Job routing (if/when you use DB-backed routing tables) + +You discussed a `routing` table: + +```sql +domain text, +instance_id uuid, +last_heartbeat timestamptz, +table_name text +``` + +Key tests (once implemented): + +1. `HeartbeatExpired_DropsRoutingEntry` + + * Insert entry with `last_heartbeat` older than 1 minute. + * Routing GC job should remove it. + * API gateway must not route new jobs to that instance. + +2. `RoundRobinAcrossAliveInstances` + + * Multiple routing rows for same domain with fresh heartbeats. + * Issue M requests via gateway. + * Assert approximately round-robin distribution across `instance_id`. + +3. `NoDurabilityRequired_JobsNotReplayedAfterRestart` + + * Confirm that in-memory or temp tables are used appropriately where you do not want durable queues. + +If you decide to go with “N gateways x M microservices via Docker load balancer only”, then the main tests here move to health-check based routing in the load balancer and become more infra than app tests. + +--- + +## 6) CI wiring summary + +To make this actually enforceable: + +1. **Unit test job** (`test:unit`) + + * Runs `StellaOps.Bundle.Tests`, `StellaOps.SbomGate.Tests`, `StellaOps.Authority.Tests`, `StellaOps.Scanner.Tests`. + +2. **DB recoverability job** (`test:db-recoverability`) + + * Uses Testcontainers to run `StellaOps.DataRecoverability.Tests`. + * Marked as “required” for `main` branch merges. + +3. **Acceptance job** (`test:acceptance-system`) + + * Spins up a minimal stack via Docker Compose. + * Executes `StellaOps.System.Acceptance` tests: + + * Feed outages & fallback. + * Air-gap modes. + * Bundle swap. + * Can be slower; run on main and release branches. + +4. **Nightly chaos job** (`test:nightly-chaos`) + + * Optional: run more expensive tests (simulated DB corruption, analyzer outages, etc.). + +--- + +If you want, next step I can generate skeleton xUnit test classes and a `/testdata` layout you can paste directly into your repo (with TODOs where real fixtures are needed). diff --git a/docs/product-advisories/29-Nov-2025 - CVSS v4.0 Momentum in Vulnerability Management.md b/docs/product-advisories/29-Nov-2025 - CVSS v4.0 Momentum in Vulnerability Management.md new file mode 100644 index 000000000..064902ea1 --- /dev/null +++ b/docs/product-advisories/29-Nov-2025 - CVSS v4.0 Momentum in Vulnerability Management.md @@ -0,0 +1,506 @@ +You might want to know this — the landscape around CVSS v4.0 is rapidly shifting, and the time is now to treat it as a first-class citizen in vulnerability-management workflows. + +![CVSS v4 identity](https://www.first.org/cvss/identity/cvssv4.png) + +![CVSS v4 schema](https://www.first.org/cvss/v4-0/media/699c7730c6e9a411584a129153e334f4.png) + +![Exploit readiness](https://orca.security/wp-content/uploads/2024/01/image-37.png?w=1200) + +--- + +## 🔎 What is CVSS v4.0 — and why it matters now + +* CVSS is the standard framework for scoring software/hardware/firmware vulnerabilities, producing a 0–10 numeric severity score. ([Wikipedia][1]) +* On **November 1 2023**, the maintainers FIRST published version 4.0, replacing older 3.x/2.0 lines. ([FIRST][2]) +* v4.0 expands the standard to four metric groups: **Base, Threat, Environmental,** and a new **Supplemental** — enabling far more granular, context-aware scoring. ([Checkmarx][3]) + +## 🧩 What Changed in v4.0 Compared to 3.x + +* The old “Temporal” metrics are renamed to **Threat** metrics; new Base metric **Attack Requirements (AT)** was added. v4.0 also refines **User Interaction (UI)** into more nuanced values (e.g., Passive vs Active). ([FIRST][4]) + +* The old “Scope” metric was removed, replaced by distinct impact metrics on the *vulnerable system* (VC/VI/VA) vs *subsequent systems* (SC/SI/SA), clarifying real-world risk propagation. ([FIRST][4]) + +* The **Supplemental** metric group adds flags like “Safety,” “Automatable,” “Recovery,” “Value Density,” “Provider Urgency,” etc. — providing extra context that doesn’t alter the numeric score but helps decision-making. ([FIRST][4]) + +* As a result, the scoring nomenclature also changes: instead of a single “CVSS score,” v4.0 supports combinations like **CVSS-B (Base only)**, **CVSS-BT (Base + Threat)**, **CVSS-BE**, or **CVSS-BTE** (Base + Threat + Environmental). ([FIRST][4]) + +## ✅ Industry Adoption Is Underway — Not In The Future, Now + +* The National Vulnerability Database (NVD) — maintained by NIST — officially supports CVSS v4.0 as of June 2024. ([NVD][5]) + +* The major code-host & vulnerability platform GitHub Security Advisories began exposing CVSS v4.0 fields in September 2024. ([The GitHub Blog][6]) + +* On the ecosystem and vendor side: Microsoft Defender Vulnerability Management updated to prefer CVSS v4 in March 2025. ([TECHCOMMUNITY.MICROSOFT.COM][7]) + +* According to Snyk Open Source, all new advisories now come with both CVSS v4.0 and v3.1 assessments — with v4.0 becoming the default for evaluating severity. ([Snyk Updates][8]) + +* Several vendor-facing security / vulnerability-management products have signaled or started integrating v4.0 (though rollout is still ongoing) — consistent with the broader industry shift. ([Rapid7 Discuss][9]) + +## 🎯 What This Means for Your Approach (Given What You’re Building) + +Because you’re architecting a supply-chain / container / SCA-aware scanning stack (as with your StellaOps plans), this shift matters: + +1. **Granular scoring** — v4.0’s more detailed metrics help differentiate between a “theoretical high-impact but low-likelihood” flaw versus a “realistic immediate threat.” +2. **Context-aware risk** — using Threat + Environmental + Supplemental metrics lets you layer real-world usage context (e.g. container deployment environment, exposure profile, exploit maturity, safety implications). +3. **Auditability & traceability** — storing full vector strings, metric breakdowns (Base/Threat/Environmental/Supplemental), computed scores (CVSS-B, BT, BE, BTE), timestamp, scorer/source URL — plus legacy v3.1 vector — gives you a full provenance trail. That aligns with your ambition to build a “proof-of-integrity graph” and supply-chain-compliance ledger. + +## 🧠 Caveats & What To Watch Out For + +* Although v4.0 is supported by core infrastructures (NVD, GitHub, Microsoft, Snyk, etc.), **not all existing CVEs have v4.0 assessments yet**. Many older entries still carry only v3.x scores. ([NVD][10]) +* v4.0’s increased granularity brings **increased complexity** — more metric fields to collect, store, and evaluate. Without consistent population of Threat, Environmental or Supplemental fields, many scores could default back to “base only,” limiting the advantage over v3.1. ([Checkmarx][3]) +* A purely numeric score remains a **partial view of risk** — v4.0 improves it, but still does *not* compute exploitation likelihood. Combining CVSS with external data (e.g. exploit intelligence, usage context, threat modeling) remains critical. ([CrowdStrike][11]) + +--- + +Given your goals — supply-chain compliance, auditability, multi-tool integration — adopting CVSS v4.0 now (or planning for it) is absolutely the right move. + +If you like, I can check **as of late 2025** which major container/SCA tools (like grype, Trivy, Aqua, GitLab, etc.) already support v4.0 — this may help you decide how quickly to bake v4 support into StellaOps. + +[1]: https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System?utm_source=chatgpt.com "Common Vulnerability Scoring System" +[2]: https://www.first.org/cvss/specification-document?utm_source=chatgpt.com "CVSS v4.0 Specification Document" +[3]: https://checkmarx.com/blog/cvss4-what-you-need-to-know-about-the-latest-version/?utm_source=chatgpt.com "CVSS v4.0: What You Need to Know about the Latest Version" +[4]: https://www.first.org/cvss/v4-0/?utm_source=chatgpt.com "Common Vulnerability Scoring System Version 4.0" +[5]: https://nvd.nist.gov/general/news/cvss-v4-0-official-support?utm_source=chatgpt.com "CVSS v4.0 Official Support - NVD" +[6]: https://github.blog/changelog/2024-09-13-github-security-advisories-support-cvss-4-0/?utm_source=chatgpt.com "GitHub security advisories support CVSS 4.0" +[7]: https://techcommunity.microsoft.com/blog/vulnerability-management/supporting-cvss-v4-score-for-cve-for-enhanced-vulnerability-assessment/4391439?utm_source=chatgpt.com "Supporting CVSS V4 score for CVE for Enhanced ..." +[8]: https://updates.snyk.io/improved-security-prioritization-with-cvss-version-4-0-295136/?utm_source=chatgpt.com "Improved security prioritization with CVSS version 4.0" +[9]: https://discuss.rapid7.com/t/cvss-4-0-when/29354?utm_source=chatgpt.com "CVSS 4.0 when? - InsightVM" +[10]: https://nvd.nist.gov/vuln/detail/CVE-2025-2024?utm_source=chatgpt.com "CVE-2025-2024 Detail - NVD" +[11]: https://www.crowdstrike.com/en-us/cybersecurity-101/exposure-management/common-vulnerability-scoring-system-cvss/?utm_source=chatgpt.com "Common Vulnerability Scoring System (CVSS)" +Good request. I checked — support for CVSS v4.0 is being gradually adopted in major vulnerability-tracking and container/SCA tools, but **it is not yet ubiquitous**. Here’s what’s clear today (late 2025), plus what remains uncertain — from a neutral fact-based perspective. + +--- + +## ✅ What we know: CVSS 4.0 is gaining traction + +* GitHub Advisory Database — widely used for open source vulnerability advisories — **now supports CVSS v4.0** (in addition to older v3.1). ([GitHub Docs][1]) +* The maintainers of the standard, FIRST, define CVSS 4.0 as full suite of metric groups (Base, Threat, Environmental, Supplemental), giving more precise, environment-aware severity scoring. ([first.org][2]) +* Some high-profile CVEs (e.g. Trivy-related CVE-2024-35192) already contain CVSS v4.0 vector strings in their official NVD entries, indicating that the community is reporting vulnerabilities under the new standard. ([nvd.nist.gov][3]) + +--- + +## 🔎 What modern container/SCA tools do — mixed readiness + +* Trivy remains one of the most popular open-source scanners for containers, VMs, filesystems, language dependencies, and Kubernetes environments. ([GitHub][4]) + +* Documentation and tool descriptions for Trivy (and its ecosystem integrations) emphasize detection of CVEs, generation of SBOMs, license/compliance scanning, IaC misconfigurations, secrets, etc. ([Chainguard Academy][5]) + +* However — I did **not** find explicit public documentation confirming that Trivy (or other major SCA/container scanners) **fully parse, store, and expose CVSS v4.0 metrics** (Base, Threat, Environmental, Supplemental) across all output formats. + + * Many vulnerability-scanning tools still default to older scoring (e.g. CVSS v3.x) because much of the existing CVE ecosystem remains populated with v3.x data. + * Some vulnerabilities (even recent ones) may carry v4.0 vectors, but if the scanning tool or its database backend does not yet support v4.0, the output may degrade to “severity level” (Low/Medium/High/Critical) or rely on legacy score — losing some of the finer granularity. + +* A recent academic evaluation of the “VEX tool space” for container-scanning highlighted **low consistency across different scanners**, suggesting divergent capabilities and output semantics. ([arxiv.org][6]) + +--- + +## ⚠ What remains uncertain / risk factors + +* Lack of uniform adoption: Many older CVEs still carry only CVSS v2 or v3.x scores; full re-scoring with v4.0 is non-trivial and may lag. Without upstream v4 scoring, scanner support is moot. +* Tools may partially support v4.0: Even if a scanner identifies a v4-vector, it may not support all metric groups (e.g. Threat, Environmental, Supplemental) — or may ignore them in summary output. +* Output format issues: For compliance, audit, or traceability (as you plan for with your “proof-of-integrity graph”), you need vector strings, not just severity labels. If scanners only emit severity labels or old-school base scores, you lose auditability. +* Inconsistency among scanners: As shown in recent comparative research, different tools may disagree on which vulnerabilities they detect — and by extension, which CVEs get v4.0 scoring in their data. ([arxiv.org][6]) + +--- + +## 🎯 What this means for your build (e.g. Stella Ops) + +* You should **design Stella Ops to natively support CVSS v4.0 vectors** — store them as structured data (vector string + separate metric fields) rather than just severity label or numeric base score. That preserves maximum fidelity and auditability. +* Continue to ingest from sources that publish v4.0 data (e.g. GitHub Advisory Database, NVD entries already tagged with v4 vector). +* For scanners (e.g. Trivy) that lack confirmed v4 output support — treat them as legacy feed, and supplement with heuristics or external metadata fetch to reconstruct full context where possible (or fallback to v3.x with clear marking). +* Account for inconsistency among scanners: treat scan output as advisory — use cross-tool correlation, SBOM provenance tracking, and your planned “proof-of-integrity graph” to triangulate real risk rather than trust a single tool. + +--- + +## ✅ Final Assessment — “Yes, but partial” + +CVSS 4.0 is **supported in key vulnerability/advisory databases** and **appearing in real CVE entries**, but **full support across container/SCA scanning tools remains uneven**. For a forward-looking, compliance- and audit-oriented platform like Stella Ops, it is rational and recommended to **adopt v4.0 as primary internal standard**, while building fallback paths for legacy data. + +If you like — I can run a small **2025-era snapshot** of **7–10 top container / SCA tools** (open source + commercial) and tabulate which of them already publish full CVSS v4.0 output (vectors + all metric groups). That gives you a practical compatibility map for Stella Ops architecture. + +[1]: https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database?utm_source=chatgpt.com "About the GitHub Advisory database" +[2]: https://www.first.org/cvss/v4-0/cvss-v40-faq.pdf?utm_source=chatgpt.com "CVSS v4.0 Frequently Asked Questions 2025- ..." +[3]: https://nvd.nist.gov/vuln/detail/CVE-2024-35192?utm_source=chatgpt.com "CVE-2024-35192 Detail - NVD" +[4]: https://github.com/aquasecurity/trivy?utm_source=chatgpt.com "aquasecurity/trivy: Find vulnerabilities, misconfigurations, ..." +[5]: https://edu.chainguard.dev/chainguard/chainguard-images/staying-secure/working-with-scanners/trivy-tutorial/?utm_source=chatgpt.com "Using Trivy to Scan Software Artifacts" +[6]: https://arxiv.org/abs/2503.14388?utm_source=chatgpt.com "Vexed by VEX tools: Consistency evaluation of container vulnerability scanners" +Below is something you can drop into an internal design doc and hand straight to engineering. + +--- + +## 1. Objectives (what “done” looks like) + +Developers should aim for: + +1. **First‑class CVSS v4.0 support** + + * Parse and store full v4.0 vectors (`CVSS:4.0/...`) including all Base, Threat, Environmental, and Supplemental metrics. ([first.org][1]) + * Compute correct CVSS‑B / CVSS‑BT / CVSS‑BE / CVSS‑BTE scores using the official equations. ([first.org][1]) + +2. **Version‑aware, lossless storage** + + * Preserve original vector strings and raw metrics for both v3.x and v4.0. + * Do not down‑convert v4.0 to v3.x or blend them into a single number. + +3. **Clear separation of concerns** + + * CVSS = standardized “severity” input, not your internal “risk score.” ([first.org][1]) + * Threat / Environmental / Supplemental metrics feed your risk model, but are stored distinctly. + +--- + +## 2. Data model & schema + +### 2.1 Core CVSS entity + +For each vulnerability finding (CVE or tool‑specific ID), you should support multiple CVSS assessments over time and from different sources. + +**Table: `cvss_assessments` (or equivalent)** + +Required columns: + +* `id` (PK) +* `vuln_id` (FK to CVE/finding table) +* `version` (`'2.0' | '3.0' | '3.1' | '4.0'`) +* `vector_string` (full textual vector, e.g. `CVSS:4.0/AV:N/AC:L/...`) +* `score` (numeric, 0.0–10.0) +* `score_type` (`'CVSS-B' | 'CVSS-BT' | 'CVSS-BE' | 'CVSS-BTE'`) ([first.org][1]) +* `severity_band` (`'None' | 'Low' | 'Medium' | 'High' | 'Critical'`) per official v4 qualitative scale. ([first.org][1]) +* `source` (`'NVD' | 'GitHub' | 'Vendor' | 'Internal' | 'Scanner:' | ...`) +* `assessed_at` (timestamp) +* `assessed_by` (user/system id) + +### 2.2 Structured metrics (JSON or separate tables) + +Store metrics machine‑readably, not just in the string: + +```jsonc +{ + "base": { + "AV": "N", // Attack Vector + "AC": "L", // Attack Complexity + "AT": "N", // Attack Requirements + "PR": "N", // Privileges Required + "UI": "N", // User Interaction: N, P, A in v4.0 + "VC": "H", + "VI": "H", + "VA": "H", + "SC": "H", + "SI": "H", + "SA": "H" + }, + "threat": { + "E": "A" // Exploit Maturity + }, + "environmental": { + "CR": "H", // Confidentiality Requirement + "IR": "H", + "AR": "H", + // plus modified versions of base metrics, per spec + "MAV": "N", + "MAC": "L", + "MPR": "N", + "MUI": "N", + "MVC": "H", + "MVI": "H", + "MVA": "H", + "MSC": "H", + "MSI": "H", + "MSA": "H" + }, + "supplemental": { + "S": "S", // Safety + "AU": "Y", // Automatable (v4.0 uses AU in the spec) :contentReference[oaicite:5]{index=5} + "U": "H", // Provider Urgency + "R": "H", // Recovery + "V": "H", // Value Density + "RE": "H" // Vulnerability Response Effort + } +} +``` + +Implementation guidance: + +* Use a JSON column or dedicated tables (`cvss_base_metrics`, `cvss_threat_metrics`, etc.) depending on how heavily you will query by metric. +* Always persist both `vector_string` and parsed metrics; treat parsing as a reversible transformation. + +--- + +## 3. Parsing & ingestion + +### 3.1 Recognizing and parsing vectors + +Develop a parser library (or adopt an existing well‑maintained one) with these properties: + +* Recognizes prefixes: `CVSS:2.0`, `CVSS:3.0`, `CVSS:3.1`, `CVSS:4.0`. ([Wikipedia][2]) +* Validates all metric abbreviations and values for the given version. +* Returns: + + * `version` + * `metrics` object (structured as above) + * `score` (recalculated from metrics, not blindly trusted) + * `score_type` (B / BT / BE / BTE inferred from which metric groups are defined). ([first.org][1]) + +Error handling: + +* If the vector is syntactically invalid: + + * Store it as `vector_string_invalid`. + * Mark assessment as `status = 'invalid'`. + * Do not drop the vulnerability; log and alert. +* If metrics are missing for Threat / Environmental / Supplemental: + + * Treat them as “Not Defined” per spec, defaulting them in calculations. ([first.org][1]) + +### 3.2 Integrating with external feeds + +For each source (NVD, GitHub Advisory Database, vendor advisories, scanner outputs): + +* Normalize: + + * If numeric score + vector provided: always trust the vector, not the numeric; recompute score internally and compare for sanity. + * If only numeric score provided (no vector): store, but mark `vector_string = null` and `is_partial = true`. +* Preserve `source` and `source_reference` (e.g., NVD URL, GHSA ID) for audit. + +--- + +## 4. Scoring engine + +### 4.1 Core functions + +Implement pure, deterministic functions: + +* `Cvss4Result computeCvss4Score(Cvss4Metrics metrics)` +* `Cvss3Result computeCvss3Score(Cvss3Metrics metrics)` + +Where `Cvss4Result` includes: + +* `score` (0.0–10.0, properly rounded per spec; one decimal place) ([first.org][1]) +* `score_type` (`CVSS-B` / `CVSS-BT` / `CVSS-BE` / `CVSS-BTE`) +* `severity_band` (`None` / `Low` / `Medium` / `High` / `Critical`) + +Implementation notes: + +* Use the equations from section 8 of the CVSS v4.0 spec; do not invent your own approximations. ([first.org][1]) +* Ensure rounding and severity bands exactly match official calculators (run golden tests). + +### 4.2 Nomenclature & flags + +* Always tag scores with the nomenclature: `CVSS-B`, `CVSS-BT`, `CVSS-BE`, or `CVSS-BTE`. ([first.org][1]) +* Default behavior: + + * If Threat/Environmental metrics are not explicitly provided, treat them as “Not Defined” (defaults), and still label the score `CVSS-B`. + * Only use `CVSS-BT` / `CVSS-BE` / `CVSS-BTE` when non‑default values are present. + +### 4.3 Strict separation from “risk” + +* Do not overload CVSS as a risk score. The spec is explicit that base scores measure severity, not risk. ([first.org][1]) +* Implement a separate risk model (e.g., `risk_score` 0–100) that uses: + + * CVSS (v3/v4) as one input + * Asset criticality + * Compensating controls + * Business context + * Threat intel signals + +--- + +## 5. Threat & Environmental automation + +You want developers to wire CVSS v4.0 to real‑world intel and asset data. + +### 5.1 Threat metrics (Exploit Maturity, E) + +Threat metrics in v4.0 are largely about Exploit Maturity (E). ([first.org][1]) + +Implementation guidelines: + +* Define a mapping from your threat intel signals to E values: + + * `E:X` – Not Defined (no data) + * `E:U` – Unreported + * `E:P` – Proof‑of‑concept only + * `E:F` – Functional + * `E:A` – Active exploitation + +* Data sources: + + * Threat intel feeds (CISA KEV, vendor threat bulletins, commercial feeds) + * Public PoC trackers (exploit DB, GitHub PoC repositories) + +* Automation: + + * Nightly job updates Threat metrics per vuln based on latest intel. + * Recompute `CVSS-BT` / `CVSS-BTE` and store new assessments with `assessed_at` timestamp. + +### 5.2 Environmental metrics + +Environmental metrics adjust severity to a specific environment. ([first.org][1]) + +Implementation guidelines: + +* Link `vuln_id` to asset(s) via your CMDB / asset inventory. +* For each asset, maintain: + + * CIA requirements: `CR`, `IR`, `AR` (Low / Medium / High) + * Key controls: e.g., network segmentation, WAF, EDR, backups, etc. +* Map asset metadata to Environmental metrics: + + * For high‑critical systems, set `CR/IR/AR = H`. + * For heavily segmented systems, adjust Modified Attack Vector or Impact metrics accordingly. +* Provide: + + * A default environment profile (for generic scoring). + * Per‑asset or per‑asset‑group overrides. + +--- + +## 6. Supplemental metrics handling + +Supplemental metrics do not affect the CVSS score formula but provide critical context: Safety (S), Automatable (AU), Provider Urgency (U), Recovery (R), Value Density (V), Response Effort (RE). ([first.org][3]) + +Guidelines: + +* Always parse and store them for v4.0 vectors. +* Provide an internal risk model that can optionally: + + * Up‑weight vulns with: + + * `S = Safety-critical` + * `AU = True` (fully automatable exploitation) + * `V = High` (many assets affected) + * Down‑weight vulns with very high Response Effort (RE) when there is low business impact. +* Make Supplemental metrics explicitly visible in the UI as “context flags,” not hidden inside a numeric score. + +--- + +## 7. Backward compatibility with CVSS v3.x + +You will be living in a dual world for a while. + +Guidelines: + +1. **Store v3.x assessments as‑is** + + * `version = '3.1'` or `'3.0'` + * `vector_string`, `score`, `severity_band` (using v3 scale). +2. **Never treat v3.x and v4.0 scores as directly comparable** + + * 7.5 in v3.x ≠ 7.5 in v4.0. + * For dashboards and filters, compare inside a version, or map both to coarse severity bands. +3. **Do not auto‑convert v3 vectors to v4 vectors** + + * Mapping is not lossless; misleading conversions are worse than clearly labeled legacy scores. +4. **UI and API** + + * Always show `version` and `score_type` next to the numeric score. + * For API clients, include structured metrics so downstream systems can migrate at their own pace. + +--- + +## 8. UI/UX guidelines + +Give your designers and frontend devs explicit behaviors. + +* On vulnerability detail pages: + + * Show: + + * `CVSS v4.0 (CVSS-BTE) 9.1 – Critical` + * Version and score type are always visible. + * Provide a toggle or tab: + + * “View raw vector,” expanding to show all metrics grouped: Base / Threat / Environmental / Supplemental. +* For lists and dashboards: + + * Use severity bands (None / Low / Medium / High / Critical) as primary visual grouping. + * For mixed environments: + + * Show two columns if available: `CVSS v4.0` and `CVSS v3.1`. +* For analysts: + + * Provide a built‑in calculator UI that lets them adjust Threat / Environmental metrics and immediately see updated `CVSS-BT/BE/BTE`. + +--- + +## 9. Testing & validation + +Define a clean test strategy before implementation. + +1. **Golden test cases** + + * Use official FIRST examples and calculators (v4.0 spec & user guide) as ground truth. ([first.org][4]) + * Build a small public corpus: + + * 20–50 CVEs with published v4.0 vectors and scores (from NVD / vendors) and confirm byte‑for‑byte matches. + +2. **Round‑trip tests** + + * `vector_string -> parse -> metrics -> compute -> vector_string` + + * Ensure you can reconstruct the same canonical vector. + +3. **Regression tests** + + * Add tests for: + + * Missing Threat / Environmental metrics (defaults). + * Invalid vectors (fail gracefully, log, do not crash pipelines). + * Mixed v3/v4 data in the same views. + +4. **Performance** + + * CVSS computations are cheap, but in bulk (e.g., 1M+ findings) avoid per‑request recalculation; cache computed results per `(vuln_id, metrics_hash)`. + +--- + +## 10. Rollout / migration plan (high level) + +What you can ask engineering to follow: + +1. **Phase 1 – Data model & parsing** + + * Add schema changes for `cvss_assessments`. + * Implement v4.0 parser and scorer, with full unit tests. +2. **Phase 2 – Ingestion** + + * Update NVD / GitHub / vendor importers to capture v4 vectors when present. + * Backfill existing data with `version` = v3.x or v2.0 as appropriate. +3. **Phase 3 – UI & API** + + * Expose versioned CVSS data and vector strings. + * Mark all legacy places that assumed “CVSS = single number” and refactor to be version‑aware. +4. **Phase 4 – Threat & Environmental automation** + + * Wire in threat intel feeds and asset metadata. + * Start generating `CVSS-BT` / `CVSS-BTE` scores for key asset groups. +5. **Phase 5 – Risk model integration** + + * Use CVSS (v3/v4), plus Supplemental metrics, plus your own business logic to output a platform‑specific risk score. + +--- + +## 11. Developer checklist (condensed) + +You can literally put this as a checklist in Jira/Notion: + +* [ ] Schema updated to store: + + * [ ] `version`, `vector_string`, `score`, `score_type`, `severity_band`, `source`, timestamps. + * [ ] Structured Base / Threat / Environmental / Supplemental metrics. +* [ ] Parser for `CVSS:4.0/...` with validation and graceful error handling. +* [ ] Scoring engine for v4.0 using official equations; golden tests against FIRST calculator. +* [ ] Support for v3.x preserved; no cross‑version conversion. +* [ ] UI shows version and score type; exposes vector and metrics. +* [ ] Threat intel mapping to Exploit Maturity (E) implemented. +* [ ] Environmental mapping from asset metadata implemented (even if initially basic). +* [ ] Supplemental metrics parsed, stored, and surfaced. +* [ ] Risk scoring logic separated from raw CVSS. +* [ ] Regression tests + performance tests in CI. + +If you tell me what your current schema and pipeline look like (tables, services, scanners in use), I can produce a concrete migration diff (DDL and service‑level changes) tailored to your stack. + +[1]: https://www.first.org/cvss/v4-0/cvss-v40-specification.pdf "CVSS v4.0 Specification - 2024-06-18" +[2]: https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System?utm_source=chatgpt.com "Common Vulnerability Scoring System" +[3]: https://www.first.org/cvss/v4-0/?utm_source=chatgpt.com "Common Vulnerability Scoring System Version 4.0" +[4]: https://www.first.org/cvss/v4-0/user-guide?utm_source=chatgpt.com "CVSS v4.0 User Guide" diff --git a/docs/product-advisories/29-Nov-2025 - SBOM to VEX Proof Pipeline Blueprint.md b/docs/product-advisories/29-Nov-2025 - SBOM to VEX Proof Pipeline Blueprint.md new file mode 100644 index 000000000..caf7cee71 --- /dev/null +++ b/docs/product-advisories/29-Nov-2025 - SBOM to VEX Proof Pipeline Blueprint.md @@ -0,0 +1,108 @@ +Here’s a compact, diagram-first blueprint that shows how to turn a CycloneDX SBOM into signed, replay-safe proofs across DSSE/in-toto, Rekor v2 (tile-backed) receipts, and VEX—plus how to run this with the public instance or fully offline. + +--- + +## 1) Mental model (one line per hop) + +``` +[SBOM: CycloneDX JSON] + └─(wrap as DSSE payload; predicate = CycloneDX) + └─(optional: in-toto statement for context) + └─(sign → cosign/fulcio or your own CA) + └─(log entry → Rekor v2 / tiles) + └─(checkpoint + inclusion proof + receipt) + └─(VEX attestation references SBOM/log) + └─(Authority anchors/keys + policies) +``` + +* **CycloneDX SBOM** is your canonical inventory. ([cyclonedx.org][1]) +* **DSSE** provides a minimal, standard signing envelope; in-toto statements add supply-chain context. ([JFrog][2]) +* **Rekor v2** stores a hash of your attestation in a **tile-backed transparency log** and returns **checkpoint + inclusion proof** (small, verifiable). ([Sigstore Blog][3]) +* **VEX** conveys exploitability (e.g., “not affected”) and should reference the SBOM and, ideally, the Rekor receipt. ([cyclonedx.org][4]) + +--- + +## 2) Exact capture points (what to store) + +* **SBOM artifact**: `sbom.cdx.json` (canonicalized bytes + SHA256). ([cyclonedx.org][1]) +* **DSSE envelope** over SBOM (or in-toto statement whose predicate is CycloneDX): keep the full JSON + signature. ([JFrog][2]) +* **Rekor v2 receipt**: + + * **Checkpoint** (signed tree head) + * **Inclusion proof** (audit path) + * **Entry leaf hash / UUID** + Persist these with your build to enable offline verification. ([Sigstore Blog][3]) +* **VEX attestation** (CycloneDX VEX): include references (by digest/URI) to the **SBOM** and the **Rekor entry/receipt** used for the SBOM attestation. ([cyclonedx.org][4]) +* **Authority anchors**: publish the verifying keys (or TUF-root if using Sigstore public good), plus your policy describing accepted issuers and algorithms. ([Sigstore][5]) + +--- + +## 3) Field-level linkage (IDs you’ll wire together) + +* **`subject.digest`** in DSSE/in-toto ↔ **SBOM SHA256**. ([OpenSSF][6]) +* **Rekor entry** ↔ **DSSE envelope digest** (leaf/UUID recorded in receipt). ([GitHub][7]) +* **VEX `affects` / `analysis`** entries ↔ **components in SBOM** (use purl/coordinates) and include **`evidence`/`justification`** with **Rekor proof URI**. ([cyclonedx.org][4]) + +--- + +## 4) Verification flow (online or air-gapped) + +**Online (public good):** + +1. Verify DSSE signature against accepted keys/issuers. ([Sigstore][5]) +2. Verify Rekor **checkpoint signature** and **inclusion proof** for the logged DSSE digest. ([Go Packages][8]) +3. Validate VEX against the same SBOM digest (and optionally that its own attestation is also logged). ([cyclonedx.org][4]) + +**Air-gapped / sovereign:** + +* Mirror/export **Rekor tiles + checkpoints** on a courier medium; keep receipts small by shipping only tiles covering the ranges you need. +* Run **self-hosted Rekor v2** or a **local tile cache**; verifiers check **checkpoint signatures** and **consistency proofs** exactly the same way. ([Sigstore Blog][3]) + +--- + +## 5) Public instance vs self-hosted (decision notes) + +* **Public**: zero-ops, audited community infra; you still archive receipts with your releases. ([Sigstore][5]) +* **Self-hosted Rekor v2 (tiles)**: cheaper/simpler than v1, tile export makes **offline kits** practical; publish your **root keys** as organization anchors. ([Sigstore Blog][3]) + +--- + +## 6) Minimal CLI recipe (illustrative) + +* Generate SBOM → wrap → attest → log → emit receipt: + + * Create CycloneDX JSON; compute digest. ([cyclonedx.org][1]) + * Create **DSSE** or **in-toto** attestation for the SBOM; sign (cosign or your CA). ([JFrog][2]) + * Submit to **Rekor v2**; store **checkpoint + inclusion proof + UUID** with the build. ([Sigstore Blog][3]) + * Emit **VEX** referencing the SBOM digest **and** the Rekor entry (URI/UUID). ([cyclonedx.org][4]) + +--- + +## 7) Developer guardrails (to keep proofs replay-safe) + +* **Canonical bytes only** (stable JSON ordering/whitespace) before hashing/signing. ([JFrog][2]) +* **Pin algorithms** (e.g., SHA-256 + key types) in policy; reject drift. ([Sigstore][5]) +* **Always persist**: SBOM, DSSE envelope, Rekor receipt, VEX, and your **accepted-keys manifest** with version. ([Sigstore Blog][3]) +* **Test offline**: verification must pass using only tiles + receipts you ship. ([Go Packages][9]) + +--- + +## 8) Optional niceties + +* Gate deployments on “image must have **signed SBOM** (attestation)”. Sigstore Policy Controller example exists. ([Stackable Documentation][10]) +* Track CVE status via **CycloneDX VEX** in your UI (“affected/not affected” with evidence links to Rekor). ([cyclonedx.org][4]) + +--- + +If you want, I can turn this into a **Stella Ops** diagram + drop-in `docs/blueprints/sbom-to-vex-rekor.md` with exact JSON stubs for: DSSE envelope, in-toto statement, Rekor receipt example, and a CycloneDX VEX snippet wired to the receipt. + +[1]: https://cyclonedx.org/specification/overview/?utm_source=chatgpt.com "Specification Overview" +[2]: https://jfrog.com/blog/introducing-dsse-attestation-online-decoder/?utm_source=chatgpt.com "Introducing the DSSE Attestation Online Decoder" +[3]: https://blog.sigstore.dev/rekor-v2-ga/?utm_source=chatgpt.com "Rekor v2 GA - Cheaper to run, simpler to maintain" +[4]: https://cyclonedx.org/capabilities/vex/?utm_source=chatgpt.com "Vulnerability Exploitability eXchange (VEX)" +[5]: https://docs.sigstore.dev/logging/overview/?utm_source=chatgpt.com "Rekor" +[6]: https://openssf.org/blog/2024/06/26/a-deep-dive-into-sbomit-and-attestations/?utm_source=chatgpt.com "A Deep Dive into SBOMit and Attestations" +[7]: https://github.com/sigstore/rekor?utm_source=chatgpt.com "sigstore/rekor: Software Supply Chain Transparency Log" +[8]: https://pkg.go.dev/github.com/sigstore/rekor-tiles/v2/pkg/verify?utm_source=chatgpt.com "verify package - github.com/sigstore/rekor-tiles/v2/pkg/verify" +[9]: https://pkg.go.dev/github.com/sigstore/rekor-tiles?utm_source=chatgpt.com "rekor-tiles module - github.com/sigstore/rekor-tiles" +[10]: https://docs.stackable.tech/home/stable/guides/viewing-and-verifying-sboms/?utm_source=chatgpt.com "Viewing and verifying SBOMs of the Stackable Data Platform" diff --git a/docs/product-advisories/29-Nov-2025 - SCA Failure Catalogue for StellaOps Tests.md b/docs/product-advisories/29-Nov-2025 - SCA Failure Catalogue for StellaOps Tests.md new file mode 100644 index 000000000..9beaffa35 --- /dev/null +++ b/docs/product-advisories/29-Nov-2025 - SCA Failure Catalogue for StellaOps Tests.md @@ -0,0 +1,354 @@ +I put together a short “failure-catalogue” of five high-impact real issues or regressions between 2023–2025 across Trivy / Syft / Grype, Snyk and JFrog — especially focused on reachability, SBOM provenance/gaps, offline/updater problems, and DB-churn noise. I thought you might want concrete test vectors for your StellaOps backlog. + +![Image](https://d3g9o9u8re44ak.cloudfront.net/logo/e210b50b-8204-4095-b308-67e4b6097cc2/331f3e36-ea61-4b6a-a40e-1a29e0b70304.png) + +![Image](https://repository-images.githubusercontent.com/267054247/c41d4c8f-cd32-4bdd-9f2a-89a82992a359) + +![Image](https://syft.com/assets/Meta-images/syft.png) + +![Image](https://upload.wikimedia.org/wikipedia/commons/9/97/Syft_Logo.png) + +![Image](https://mma.prnewswire.com/media/807259/SYFT_Logo.jpg) + +--- + +## ⚠️ Notable failures & walk-backs + +### **1. Trivy fails to detect critical CVEs in JAR files — even when present in DB (2024)** + +* In a discussion titled “Trivy not detecting CVE 2022-37734, 2023-28867, 2024-22233 in jar file” (Feb 6, 2024), a user reports that while scanning a container image that includes `graphql-java-codegen-5.9.0.jar` or `spring-core-6.0.14.jar`, Trivy — even with the latest DB — ignores these CVEs. The same CVEs are otherwise picked up by Maven-based scanning. ([GitHub][1]) +* This indicates a fundamental mismatch between what the vulnerability DB says and what Trivy actually reports: a “reachability/noise gap” or “false negative in jar unpacking”. +* **Test vector idea for StellaOps**: build a minimal container with `graphql-java-codegen-5.9.0.jar` (or `spring-core-6.0.14.jar`), run Trivy with latest DB, check whether it flags CVE-2022-37734 / 2023-28867 / 2024-22233. If not — this is a confirmed “blind spot”. + +### **2. More general “Jar / Java-libs detection” regressions in Trivy (2023)** + +* Issue #4046 on Trivy’s repo (Apr 12, 2023) states that certain Java-based container images show no CVEs detected at all, even though other tools (like Grype) do pick them up. ([GitHub][2]) +* Similarly, older but related issues such as #1869 (Mar 2022) and #2089 (May 2022) describe cases where Trivy lists packages inside JARs, but fails to report any vulnerabilities tied to those packages — a direct SBOM-to-vuln mapping failure. ([GitHub][3]) +* **Implication**: SBOM-based scanning with Trivy underrepresents Java ecosystem risk (especially for fat-jar or dependency-bundle patterns). That undermines “SBOM completeness” assumptions. + +### **3. Discrepancy between SBOM-generation (Syft) and vulnerability scanning (Grype/Trivy) — huge package-count gap (2024)** + +* In a discussion from March 2024, a user reports that a fresh build of a patched distro (Rocky Linux 9.3 with GUI) scanned with Syft reports ≈ 6000 packages, but the same system scanned with Trivy reports only ≈ 1300 packages. ([GitHub][4]) +* When feeding the Syft SBOM to Grype, the user sees “over 300 vulnerabilities”; but when Grype processes the Trivy-generated SBOM, it reports less than 10 vulnerabilities — a dramatic underreporting. ([GitHub][4]) +* **Test vector idea**: for a known-updated Linux distro (e.g. Rocky 9.3), generate SBOMs via Syft and Trivy, then feed both into Grype — compare package counts and vuln counts side-by-side. This tests real-world “SBOM provenance gaps + under-scanning”. + +### **4. Regression in Grype (post-v0.87.0) leads to false negatives (Apr 2025)** + +* On Apr 30, 2025: issue #2628 reports that a minimal SBOM containing vulnerable artifacts is correctly flagged by Grype v0.87.0 (3 critical + 2 high), but with any later version (incl. latest) it reports no vulnerabilities at all. ([GitHub][5]) +* **Implication**: DB-churn + schema changes or scanning logic shifts can regress coverage dramatically, even for simple known cases. +* **Test vector idea**: replicate the minimal SBOM from the issue with vulnerable artifacts, run Grype at v0.87.0 and then at latest; confirm gap. Great candidate for regression monitoring in StellaOps. + +### **5. Broader SBOM integrity & trust problems — even if scanning works (2024)** + +* Recent academic work (Dec 2024) titled “The Lack of Integrity Protection in SBOM Solutions” shows that many SBOM tools (generation and consumption) lack integrity/integrity-protection: dependencies can be tampered or mislabeled, which can lead to “incorrect SBOM data → vulnerabilities overlooked.” ([arXiv][6]) +* Meanwhile, a 2025 empirical study on real-world open-source repos found that downstream scanners using SBOMs produce a ~97.5% false-positive rate (largely due to unreachable code), recommending function-call analysis to prune noise. ([arXiv][7]) +* **Implication for StellaOps**: even a “perfect SBOM → perfect scan” pipeline may be fundamentally flawed unless you add provenance validation + reachability analysis. Reliance on SBOM + vuln-scanner alone gives a very noisy, untrustworthy result. + +--- + +## 🎯 Why this matters for StellaOps & your architecture + +* These are concrete, high-impact failures in widely used SCA tools (Trivy, Syft, Grype, Snyk ecosystem) — they demonstrate **false negatives**, **SBOM gaps**, **DB/logic regressions**, and **SBOM-integrity weaknesses**. +* Given StellaOps’ goals (deterministic replayable scans, cryptographic provenance, “Trust Algebra”, remediation prioritization), you should treat each issue above as a **test/sanity vector** or **alarm scenario** to include in your backlog. +* In particular: the jar-file CVE misses, SBOM-generation vs scan divergence, and post-DB-schema regressions represent the kind of toolchain brittleness that StellaOps is designed to mitigate (or detect). + +--- + +If you like, I can expand this into a **10-item catalogue** covering also misconfig scanning, SBOM forgery, offline-updater stale DB incidents and compare across all major SCA tools (Trivy / Grype / Snyk / JFrog / CycloneDX, etc.) — which would be a richer resource for StellaOps testing. + +[1]: https://github.com/aquasecurity/trivy/discussions/6074?utm_source=chatgpt.com "Trivy not detecting CVE 2022-37734, 2023-28867, 2024- ..." +[2]: https://github.com/aquasecurity/trivy/issues/4046?utm_source=chatgpt.com "trivy did not detect java libs · Issue #4046 · aquasecurity/trivy" +[3]: https://github.com/aquasecurity/trivy/issues/1869?utm_source=chatgpt.com "Missing vulnerabilities for jar files #1869 - aquasecurity/trivy" +[4]: https://github.com/aquasecurity/trivy/discussions/6325?utm_source=chatgpt.com "and of those detected Trivy sometimes does not report a ..." +[5]: https://github.com/anchore/grype/issues/2628?utm_source=chatgpt.com "Grype false negatives in versions v0.88.0 and later leading ..." +[6]: https://arxiv.org/abs/2412.05138?utm_source=chatgpt.com "Supply Chain Insecurity: The Lack of Integrity Protection in SBOM Solutions" +[7]: https://arxiv.org/abs/2511.20313?utm_source=chatgpt.com "A Reality Check on SBOM-based Vulnerability Management: An Empirical Study and A Path Forward" +Think of this as a short internal directive you can hand to the StellaOps team. + +--- + +## 1. Non‑negotiable principles + +Tell the team these are hard constraints, not “nice to haves”: + +1. **No silent false negatives.** + + * The engine must *never* drop a potential vulnerability just to reduce noise. + * If something is uncertain → label it (`“low confidence”`, `"maybe affected"`) and show it, don’t hide it. + +2. **Separation of “detection” vs. “prioritization”.** + + * Detection layer: “Which CVEs *might* apply?” → exhaustive and conservative. + * Prioritization layer: “Which matter most?” → filters for humans, but never deletes raw matches. + +3. **Offline and air‑gapped are first‑class.** + + * Any feature must work in a strictly offline mode or fail *explicitly* with a clear error, not partial silent behavior. + * A `--offline` / `no-network` mode must guarantee **zero network calls** on every code path. + +4. **No schema or DB surprises.** + + * Engine and DB must have explicit, negotiated versions. + * If a DB is incompatible, the scan must fail loudly with a remediation hint, not degrade silently. + +5. **SBOM integrity over convenience.** + + * SBOMs must be treated as evidence that can be wrong or tampered with, not as gospel. + * The system should verify SBOMs against artefact digests and highlight discrepancies. + +6. **Reachability is a signal, not a gate.** + + * Reachability analysis may reprioritize, but never suppress the existence of a vulnerability. + +--- + +## 2. Guardrails mapped to past failure modes + +### A. “Missed JAR CVEs” / detection‑priority bugs + +**Problem type:** Scanner skipped valid vulnerabilities because matching logic was too strict or defaulted to a “safe” mode that sacrificed coverage. + +**Instructions to devs:** + +1. **Tri‑state matching, never binary only.** + + * Every candidate CVE should end up in one of: + + * `confirmed_affected` + * `potentially_affected` (e.g. upstream advisory, weak match) + * `not_affected` + * *Prohibited:* silently dropping `potentially_affected` to “help” users. + +2. **Explicit detection modes with safe defaults.** + + * Provide: + + * `mode=exhaustive` (default in backend; UI can choose what to show prominently) + * `mode=precise` (for strict production gating, but still storing all candidates) + * Detection mode can change *presentation*, not the underlying recorded facts. + +3. **Mandatory explanation fields.** + + * For each match, store: + + * `why_matched` (e.g. “CPE normalized match via alias map”) + * `why_not_promoted` (e.g. “only upstream advisory, no distro backport”) + * If something is dropped from “confirmed”, it must have a machine‑readable reason. + +--- + +### B. SBOM incompleteness (“missing orphan packages”) + +**Problem type:** Merge logic or graph logic discarded “orphan” packages → incomplete SBOM → missed CVEs and EOL issues. + +**Instructions:** + +1. **Invariant: no silent dropping of packages.** + + * During SBOM generation/ingestion, any package discovered must be present in the final SBOM *or* explicitly listed in a `dropped_components` section with a reason code. + * Add automated tests asserting: `discovered_count == sbom_components_count + dropped_components_count`. + +2. **Cross‑check with package managers.** + + * For OS images: + + * Compare SBOM against `rpm -qa`, `dpkg -l`, `apk info`, etc. + * Define allowed differences (e.g. virtual packages, metapackages). + * Fail CI if SBOM under‑reports by more than a small, configurable margin. + +3. **Graph vs list separation.** + + * Keep a flat **component list** separate from the **dependency graph**. + * Graph logic may exclude out‑of‑graph components from “dependency tree”, but never from the raw component list. + +4. **SBOM round‑trip tests.** + + * For each supported SBOM format: + + * Generate SBOM → re‑ingest → generate again → compare component sets. + * Any component lost in the round‑trip is a bug. + +--- + +### C. Offline DB, Java DB and schema‑churn failures + +**Problem type:** New DBs introduced (e.g. Java DB) forced online updates despite `--skip-db-update`; schema changes broke offline setups. + +**Instructions:** + +1. **Strict offline contract.** + + * If `offline=true`: + + * Absolutely forbidden: network calls, even retries. + * If a DB is missing: abort with an explicit error like + `“Java vulnerability DB not present; run command X on a connected host to fetch it.”` + +2. **Version negotiation between engine and DB.** + + * DB contains: + + * `db_schema_version` + * `min_engine_version` / `max_engine_version` + * Engine on startup: + + * If DB schema is unsupported → fail fast with a migration hint. + * *Never* continue with a mismatched DB. + +3. **Separate DBs for language ecosystems.** + + * Each language/ecosystem DB handled independently: + + * Missing Java DB should not break OS scanning. + * Mark those language results as “unavailable due to missing DB”. + +4. **Compatibility test matrix.** + + * For every release: + + * Test with **previous N DB versions** and **newest DB**. + * Test both online and offline flows. + * Regression tests should include: + + * Stale DB, corrupted DB, missing DB, wrong schema version. + +--- + +### D. Reachability misclassification (Snyk‑style issues) + +**Problem type:** “Unreachable” label used where analysis simply lacked coverage → real exploitable bugs down‑weighted or hidden. + +**Instructions:** + +1. **Coverage‑aware reachability labels.** + + * Distinguish: + + * `reachable` (with proof) + * `not_reachable_within_coverage` (negative proof within known coverage) + * `not_analyzed` (no coverage for this stack/entrypoint) + * **Never** label as “unreachable” if the language/framework isn’t supported. + +2. **No gating by reachability.** + + * Policies may say “ignore non‑reachable in dashboard”, but the raw issue must exist and be queryable. + * Any mass change in reachability (due to engine upgrade) must be tracked and explained in release notes. + +3. **Reachability regression tests.** + + * Maintain a small corpus of apps where: + + * Specific vulnerable functions *are* called. + * Specific vulnerable functions are present but deliberately never called. + * CI must ensure reachability engine’s decisions on this corpus remain stable or improve, never regress. + +--- + +### E. Package identity & provenance (Xray‑style name collision issues) + +**Problem type:** Name/version alone used for matching; local/internal components misidentified as public packages (false positives) and vice versa (false negatives). + +**Instructions:** + +1. **Always prefer fully qualified identifiers.** + + * Normalize everything to purl‑like coordinates: + + * e.g. `pkg:npm/float-kit@1.0.0`, `pkg:docker/library/alpine@3.18`, `pkg:maven/com.squareup.okio/okio@3.0.0` + * Name+version alone should be treated as low‑confidence. + +2. **Provenance as a first‑class field.** + + * Record for each component: + + * Source: registry (npm, PyPI, Maven, OS repo), VCS, local, vendored. + * Evidence: lockfile, SBOM, manifest, binary heuristics. + * Only match against the correct source: + + * npm advisories apply only to `source=npm` components, etc. + +3. **Tiered match confidence.** + + * `high` – exact purl or CPE match with expected registry. + * `medium` – alias mapping, well‑known naming differences. + * `low` – name/version only, with no provenance. + * Policy: **high** and **medium** shown normally; **low** flagged as “needs manual review” or hidden behind a specific toggle, never silently treated as truth. + +4. **SBOM + runtime fusion.** + + * When ingesting external SBOMs, try to confirm: + + * Component digest vs actual file. + * If mismatched → downgrade confidence or flag as “SBOM out of sync with artefact”. + +--- + +## 3. Testing & regression harness (what must exist before launch) + +Give the team a checklist that must be green before any release: + +1. **Golden corpus of images/projects.** + + * Include: + + * Known vulnerable JARs (like the ones from earlier incidents). + * Distros with thousands of packages (Rocky, Debian, Alpine). + * Mixed stacks (Java + Python + Node). + * For each, store: + + * Expected # of components. + * Expected CVEs (per tool / per feed). + * Expected SBOM shape. + +2. **Cross‑tool differential tests.** + + * Regularly run StellaOps + Trivy + Grype/Syft + Snyk on the corpus (in a lab). + * Any large deviation: + + * More findings: investigate FPs. + * Fewer findings: treat as potential FN until proven otherwise. + +3. **Mutation tests for SBOM and metadata.** + + * Randomly: + + * Remove lockfiles. + * Change versions. + * Make SBOM inconsistent with artefact. + * Verify: + + * System flags inconsistencies. + * Doesn’t crash. + * Doesn’t blindly trust clearly broken data. + +4. **Offline chaos tests.** + + * Simulate: + + * No network. + * Corrupted DB. + * Outdated DB with new engine. + * Required behavior: + + * Clear, actionable errors. + * No partial, silent scans. + +--- + +## 4. Concrete acceptance criteria you can mandate + +You can literally hand this as a policy: + +> A feature or release must **not** ship unless: +> +> 1. Running in offline mode never triggers a network call and does not crash if a language‑specific DB is missing; instead, it emits a clear, actionable error. +> 2. SBOM component counts for golden images are within agreed tolerances vs package manager outputs, with all differences explained. +> 3. For every CVE in the golden corpus, the engine either reports it as `confirmed_affected` / `potentially_affected` or logs a machine‑readable justification for `not_affected`. +> 4. Reachability status for golden test apps is stable or improved vs previous release; there are no reclassified “unreachable” issues where coverage is absent. +> 5. Any schema or DB change is accompanied by: +> +> * A migration path. +> * A compatibility test. +> * A failure mode that is explicit and documented. + +If you bake those rules into your engineering process (and CI), StellaOps will be structurally protected from the exact classes of failures we’ve seen in the other tools — and when something *does* go wrong, it will be visible, explainable, and fixable, rather than a silent loss of coverage. diff --git a/docs/product-advisories/29-Nov-2025 - StellaOps – Mid-Level .NET Onboarding (Quick Start).md b/docs/product-advisories/29-Nov-2025 - StellaOps – Mid-Level .NET Onboarding (Quick Start).md new file mode 100644 index 000000000..cca284909 --- /dev/null +++ b/docs/product-advisories/29-Nov-2025 - StellaOps – Mid-Level .NET Onboarding (Quick Start).md @@ -0,0 +1,637 @@ +Here’s a compact, no‑fluff onboarding brief to get a mid‑level .NET dev productive on StellaOps in a couple of days. + +--- + +# StellaOps – Mid‑Level .NET Onboarding (Quick Start) + +## 0) What you’re building (context in one minute) + +StellaOps is a sovereign, air‑gap‑friendly SBOM→VEX platform written in .NET 10. Core ideas: deterministic scans, cryptographic attestations (DSSE/in‑toto; optional PQC), trust lattices for VEX decisions, and a replayable audit trail. + +--- + +## 1) Repos to open first (read in this order) + +1. `src/StellaOps.Scanner.WebService/` — scanning surfaces, rules plumbing, lattice calls. +2. `src/StellaOps.Vexer/` (a.k.a. Excititor) — VEX verdict engine & trust‑merge logic. +3. `src/StellaOps.Sbomer/` — SBOM ingest/normalize (CycloneDX/SPDX). +4. `src/StellaOps.Authority/` — keys, attestations, license tokens, Rekor integration. +5. `src/StellaOps.Scheduler/` — batch & replay orchestration. +6. `src/StellaOps.Shared/CanonicalModel/` — canonical entities & graph IDs (read carefully). + +Tip: keep `docs/modules/platform/*` open for protocol notes (DSSE envelopes, lattice terms). + +--- + +## 2) Local dev up in ~10–15 min + +* Prereqs: .NET 10 SDK (preview), Docker, Node (for Angular UI if needed), WSL2 optional. +* From repo root: + +```bash +# infra +docker compose -f compose/offline-kit.yml up -d # Mongo/Postgres/Rabbit/MinIO per profile + +# backend +dotnet restore +dotnet build -c Debug + +# run a focused slice (scanner + authority) +dotnet run --project src/StellaOps.Authority/StellaOps.Authority.csproj +dotnet run --project src/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj +``` + +* Environment: copy `env/example.local.env` → `.env`, then set `STELLAOPS_DB` provider (`Mongo` or `Postgres`) and `AUTHORITY_*` keys. + +--- + +## 3) Deterministic testcases to run locally (prove your env is correct) + +These are “golden” replays: same inputs → same graph & hashes. + +1. **Hello‑SBOM → VEX Not‑Affected (Reachability‑false)** + +```bash +dotnet test tests/Determinism/Det_SbomToVex_NotAffected.csproj +``` + +Checks: identical GraphRevisionID and DSSE payload hash across two consecutive runs. + +2. **In‑toto chain: source→build→image attestation** + +```bash +dotnet test tests/Attestations/Att_InToto_Chain.csproj +``` + +Checks: DSSE envelope canonicalization stable; signature over CBOR‑canonical JSON matches stored hash. + +3. **Lattice merge: vendor VEX + runtime signal** + +```bash +dotnet test tests/Lattice/Lattice_VendorPlusRuntime.csproj +``` + +Checks: merge verdict is stable with the same input set order; produces identical TrustReceipt. + +If any golden snapshot differs, your clock/locale/line‑endings or JSON canonicalizer is misconfigured. + +--- + +## 4) Coding conventions (cryptographic attestations & determinism) + +* **JSON**: serialize with our `CanonicalJson` (UTF‑8, sorted keys, no insignificant whitespace, `\n` line endings). +* **DSSE**: always embed `payloadType` = `application/vnd.stellaops.trust+json`. +* **Hashing**: BLAKE3 for internal content addressing, SHA‑256 where interop requires. +* **Keys**: use `Authority.KeyRing` provider (Ed25519 by default; PQC Dilithium optional flag `AUTHORITY_PQC=on`). +* **Timestamps**: use `Instant` (UTC) with truncation to milliseconds; never `DateTime.Now`. +* **IDs**: graph nodes use `HashStableId` derived from canonical bytes; never database autoinc for public IDs. +* **VEX**: verdicts must include `proofs[]` (e.g., reachability, config‑guards, runtime path) and a `receipt` signed by Authority. +* **Repro**: any function that affects verdicts must be pure or behind a deterministic adapter. + +Snippet (sign a DSSE envelope deterministically): + +```csharp +var payload = CanonicalJson.Serialize(trustDoc); +var env = DsseEnvelope.Create("application/vnd.stellaops.trust+json", payload); +var signed = await keyRing.SignAsync(env.CanonicalizeBytes()); +await rekor.SubmitAsync(signed, RekorMode.OfflineMirrorIfAirgapped); +``` + +--- + +## 5) Minimal daily loop + +* Pick one starter issue (below). +* Write unit tests first; ensure golden snapshots match. +* Run `dotnet test --filter Category=Determinism`. +* Commit with `feat(scanner|vexer|authority): …` and include the GraphRevisionID delta in the body. + +--- + +## 6) Three starter issues (teach the canonical data model) + +### A) Normalize CycloneDX components → Canonical Packages + +**Goal**: map CycloneDX `components` to `CanonicalPackage` with stable IDs. +**Where**: `StellaOps.Sbomer` + tests in `tests/Determinism/Det_SbomMapping`. +**Done when**: + +* Two equivalent SBOMs (field order shuffled) map to identical package set & IDs. +* Snapshot `CanonicalPackageSet.hash` is stable. +* Edge cases: missing `purl`, duplicate components, case differences. + +### B) Implement “Not‑Affected by Configuration” proof + +**Goal**: add proof generator that marks a CVE as not‑affected if a config gate is off. +**Where**: `StellaOps.Vexer/Proofs/ConfigSwitchProof.cs`. +**Done when**: + +* Given `FeatureX=false`, CVE‑1234 becomes `not_affected` with proof payload including `configPath`, `observed=false`. +* Deterministic proof hash and DSSE receipt exist. +* Lattice merge keeps vendor “affected” but flips to `not_affected` when runtime/config proof weight > threshold. + +### C) Authority offline Rekor mirror submitter + +**Goal**: if air‑gapped, write DSSE entries to local mirror; sync later. +**Where**: `StellaOps.Authority/Rekor/RekorMirrorClient.cs`. +**Done when**: + +* `RekorMode.OfflineMirrorIfAirgapped` stores canonical entry (JSON+hash path). +* `rekor sync` job replays in order, preserving entry IDs. +* Golden test ensures same input sequence → same mirror tree hash. + +--- + +## 7) Database notes (Mongo ↔ Postgres switchability) + +* Use repository interfaces in `StellaOps.Shared.Persistence`. +* Canonical/public IDs are hash‑derived; DB keys are implementation‑local. +* Never rely on DB sort order for any hash or verdict; always re‑canonicalize before hashing. + +--- + +## 8) Debug checklist (most common slips) + +* Non‑canonical JSON (unsorted keys, trailing spaces). +* Local time sneaking into proofs. +* Unstable GUIDs in tests. +* Non‑deterministic enumeration over `Dictionary<>`. +* Different newline conventions on Windows—enforce `\n` in canonical paths. + +--- + +## 9) Useful commands + +```bash +# run determinism pack +dotnet test --filter Category=Determinism + +# update golden snapshots (intentional change only) +dotnet test --filter Category=Determinism -- TestRunParameters.Parameter(name=\"UpdateSnapshots\", value=\"true\") + +# quick API smoke +curl -s http://localhost:5080/health +curl -s -X POST http://localhost:5081/scan -d @samples/nginx.sbom.json + +# verify DSSE signature locally +dotnet run --project tools/StellaOps.Tools.Verify -- file trust.receipt.json +``` + +--- + +## 10) Ask‑once glossary + +* **SBOM**: software bill of materials (CycloneDX/SPDX). +* **VEX**: vulnerability exploitability exchange (verdicts: affected / not‑affected / under‑investigation). +* **DSSE**: signed payload wrapper; we canonicalize before signing. +* **Lattice**: rule system to merge proofs/verdicts from different sources deterministically. +* **GraphRevisionID**: hash of the canonical trust graph; your “build number” for audits. + +--- + +Want me to turn this into `docs/onboarding/dev-quickstart.md` plus three ready‑to‑run GitHub issues and the test scaffolds? +````markdown +# StellaOps Developer Quickstart + +> **Audience:** Mid‑level .NET developers +> **Goal:** Get you productive on StellaOps in 1–2 days, with special focus on determinism, cryptographic attestations, and the canonical data model. + +--- + +## 1. What You’re Building (Context) + +StellaOps is a sovereign, air‑gap‑friendly platform that turns **SBOMs → VEX** with a fully **replayable, deterministic trust graph**. + +Core concepts: + +- **Deterministic scans:** Same inputs → same graph, hashes, and verdicts. +- **Cryptographic attestations:** DSSE/in‑toto envelopes, optional PQC. +- **Trust lattice:** Merges vendor VEX, runtime signals, configs, etc. into a single deterministic verdict. +- **Audit trail:** Every decision is reproducible from stored inputs and proofs. + +If you think “content‑addressed trust pipeline for SBOMs + VEX,” you’re in the right mental model. + +--- + +## 2. Repository & Docs Map + +Start by opening these projects **in order**: + +1. `src/StellaOps.Scanner.WebService/` + Scanning endpoints, rule plumbing, and calls into the trust lattice. + +2. `src/StellaOps.Vexer/` (a.k.a. *Excititor*) + VEX verdict engine and trust‑merge logic. + +3. `src/StellaOps.Sbomer/` + SBOM ingest / normalize (CycloneDX, SPDX). + +4. `src/StellaOps.Authority/` + Key management, DSSE/in‑toto attestations, license tokens, Rekor integration. + +5. `src/StellaOps.Scheduler/` + Batch processing, replay orchestration. + +6. `src/StellaOps.Shared/CanonicalModel/` + Canonical entities & graph IDs. **Read this carefully** – it underpins determinism. + +Helpful docs: + +- `docs/modules/platform/*` – protocols (DSSE envelopes, lattice terms, trust receipts). +- `docs/architecture/*` – high‑level diagrams and flows (if present). + +--- + +## 3. Local Dev Setup + +### 3.1 Prerequisites + +- **.NET 10 SDK** (preview as specified in repo) +- **Docker** (for DB, queues, object storage) +- **Node.js** (for Angular UI, if you’re touching the frontend) +- **WSL2** (optional, but makes life easier on Windows) + +### 3.2 Bring Up Infra + +From the repo root: + +```bash +# Bring up core infra for offline / air‑gap friendly dev +docker compose -f compose/offline-kit.yml up -d +```` + +This usually includes: + +* MongoDB or Postgres (configurable) +* RabbitMQ (or equivalent queue) +* MinIO / object storage (depending on profile) + +### 3.3 Configure Environment + +Copy the example env and tweak: + +```bash +cp env/example.local.env .env +``` + +Key settings: + +* `STELLAOPS_DB=Mongo` or `Postgres` +* `AUTHORITY_*` – key material and config (see comments in `example.local.env`) +* Optional: `AUTHORITY_PQC=on` to enable post‑quantum keys (Dilithium). + +### 3.4 Build & Run Backend + +```bash +# Restore & build everything +dotnet restore +dotnet build -c Debug + +# Run a focused slice for development +dotnet run --project src/StellaOps.Authority/StellaOps.Authority.csproj +dotnet run --project src/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj +``` + +Health checks (adjust ports if needed): + +```bash +curl -s http://localhost:5080/health # Authority +curl -s http://localhost:5081/health # Scanner +``` + +--- + +## 4. Deterministic Sanity Tests + +These tests prove your local environment is configured correctly for **determinism**. If any of these fail due to snapshot mismatch, fix your environment before writing new features. + +### 4.1 SBOM → VEX “Not Affected” (Reachability False) + +```bash +dotnet test tests/Determinism/Det_SbomToVex_NotAffected.csproj +``` + +**What it checks:** + +* Two consecutive runs with the same SBOM produce: + + * Identical `GraphRevisionID` + * Identical DSSE payload hashes + +If they differ, inspect: + +* JSON canonicalization +* Locale / culture +* Line endings + +--- + +### 4.2 In‑toto Chain: Source → Build → Image Attestation + +```bash +dotnet test tests/Attestations/Att_InToto_Chain.csproj +``` + +**What it checks:** + +* DSSE envelope canonicalization is stable. +* Signature over CBOR‑canonical JSON matches the stored hash. +* Full in‑toto chain can be replayed deterministically. + +--- + +### 4.3 Lattice Merge: Vendor VEX + Runtime Signal + +```bash +dotnet test tests/Lattice/Lattice_VendorPlusRuntime.csproj +``` + +**What it checks:** + +* Merge verdict is stable regardless of input set order. +* Resulting `TrustReceipt` is byte‑for‑byte identical between runs. + +If any “golden” snapshots differ, you likely have: + +* Non‑canonical JSON +* Unstable enumeration (e.g., iterating `Dictionary<>` directly) +* Locale or newline drift + +--- + +## 5. Coding Conventions (Determinism & Crypto) + +These are **non‑negotiable** when working on code that affects trust graphs, proofs, or attestations. + +### 5.1 JSON & Canonicalization + +* Use our **`CanonicalJson`** helper for anything that will be: + + * Hashed + * Signed + * Used as a canonical ID input +* Rules: + + * UTF‑8 + * Sorted keys + * No insignificant whitespace + * `\n` line endings (enforced for canonical paths) + +### 5.2 DSSE Envelopes + +* `payloadType` must always be: + + * `application/vnd.stellaops.trust+json` +* Envelopes are signed over the **canonicalized bytes** of the payload. + +Example: + +```csharp +var payload = CanonicalJson.Serialize(trustDoc); +var env = DsseEnvelope.Create("application/vnd.stellaops.trust+json", payload); +var signed = await keyRing.SignAsync(env.CanonicalizeBytes()); +await rekor.SubmitAsync(signed, RekorMode.OfflineMirrorIfAirgapped); +``` + +### 5.3 Hashing + +* Internal content addressing: **BLAKE3** +* External / interop where required: **SHA‑256** + +Never mix algorithms for the same ID type. + +### 5.4 Keys & Algorithms + +* Default signatures: **Ed25519** via `Authority.KeyRing` +* Optional PQC: **Dilithium** when `AUTHORITY_PQC=on` +* Never manage keys directly – always use the keyring abstraction. + +### 5.5 Time & Clocks + +* Use `Instant` (UTC) / `DateTimeOffset` in UTC. +* Truncate to **milliseconds** for anything that ends up in canonical data. +* Never use `DateTime.Now` or local time in trust‑critical code. + +### 5.6 IDs & Graph Nodes + +* Public / canonical IDs are derived from **hashes of canonical bytes**. +* DB primary keys are implementation details; do **not** leak them externally. +* Do **not** depend on DB auto‑increment or sort order for anything that affects hashing. + +### 5.7 VEX Verdicts + +Every VEX verdict must: + +* Be backed by `proofs[]` (e.g., reachability analysis, config guards, runtime path). +* Emit a `receipt` signed by **Authority**, wrapping: + + * Verdict + * Proof hashes + * Context (component, vulnerability, scope, etc.) + +--- + +## 6. Daily Workflow + +A minimal loop that keeps you aligned with the platform’s guarantees: + +1. **Pick a focused issue** (see starter tasks below). + +2. **Write tests first**, especially determinism tests where applicable. + +3. Implement the change, keeping: + + * Canonicalization boundaries explicit + * Hashing and signing centralized + +4. Run: + + ```bash + dotnet test --filter Category=Determinism + ``` + +5. Commit using: + + * `feat(scanner): ...` + * `feat(vexer): ...` + * `feat(authority): ...` + * etc. + + Include the affected `GraphRevisionID` in the commit body when relevant to trust‑graph changes. + +--- + +## 7. Suggested Starter Tasks + +These are good first issues that teach the canonical model and determinism expectations. + +### 7.1 Normalize CycloneDX Components → Canonical Packages + +**Goal:** Map CycloneDX `components` to our `CanonicalPackage` model with stable IDs. + +* **Area:** `StellaOps.Sbomer` +* **Tests:** `tests/Determinism/Det_SbomMapping` + +**Definition of done:** + +* Two equivalent SBOMs (only field order differs) produce: + + * Identical package sets + * Identical canonical package IDs +* `CanonicalPackageSet.hash` is stable. +* Edge cases handled: + + * Missing `purl` + * Duplicate components + * Case differences in names or versions + +--- + +### 7.2 Implement “Not‑Affected by Configuration” Proof + +**Goal:** Add proof generator that marks a CVE as `not_affected` when a config gate disables the vulnerable path. + +* **Area:** `StellaOps.Vexer/Proofs/ConfigSwitchProof.cs` + +**Definition of done:** + +* Given `FeatureX=false`, CVE‑1234 yields verdict: + + * `status = not_affected` + * `proofs[]` includes a `ConfigSwitchProof` with: + + * `configPath` + * `observed=false` +* Proof is hashed deterministically and included in the DSSE receipt. +* Lattice merge behavior: + + * Vendor verdict: `affected` + * Runtime/config proof weight > threshold → merged verdict becomes `not_affected` deterministically. + +--- + +### 7.3 Authority Offline Rekor Mirror Submitter + +**Goal:** Support air‑gapped mode: DSSE entries are written to a local Rekor mirror and synced later. + +* **Area:** `StellaOps.Authority/Rekor/RekorMirrorClient.cs` + +**Definition of done:** + +* `RekorMode.OfflineMirrorIfAirgapped`: + + * Stores canonical entries (JSON + hash‑based path) on disk / object store. +* A `rekor sync` background job (or CLI) replays entries in order to the real Rekor instance. +* Determinism test: + + * Same input DSSE sequence → same mirror tree hash and Rekor entry order. + +--- + +## 8. Database Notes (Mongo ↔ Postgres) + +StellaOps is designed to be DB‑agnostic. + +* Use repository interfaces from `StellaOps.Shared.Persistence`. +* Keep **canonical/public IDs hash‑derived**; DB keys are internal. +* Do **not** depend on DB sort order for anything that: + + * Affects hashes + * Affects verdicts + * Ends up in canonical data + +If you need ordering, sort **after** canonicalization using deterministic criteria. + +--- + +## 9. Common Pitfalls & Debug Checklist + +When a determinism test fails, look for these first: + +1. **Non‑canonical JSON** + + * Unsorted keys + * Extra whitespace + * Mixed `\r\n` vs `\n` line endings + +2. **Local Time Leaks** + + * Any `DateTime.Now` or local time in proofs or receipts. + +3. **Random / Unstable IDs** + + * New GUIDs in tests or canonical entities. + * Auto‑increment IDs leaking into hashes. + +4. **Unordered Collections** + + * Iterating over `Dictionary<>` or `HashSet<>` without ordering. + * Using LINQ without explicit `OrderBy` when hashing or serializing. + +5. **Platform Differences** + + * Windows vs Linux newline differences. + * Locale differences (`,` vs `.` for decimals, etc.) – always use invariant culture for canonical data. + +--- + +## 10. Useful Commands + +### 10.1 Determinism Pack + +```bash +# Run only determinism‑tagged tests +dotnet test --filter Category=Determinism +``` + +Update golden snapshots when you *intend* to change canonical behavior: + +```bash +dotnet test --filter Category=Determinism -- \ + TestRunParameters.Parameter(name="UpdateSnapshots", value="true") +``` + +### 10.2 Quick API Smoke + +```bash +curl -s http://localhost:5080/health + +curl -s -X POST \ + http://localhost:5081/scan \ + -H "Content-Type: application/json" \ + -d @samples/nginx.sbom.json +``` + +### 10.3 Verify DSSE Signature Locally + +```bash +dotnet run --project tools/StellaOps.Tools.Verify -- file trust.receipt.json +``` + +--- + +## 11. Glossary (Ask‑Once) + +* **SBOM** – Software Bill of Materials (CycloneDX/SPDX). +* **VEX** – Vulnerability Exploitability eXchange: + + * Verdicts like `affected`, `not_affected`, `under_investigation`. +* **DSSE** – Dead Simple Signing Envelope: + + * Wrapper around payload + signature; we sign canonical bytes. +* **In‑toto** – Supply‑chain attestation framework; we use it for source→build→artifact chains. +* **Lattice** – Rule system that merges multiple verdicts/proofs into a single deterministic verdict. +* **GraphRevisionID** – Hash of the canonical trust graph at a point in time; acts like a build number for audits. + +--- + +Welcome aboard. Your best “map” into the system is: + +1. Read `CanonicalModel` types. +2. Run the determinism tests. +3. Pick one of the starter tasks and ship a small, well‑tested change. + +If you keep everything **canonical, hashable, and replayable**, you’ll fit right in. + +``` +``` diff --git a/docs/product-advisories/30-Nov-2025 - Comparative Evidence Patterns for Stella Ops.md b/docs/product-advisories/30-Nov-2025 - Comparative Evidence Patterns for Stella Ops.md new file mode 100644 index 000000000..8faa4f45c --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Comparative Evidence Patterns for Stella Ops.md @@ -0,0 +1,933 @@ +I’m sharing this because I think the comparison will help sharpen some of the UI/UX & data-model decisions for StellaOps — especially around how “evidence + suppression + audit/export” flows are implemented in existing tools and which patterns we might want to emulate or avoid. + +Here’s a compact snapshot (with recent documentation/screens and dates) of how a few major players — Snyk, GitHub (via repo scanning), Aqua Security / container scanning, Anchore/Grype, and Prisma Cloud — handle evidence, ignores (suppression / VEX), and audit/export primitives. + +--- + +## ✅ What works: Evidence & audit flows that provide clarity + +**Snyk — good “evidence + data flow + context” for code vulnerabilities** + +* In Snyk Code, each issue links to a full “Data flow” trace from source to sink. ([Snyk User Docs][1]) +* The UI hyperlinks to the Git repo, providing code-level context and version control traceability. ([Snyk User Docs][1]) +* “Fix analysis” surfaces recommended fixes, diff examples, and community-driven suggestions. ([Snyk User Docs][1]) + +This deep evidence linkage is a valuable pattern for StellaOps triage and remediation. + +**Anchore/Grype — VEX-aware suppression + SBOM export readiness** + +* Anchore Enterprise documents vulnerability annotations + VEX support (SBOM scanning, annotations, fix statuses). ([Anchore Documentation][2]) +* Grype respects VEX statuses (`not_affected`, `fixed`) and can expose suppressed matches via `--show-suppressed`. ([GitHub][3]) +* Justifications can be scoped (e.g., `vulnerable_code_not_present` filters) and the API surfaces (Oct 2025) enable automation of SBOM + VEX + diffed results. ([Anchore Documentation][2]) + +This forms a solid SBOM → vulnerability → VEX → audit/export pipeline. + +--- + +## ⚠️ Where things are brittle + +**Snyk — suppression semantics vary across scan types** + +* Ignore rules are project/integration-specific; UI ignores may not carry over to CLI import flows. ([Snyk][4]) +* Snyk Code uses separate suppression UX (IDE/CLI) from OSS scans, leading to fragmentation. ([Snyk User Docs][5]) + +**Anchore/Grype — operational caveats** + +* Feb 2025 issue: VEX suppression failed for directory scans because the source type wasn’t supported. ([GitHub][6]) +* Some suppressions require explicit config (`GRYPE_VEX_ADD`). ([GitHub][3]) +* Ensuring SBOM + VEX + diffed outputs for audits often needs extra orchestration. + +**Prisma Cloud — audit/export primitives are coarse** + +* “Suppress Code Issues” feature exists, but no public doc shows an end-to-end SBOM + VEX + audit export flow. ([Prisma Cloud Documentation][7]) +* Repo scanning + suppression features exist, but forensic traceability appears limited to “report + suppress”. ([Prisma Cloud Documentation][8]) + +--- + +## 🔑 What StellaOps should copy / avoid + +**Copy** + +* Snyk Code evidence + fix linking provides actionable traceability. +* Anchore/Grype SBOM → VEX suppression → facade for automation is the workflow to engineer. +* Surface suppression metadata (reason, status, justification) for auditing suppressed items. + +**Avoid** + +* Fragmented ignore rules per scan surface (OSS vs Code vs Container). Instead, keep suppression consistent. +* Hidden suppression; tie it to the main UI instead of a “show suppressed” toggle. +* Incomplete VEX support for certain source types; test across containers, directories, binaries, SBOMs. + +If helpful, I can build a spreadsheet (Snyk, GitHub, Aqua, Anchore/Grype, Prisma Cloud) comparing evidence, suppression, APIs, exports, and gaps with doc links. + +[1]: https://docs.snyk.io/scan-with-snyk/snyk-code/manage-code-vulnerabilities/breakdown-of-code-analysis?utm_source=chatgpt.com "Breakdown of Code analysis" +[2]: https://docs.anchore.com/current/docs/vulnerability_management/?utm_source=chatgpt.com "Anchore Secure - Vulnerability Management" +[3]: https://github.com/anchore/grype?utm_source=chatgpt.com "anchore/grype: A vulnerability scanner for container ..." +[4]: https://snyk.io/blog/ignoring-vulnerabilities-with-snyk/?utm_source=chatgpt.com "Ignoring vulnerabilities with Snyk" +[5]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues/exclude-files-and-ignore-issues-faqs?utm_source=chatgpt.com "Exclude files and ignore issues FAQs" +[6]: https://github.com/anchore/grype/issues/2471?utm_source=chatgpt.com "Support using VEX documents with directory scans and ..." +[7]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/application-security/risk-management/monitor-and-manage-code-build/suppress-code-issues?utm_source=chatgpt.com "Suppress Code Issues - Prisma Cloud Documentation" +[8]: https://docs.prismacloud.io/en/compute-edition/32/admin-guide/vulnerability-management/code-repo-scanning?utm_source=chatgpt.com "Configure code repository scanning" +Matrix built. I’ll keep it compact enough to scan quickly, but detailed enough that you can lift it straight into a Google Sheet / Confluence page. + +--- + +## 1. Evidence & context: what a *single finding* looks like + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Code-level evidence** | Snyk Code shows a *Data flow* view (taint graph) from source → sink with step-by-step code lines in the UI and IDE. ([docs.snyk.io][1]) | Code scanning alerts show file/line and, for data-flow rules, a “how data moves through the code” view with **Show paths** for the full flow. ([GitHub Docs][2]) | Trivy is not a SAST engine; evidence is per-file / resource for vulnerabilities & misconfigurations (IaC, K8s, etc.), not data-flow graphs. ([Trivy][3]) | No SAST; evidence is package / image / SBOM level (CVE, package, version, location). Grype is focused on images/filesystems, not code flows. ([Chainguard Academy][4]) | AppSec module shows issues mapped to file & line and offers inline remediation/fix flows; surfaced in console and IDE plugins (JetBrains/VS Code). ([Palo Alto Networks][5]) | +| **Dependency / container evidence** | For OSS & containers, Snyk shows dependency paths and has a **project issue paths** API that returns the introduction chain for a vuln. ([docs.snyk.io][6]) | Dependabot alerts show vulnerable package, version, advisories, and affected functions to help decide if code actually uses them. ([GitHub Docs][7]) | Trivy shows image → layer → package → vuln with installed/fixed versions; same for repos & filesystems in JSON/SARIF. ([GitHub][8]) | Grype output lists artifact, package, version, location, severity; when paired with Syft SBOM, you get full component graph for images and other artifacts. ([Chainguard Academy][4]) | Prisma’s Vulnerability Explorer & scan reports show image/host, package, severity and compliance IDs for image and host scans. ([docs.prismacloud.io][9]) | +| **SBOM / supply-chain context** | `snyk sbom` generates CycloneDX and SPDX SBOMs for local projects; Snyk also supports SBOM export in enterprise tiers. ([docs.snyk.io][10]) | GitHub builds a dependency graph from manifests and ingests SARIF from external scanners; it is SBOM-adjacent but not an SBOM UI. ([GitHub Docs][11]) | Trivy can output SBOMs (CycloneDX, SPDX, SPDX-JSON) and Kubernetes “KBOM” views, and is often used as the SBOM generator. ([Chainguard Academy][12]) | Anchore Enterprise is explicitly “SBOM-powered”; SBOM management views let you navigate applications and export SBOM-centric vulnerability views. ([docs.anchore.com][13]) | Prisma Cloud is inventory-/RQL-centric; SBOMs are not a first-class UI artifact and there is no public doc for full SBOM navigation comparable to Anchore or Trivy. ([Palo Alto Networks][5]) | +| **Where the evidence appears** | Web UI, CLI, IDEs (VS Code, JetBrains) and PR checks. Ignores for Snyk Code are now “consistent” across UI/CLI/IDE (2025-11 EA). ([docs.snyk.io][14]) | Web UI (Security tab), PR Conversation tab for code scanning reviews, REST & GraphQL APIs, and SARIF imports from many scanners. ([GitHub Docs][15]) | Trivy: CLI / JSON / SARIF / CI integrations (GitHub Actions, etc.). Aqua Platform adds a CNAPP UI with risk-based vulnerability views. ([GitHub][8]) | Grype: CLI & JSON/SARIF outputs; Anchore Enterprise: browser UI (dashboards, SBOM views, reporting) plus REST/GraphQL APIs. ([Chainguard Academy][4]) | Prisma Cloud: web console (AppSec dashboards, Vulnerability Explorer), REST APIs, IDE plugins for JetBrains & VS Code. ([docs.prismacloud.io][16]) | + +--- + +## 2. Suppression / ignore / VEX behavior + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Suppression primitives** | `.snyk` policy file for OSS & container & IaC; `snyk ignore` CLI; “Ignore” in web UI; Snyk Code uses UI/CLI/IDE ignores but *not* `.snyk`. ([docs.snyk.io][17]) | Code scanning and Dependabot alerts can be **dismissed** with reasons; Dependabot has auto-triage rules (auto-dismiss/snooze). ([GitHub Docs][18]) | Trivy: `.trivyignore` / `.trivyignore.yaml` lists vuln IDs; `--ignore-policy` for Rego-based filtering; severity filters. ([aquasecurity.github.io][19]) | Grype: VEX-aware filtering via `--vex` and ignore rules; Anchore Enterprise: UI-driven vulnerability annotations that act as a structured “suppress or re-classify” layer. ([GitHub][20]) | AppSec: “Suppress Code Issues” (per-issue or globally) and more general suppression rules (disable policy / by source / by resource / by tag). ([docs.prismacloud.io][21]) | +| **Metadata captured with suppression** | `snyk ignore` supports `--reason` and `--expiry`; UI shows who ignored and allows editing/unignore; APIs expose `ignoreReasons` on issues. ([docs.snyk.io][17]) | Dismissal reason: `false positive`, `won't fix`, `used in tests`; optional comment. Dismissal requests for code scanning add requester/reviewer, timestamps, status. ([GitHub Docs][22]) | Ignores live in repo-local files (`.trivyignore*`) or Rego policies; VEX documents contribute status metadata (per CVE, per product). No central UI metadata by default. ([aquasecurity.github.io][19]) | Annotations carry VEX-aligned status plus explanatory text; RBAC role `vuln-annotator-editor` controls who can create/modify/delete them. ([docs.anchore.com][23]) | Suppressions include justification text; there is a dedicated API to retrieve suppression justifications by policy ID and query (auditable). ([pan.dev][24]) | +| **Scope & consistency** | Ignores are *local* to project/integration/org; ignoring in a Git repo project does not automatically ignore the same issue imported via CLI. Snyk Code ignore semantics differ from OSS/Container (no `.snyk`). ([Snyk][25]) | Dismissals attach to a specific alert (repo, branch baseline); PRs inherit baseline. Dependabot has repo-level config (`dependabot.yml`) and org-level alert configuration. ([GitHub Docs][7]) | Suppression is run-local: `.trivyignore` travels with source, policies live in config repos; VEX docs are attached per artifact or discovered via VEX Hub / registries. Reproducible but not centralized unless you build it. ([aquasecurity.github.io][19]) | Grype suppression is per scan & VEX document; Anchore Enterprise stores annotations centrally per artifact and exposes them consistently in UI/API and exported VEX. ([GitHub][20]) | Suppression rules can be global, per source, per resource, or tag-scoped; they affect console views and API results consistently when filters are applied. ([pan.dev][24]) | +| **VEX support (ingest + semantics)** | Snyk generates SBOMs but has no documented first-class VEX ingestion or export; VEX is referenced mostly at the SBOM/standards level, not as a product feature. ([Snyk][26]) | No native VEX semantics in alerts; GitHub consumes SARIF only. VEX can exist in repos or external systems but is not interpreted by GHAS. ([GitHub Docs][11]) | Trivy has **explicit VEX support**: supports OpenVEX, CycloneDX VEX, and CSAF VEX, using local files, VEX repositories, SBOM externalReferences, and OCI VEX attestations to filter results. ([Trivy][27]) | Grype has VEX-first behavior: `--vex` to ingest OpenVEX; by default `not_affected`/`fixed` go to an ignore set; `--show-suppressed` exposes them. Anchore Enterprise 5.22+ adds OpenVEX export and CycloneDX VEX export (5.23). ([GitHub][20]) | No public documentation of native VEX ingestion or export; prioritization is via policies/RQL rather than VEX semantics. | +| **Default handling of suppressed items** | Ignored issues are hidden by default in UI/CLI/IDE views to reduce noise; some tools like `snyk-to-html` will still show ignored Snyk Code issues. ([docs.snyk.io][28]) | Dismissed / auto-dismissed alerts are *not* shown by default (`is:open` filter); you must explicitly filter for closed / auto-dismissed alerts. ([GitHub Docs][29]) | When VEX is supplied, non-affected/fixed statuses are filtered out; suppressed entries are generally not shown unless you adjust filters/output. Some edge-cases exist around `.trivyignore.yaml` behavior. ([Trivy][27]) | Grype hides suppressed matches unless `--show-suppressed` is set; community discussions explicitly argue for “suppressed hidden by default” across all formats. ([artifacthub.io][30]) | Suppressed / excluded issues are hidden from normal risk views and searches and only visible via explicit filters or suppression APIs. ([pan.dev][24]) | +| **Known pain points** | Fragmented semantics: Snyk Code vs OSS/Container vs IaC handle ignores differently; ignores are not global across ingest types. ([docs.snyk.io][31]) | Dismissal is per-tool: Dependabot rules don’t govern CodeQL and vice-versa; no VEX; you must build cross-tool policy on top. | Some rough edges reported around `.trivyignore.yaml` and SARIF vs exit-code behavior; VEX support is powerful but still labeled experimental in docs. ([GitHub][32]) | VEX support for certain source types (e.g., some directory + SBOM combos) has had bugs; suppressed entries disappear by default unless you remember `--show-suppressed`. ([GitHub][33]) | No VEX, and suppression semantics may diverge across CSPM/AppSec/Compute; complexity of RQL and multiple modules can make suppression strategy opaque without good governance. ([Palo Alto Networks][5]) | + +--- + +## 3. Audit & export primitives + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Built-in UI exports** | Reporting UI lets you export reports as PDF and table data as CSV; you can also download CSV from several report views. ([docs.snyk.io][34]) | UI is mostly “view only”; some security overview exports are available, but reports are usually built via API or marketplace actions (e.g., PDF report GitHub Action). ([GitHub Docs][35]) | Aqua Platform provides CSV exports and API integrations for vulnerability reports; on-prem deployments can export “user policy data, vulnerability data and system settings data.” ([Aqua][36]) | SBOM/Vulnerability views have an **Export CSV** button; the Reports service aggregates via GraphQL into CSV/JSON from pre-built templates and dashboards. ([docs.anchore.com][37]) | Vulnerability Explorer and scan reports allow CSV export; code-build issues can be exported with filters as CSV from the AppSec UI. ([docs.prismacloud.io][16]) | +| **Machine-readable exports / APIs** | REST Issues API; Export API (GA) to create CSV exports via async jobs; SBOM CLI (`snyk sbom`) for CycloneDX/SPDX; audit log export for user actions. ([docs.snyk.io][38]) | SARIF input/output; REST & GraphQL APIs for code scanning & Dependabot; many third-party actions to transform alerts into PDFs/CSVs or send to other systems. ([GitHub Docs][11]) | Trivy outputs JSON, SARIF, CycloneDX, SPDX, SPDX-JSON, GitHub formats; these can be uploaded into GHAS or other platforms. Aqua exposes vulnerability data via API. ([Trivy][39]) | Grype outputs JSON, table, CycloneDX, SARIF, plus Go-template based CSV; Anchore Enterprise exposes APIs for vulnerabilities, policies, and reports. ([Chainguard Academy][4]) | CSPM/AppSec APIs: `vulnerabilities/search` (JSON) and `.../search/download` (GZIP CSV) for RQL queries; broader Prisma Cloud API for automation/integration. ([pan.dev][40]) | +| **VEX / “authoritative statement” export** | No native VEX export; at best you can export SBOMs and issues and post-process into VEX with external tooling. ([Snyk][26]) | None. Vendors can publish VEX separately, but GHAS doesn’t emit VEX. | Trivy consumes VEX but does not itself author VEX; Aqua operates VEX Hub as a central repository of vendor VEX documents rather than as a generator. ([Trivy][27]) | Anchore Enterprise 5.22/5.23 is currently the most “productized” VEX exporter: OpenVEX and CycloneDX VEX including annotations, PURLs, metadata about the scanned image. ([docs.anchore.com][41]) | No VEX export; risk posture is expressed via internal policies, not standardized exploitability statements. | +| **Suppression / ignore audit** | Ignored issues can be exported by filtering for “Ignored” in Reporting and downloading CSV; APIs expose ignore reasons; audit log export records user actions such as ignoring issues. ([docs.snyk.io][42]) | Code scanning dismissal requests are full audit records (who requested, who approved, reason, timestamps). Dependabot dismissal comments & auto-dismissed alerts are queryable via GraphQL/REST and UI filters. ([GitHub Docs][43]) | Auditability is repo-driven: `.trivyignore`/VEX/rego live in version control; Aqua’s own reporting/APIs can include current vulnerability state but do not (publicly) expose a dedicated VEX change log. ([aquasecurity.github.io][19]) | Annotations are first-class objects, controlled via RBAC and exportable as OpenVEX/CycloneDX VEX; this effectively *is* an audit log of “why we suppressed or downgraded this CVE” over time. ([docs.anchore.com][23]) | Suppression justification APIs provide a structured record of why and where policies are suppressed; combined with CSV/API exports of vulns, this gives a workable audit trail for regulators. ([pan.dev][24]) | + +--- + +## 4. What this suggests for StellaOps UX & data-model + +### Patterns worth copying + +1. **Data-flow & path-level “evidence” as the default detail view** + + * Snyk Code’s taint-flow UI and GitHub’s “Show paths” for CodeQL alerts make a single issue *explain itself* without leaving the tool. ([docs.snyk.io][1]) + * For StellaOps: + + * For code: provide a path graph (source → sanitizers → sinks) with line-level jumps. + * For SBOM/containers: mirror Snyk’s Project Issue Paths & Grype/Syft by showing a dependency path or image-layer path for each CVE. + +2. **VEX-first suppression semantics** + + * Trivy and Grype treat VEX as the *canonical* suppression mechanism; statuses like `not_affected` / `fixed` hide findings by default, with CLI flags to show suppressed. ([Trivy][27]) + * Anchore Enterprise adds a full annotation workflow and emits OpenVEX/CycloneDX VEX as the authoritative vendor statement. ([docs.anchore.com][23]) + * For StellaOps: make VEX status the **single source of truth** for suppression across all modules, and always be able to reconstruct a proof tree: SBOM → vuln → VEX statement → policy decision. + +3. **Rich suppression metadata with explicit justification & expiry** + + * Snyk captures reason + expiry; GitHub uses structured reasons + free-form comment; Prisma provides a dedicated suppression-justification API. ([docs.snyk.io][17]) + * For StellaOps: require a reason taxonomy + free-text + optional “proof link” (e.g., internal ticket, upstream vendor VEX) for any “Not Affected” or “Won’t Fix,” and store that as part of the public proof spine. + +4. **Strong export & API stories for regulators** + + * Snyk’s Export API, Anchore’s Reports service, and Prisma’s RQL+CSV APIs give customers reliable machine-readable exports for audits and custom BI. ([updates.snyk.io][44]) + * Anchore’s CycloneDX VEX export is the best current model for “signable, portable, regulator-friendly” proofs. ([Security Boulevard][45]) + * For StellaOps: + + * Treat CSV/JSON/SBOM/VEX exports as *first-class* features, not add-ons. + * Tie them to deterministic scan manifests and graph revision IDs (your “trust algebra” backbone) so an auditor can replay any historical report. + +5. **Repo-local suppression files as “policy as code”** + + * `.snyk`, `.trivyignore`, Rego policies, and VEX docs living alongside source are proven patterns for reproducible suppression. ([docs.snyk.io][46]) + * For StellaOps: mirror this with a small set of declarative files (`stellaops.vexmap.yaml`, `trust-lattice.yaml`) that your engine and UI both honor. + +### Patterns to deliberately avoid + +1. **Fragmented ignore semantics by scan type** + + * Snyk’s different behavior across Code vs OSS vs Container vs IaC (e.g., `.snyk` not honored for Snyk Code) is a recurring source of confusion. ([docs.snyk.io][31]) + * For StellaOps: a single suppression model (VEX+policy) must apply consistently whether the evidence came from SAST, SBOM, container scan, or runtime telemetry. + +2. **Making suppressed issues effectively invisible** + + * Grype and GitHub both hide suppressed/dismissed issues by default and require extra flags/filters to see them. ([artifacthub.io][30]) + * For StellaOps: + + * Default UI for any artifact should show a small, always-visible “suppressed” counter with a click-through view. + * Every export format should have an option to include suppressed entries with their VEX status and reasoning. + +3. **Lack of a standard “authoritative statement” artifact** + + * Snyk, GitHub, Prisma Cloud all rely on internal notions of suppression and policy without emitting a standardized VEX or equivalent artifact. ([Snyk][26]) + * For StellaOps: your “Proof-of-Integrity Graph” and VEX exports should be the primary way vendors and customers communicate risk, not just internal UI states. + +4. **Over-reliance on informal text for serious decisions** + + * Many tools lean heavily on free-form comments (GitHub dismissal comments, Snyk ignore reasons) without tying them to structured policies or verifiable upstream evidence. ([GitHub Docs][22]) + * For StellaOps: keep free text, but anchor it in structured fields (VEX status, justification category, linked upstream VEX/SBOM, ticket ID) and make these queryable across the entire system. + +If you want, next step can be a **StellaOps UX spec** that maps each of these rows to concrete screens and micro-interactions (e.g., “Finding details drawer,” “Suppression dialog,” “Export / attest dialog”) so your .NET/Angular team has something they can implement directly. + +[1]: https://docs.snyk.io/scan-with-snyk/snyk-code/manage-code-vulnerabilities/breakdown-of-code-analysis?utm_source=chatgpt.com "Breakdown of Code analysis" +[2]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/about-code-scanning-alerts?utm_source=chatgpt.com "About code scanning alerts" +[3]: https://trivy.dev/docs/v0.56/scanner/misconfiguration/?utm_source=chatgpt.com "Misconfiguration Scanning" +[4]: https://edu.chainguard.dev/chainguard/chainguard-images/staying-secure/working-with-scanners/grype-tutorial/?utm_source=chatgpt.com "Using Grype to Scan Software Artifacts" +[5]: https://www.paloaltonetworks.com/prisma/cloud/cloud-code-security?utm_source=chatgpt.com "Cloud Code Security" +[6]: https://docs.snyk.io/snyk-api/api-endpoints-index-and-tips/project-issue-paths-api-endpoints?utm_source=chatgpt.com "Project issue paths API endpoints" +[7]: https://docs.github.com/en/code-security/dependabot/dependabot-alerts/viewing-and-updating-dependabot-alerts?utm_source=chatgpt.com "Viewing and updating Dependabot alerts" +[8]: https://github.com/aquasecurity/trivy?utm_source=chatgpt.com "aquasecurity/trivy: Find vulnerabilities, misconfigurations, ..." +[9]: https://docs.prismacloud.io/en/compute-edition/32/admin-guide/vulnerability-management/scan-reports?utm_source=chatgpt.com "Vulnerability Scan Reports - Prisma Cloud Documentation" +[10]: https://docs.snyk.io/developer-tools/snyk-cli/commands/sbom?utm_source=chatgpt.com "SBOM" +[11]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning?utm_source=chatgpt.com "SARIF support for code scanning" +[12]: https://edu.chainguard.dev/chainguard/chainguard-images/staying-secure/working-with-scanners/trivy-tutorial/?utm_source=chatgpt.com "Using Trivy to Scan Software Artifacts" +[13]: https://docs.anchore.com/current/docs/?utm_source=chatgpt.com "Anchore Enterprise Documentation" +[14]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues/consistent-ignores-for-snyk-code?utm_source=chatgpt.com "Consistent Ignores for Snyk Code" +[15]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/assessing-code-scanning-alerts-for-your-repository?utm_source=chatgpt.com "Assessing code scanning alerts for your repository" +[16]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/runtime-security/vulnerability-management/vulnerability-explorer?utm_source=chatgpt.com "Vulnerability Explorer - Prisma Cloud Documentation" +[17]: https://docs.snyk.io/developer-tools/snyk-cli/commands/ignore?utm_source=chatgpt.com "Ignore | Snyk User Docs" +[18]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/resolving-code-scanning-alerts?utm_source=chatgpt.com "Resolving code scanning alerts" +[19]: https://aquasecurity.github.io/trivy-operator/v0.13.2/docs/vulnerability-scanning/trivy/?utm_source=chatgpt.com "Trivy Scanner - Trivy Operator - Aqua Security" +[20]: https://github.com/anchore/grype?utm_source=chatgpt.com "anchore/grype: A vulnerability scanner for container ..." +[21]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/application-security/risk-management/monitor-and-manage-code-build/suppress-code-issues?utm_source=chatgpt.com "Suppress Code Issues - Prisma Cloud Documentation" +[22]: https://docs.github.com/en/rest/code-scanning/code-scanning?utm_source=chatgpt.com "REST API endpoints for code scanning" +[23]: https://docs.anchore.com/current/docs/vulnerability_management/vuln_annotations/?utm_source=chatgpt.com "Vulnerability Annotations and VEX" +[24]: https://pan.dev/prisma-cloud/api/code/get-suppressions-justifications/?utm_source=chatgpt.com "Get Suppressions Justifications by Policy ID and Query ..." +[25]: https://snyk.io/blog/ignoring-vulnerabilities-with-snyk/?utm_source=chatgpt.com "Ignoring vulnerabilities with Snyk" +[26]: https://snyk.io/blog/advancing-sbom-standards-snyk-spdx/?utm_source=chatgpt.com "Advancing SBOM standards: Snyk and SPDX" +[27]: https://trivy.dev/docs/v0.50/supply-chain/vex/?utm_source=chatgpt.com "VEX" +[28]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues?utm_source=chatgpt.com "Ignore issues | Snyk User Docs" +[29]: https://docs.github.com/en/code-security/dependabot/dependabot-auto-triage-rules/managing-automatically-dismissed-alerts?utm_source=chatgpt.com "Managing automatically dismissed alerts - Dependabot" +[30]: https://artifacthub.io/packages/container/grype/grype?utm_source=chatgpt.com "grype latest · wagoodman@gmail.com/grype" +[31]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues/exclude-files-and-ignore-issues-faqs?utm_source=chatgpt.com "Exclude files and ignore issues FAQs" +[32]: https://github.com/aquasecurity/trivy/discussions/7974?utm_source=chatgpt.com ".trivyignore.yaml not allow to ignore multiple vulnerabilities ..." +[33]: https://github.com/anchore/grype/issues/2471?utm_source=chatgpt.com "Support using VEX documents with directory scans and ..." +[34]: https://docs.snyk.io/manage-risk/reporting?utm_source=chatgpt.com "Reporting | Snyk User Docs" +[35]: https://docs.github.com/en/code-security/security-overview/filtering-alerts-in-security-overview?utm_source=chatgpt.com "Filtering alerts in security overview" +[36]: https://www.aquasec.com/blog/container-vulnerability-management-best-practices/?utm_source=chatgpt.com "Cloud Native Security Best Practices: Vulnerability Management" +[37]: https://docs.anchore.com/current/docs/sbom_management/?utm_source=chatgpt.com "SBOM Management" +[38]: https://docs.snyk.io/snyk-api/using-specific-snyk-apis/issues-list-issues-for-a-package?utm_source=chatgpt.com "Issues: List issues for a package" +[39]: https://trivy.dev/docs/latest/configuration/reporting/?utm_source=chatgpt.com "Reporting" +[40]: https://pan.dev/prisma-cloud/api/cspm/vulnerabilities-search-api/?utm_source=chatgpt.com "Get Vulnerabilities by RQL | Develop with Palo Alto Networks" +[41]: https://docs.anchore.com/current/docs/release_notes/enterprise/5220/?utm_source=chatgpt.com "Anchore Enterprise Release Notes - Version 5.22.0" +[42]: https://docs.snyk.io/manage-risk/reporting/available-snyk-reports?utm_source=chatgpt.com "Available Snyk reports | Snyk User Docs" +[43]: https://docs.github.com/en/enterprise-cloud%40latest/rest/code-scanning/alert-dismissal-requests?utm_source=chatgpt.com "REST API endpoints for code scanning alert dismissal ..." +[44]: https://updates.snyk.io/export-api-ga-release-321290/?utm_source=chatgpt.com "Export API GA Release" +[45]: https://securityboulevard.com/2025/11/anchore-enterprise-5-23-cyclonedx-vex-and-vdr-support/?utm_source=chatgpt.com "Anchore Enterprise 5.23: CycloneDX VEX and VDR Support" +[46]: https://docs.snyk.io/manage-risk/policies/the-.snyk-file?utm_source=chatgpt.com "The .snyk file | Snyk User Docs" +Matrix built. I’ll keep it compact enough to scan quickly, but detailed enough that you can lift it straight into a Google Sheet / Confluence page. + +--- + +## 1. Evidence & context: what a *single finding* looks like + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Code-level evidence** | Snyk Code shows a *Data flow* view (taint graph) from source → sink with step-by-step code lines in the UI and IDE. ([docs.snyk.io][1]) | Code scanning alerts show file/line and, for data-flow rules, a “how data moves through the code” view with **Show paths** for the full flow. ([GitHub Docs][2]) | Trivy is not a SAST engine; evidence is per-file / resource for vulnerabilities & misconfigurations (IaC, K8s, etc.), not data-flow graphs. ([Trivy][3]) | No SAST; evidence is package / image / SBOM level (CVE, package, version, location). Grype is focused on images/filesystems, not code flows. ([Chainguard Academy][4]) | AppSec module shows issues mapped to file & line and offers inline remediation/fix flows; surfaced in console and IDE plugins (JetBrains/VS Code). ([Palo Alto Networks][5]) | +| **Dependency / container evidence** | For OSS & containers, Snyk shows dependency paths and has a **project issue paths** API that returns the introduction chain for a vuln. ([docs.snyk.io][6]) | Dependabot alerts show vulnerable package, version, advisories, and affected functions to help decide if code actually uses them. ([GitHub Docs][7]) | Trivy shows image → layer → package → vuln with installed/fixed versions; same for repos & filesystems in JSON/SARIF. ([GitHub][8]) | Grype output lists artifact, package, version, location, severity; when paired with Syft SBOM, you get full component graph for images and other artifacts. ([Chainguard Academy][4]) | Prisma’s Vulnerability Explorer & scan reports show image/host, package, severity and compliance IDs for image and host scans. ([docs.prismacloud.io][9]) | +| **SBOM / supply-chain context** | `snyk sbom` generates CycloneDX and SPDX SBOMs for local projects; Snyk also supports SBOM export in enterprise tiers. ([docs.snyk.io][10]) | GitHub builds a dependency graph from manifests and ingests SARIF from external scanners; it is SBOM-adjacent but not an SBOM UI. ([GitHub Docs][11]) | Trivy can output SBOMs (CycloneDX, SPDX, SPDX-JSON) and Kubernetes “KBOM” views, and is often used as the SBOM generator. ([Chainguard Academy][12]) | Anchore Enterprise is explicitly “SBOM-powered”; SBOM management views let you navigate applications and export SBOM-centric vulnerability views. ([docs.anchore.com][13]) | Prisma Cloud is inventory-/RQL-centric; SBOMs are not a first-class UI artifact and there is no public doc for full SBOM navigation comparable to Anchore or Trivy. ([Palo Alto Networks][5]) | +| **Where the evidence appears** | Web UI, CLI, IDEs (VS Code, JetBrains) and PR checks. Ignores for Snyk Code are now “consistent” across UI/CLI/IDE (2025-11 EA). ([docs.snyk.io][14]) | Web UI (Security tab), PR Conversation tab for code scanning reviews, REST & GraphQL APIs, and SARIF imports from many scanners. ([GitHub Docs][15]) | Trivy: CLI / JSON / SARIF / CI integrations (GitHub Actions, etc.). Aqua Platform adds a CNAPP UI with risk-based vulnerability views. ([GitHub][8]) | Grype: CLI & JSON/SARIF outputs; Anchore Enterprise: browser UI (dashboards, SBOM views, reporting) plus REST/GraphQL APIs. ([Chainguard Academy][4]) | Prisma Cloud: web console (AppSec dashboards, Vulnerability Explorer), REST APIs, IDE plugins for JetBrains & VS Code. ([docs.prismacloud.io][16]) | + +--- + +## 2. Suppression / ignore / VEX behavior + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Suppression primitives** | `.snyk` policy file for OSS & container & IaC; `snyk ignore` CLI; “Ignore” in web UI; Snyk Code uses UI/CLI/IDE ignores but *not* `.snyk`. ([docs.snyk.io][17]) | Code scanning and Dependabot alerts can be **dismissed** with reasons; Dependabot has auto-triage rules (auto-dismiss/snooze). ([GitHub Docs][18]) | Trivy: `.trivyignore` / `.trivyignore.yaml` lists vuln IDs; `--ignore-policy` for Rego-based filtering; severity filters. ([aquasecurity.github.io][19]) | Grype: VEX-aware filtering via `--vex` and ignore rules; Anchore Enterprise: UI-driven vulnerability annotations that act as a structured “suppress or re-classify” layer. ([GitHub][20]) | AppSec: “Suppress Code Issues” (per-issue or globally) and more general suppression rules (disable policy / by source / by resource / by tag). ([docs.prismacloud.io][21]) | +| **Metadata captured with suppression** | `snyk ignore` supports `--reason` and `--expiry`; UI shows who ignored and allows editing/unignore; APIs expose `ignoreReasons` on issues. ([docs.snyk.io][17]) | Dismissal reason: `false positive`, `won't fix`, `used in tests`; optional comment. Dismissal requests for code scanning add requester/reviewer, timestamps, status. ([GitHub Docs][22]) | Ignores live in repo-local files (`.trivyignore*`) or Rego policies; VEX documents contribute status metadata (per CVE, per product). No central UI metadata by default. ([aquasecurity.github.io][19]) | Annotations carry VEX-aligned status plus explanatory text; RBAC role `vuln-annotator-editor` controls who can create/modify/delete them. ([docs.anchore.com][23]) | Suppressions include justification text; there is a dedicated API to retrieve suppression justifications by policy ID and query (auditable). ([pan.dev][24]) | +| **Scope & consistency** | Ignores are *local* to project/integration/org; ignoring in a Git repo project does not automatically ignore the same issue imported via CLI. Snyk Code ignore semantics differ from OSS/Container (no `.snyk`). ([Snyk][25]) | Dismissals attach to a specific alert (repo, branch baseline); PRs inherit baseline. Dependabot has repo-level config (`dependabot.yml`) and org-level alert configuration. ([GitHub Docs][7]) | Suppression is run-local: `.trivyignore` travels with source, policies live in config repos; VEX docs are attached per artifact or discovered via VEX Hub / registries. Reproducible but not centralized unless you build it. ([aquasecurity.github.io][19]) | Grype suppression is per scan & VEX document; Anchore Enterprise stores annotations centrally per artifact and exposes them consistently in UI/API and exported VEX. ([GitHub][20]) | Suppression rules can be global, per source, per resource, or tag-scoped; they affect console views and API results consistently when filters are applied. ([pan.dev][24]) | +| **VEX support (ingest + semantics)** | Snyk generates SBOMs but has no documented first-class VEX ingestion or export; VEX is referenced mostly at the SBOM/standards level, not as a product feature. ([Snyk][26]) | No native VEX semantics in alerts; GitHub consumes SARIF only. VEX can exist in repos or external systems but is not interpreted by GHAS. ([GitHub Docs][11]) | Trivy has **explicit VEX support**: supports OpenVEX, CycloneDX VEX, and CSAF VEX, using local files, VEX repositories, SBOM externalReferences, and OCI VEX attestations to filter results. ([Trivy][27]) | Grype has VEX-first behavior: `--vex` to ingest OpenVEX; by default `not_affected`/`fixed` go to an ignore set; `--show-suppressed` exposes them. Anchore Enterprise 5.22+ adds OpenVEX export and CycloneDX VEX export (5.23). ([GitHub][20]) | No public documentation of native VEX ingestion or export; prioritization is via policies/RQL rather than VEX semantics. | +| **Default handling of suppressed items** | Ignored issues are hidden by default in UI/CLI/IDE views to reduce noise; some tools like `snyk-to-html` will still show ignored Snyk Code issues. ([docs.snyk.io][28]) | Dismissed / auto-dismissed alerts are *not* shown by default (`is:open` filter); you must explicitly filter for closed / auto-dismissed alerts. ([GitHub Docs][29]) | When VEX is supplied, non-affected/fixed statuses are filtered out; suppressed entries are generally not shown unless you adjust filters/output. Some edge-cases exist around `.trivyignore.yaml` behavior. ([Trivy][27]) | Grype hides suppressed matches unless `--show-suppressed` is set; community discussions explicitly argue for “suppressed hidden by default” across all formats. ([artifacthub.io][30]) | Suppressed / excluded issues are hidden from normal risk views and searches and only visible via explicit filters or suppression APIs. ([pan.dev][24]) | +| **Known pain points** | Fragmented semantics: Snyk Code vs OSS/Container vs IaC handle ignores differently; ignores are not global across ingest types. ([docs.snyk.io][31]) | Dismissal is per-tool: Dependabot rules don’t govern CodeQL and vice-versa; no VEX; you must build cross-tool policy on top. | Some rough edges reported around `.trivyignore.yaml` and SARIF vs exit-code behavior; VEX support is powerful but still labeled experimental in docs. ([GitHub][32]) | VEX support for certain source types (e.g., some directory + SBOM combos) has had bugs; suppressed entries disappear by default unless you remember `--show-suppressed`. ([GitHub][33]) | No VEX, and suppression semantics may diverge across CSPM/AppSec/Compute; complexity of RQL and multiple modules can make suppression strategy opaque without good governance. ([Palo Alto Networks][5]) | + +--- + +## 3. Audit & export primitives + +| Dimension | Snyk | GitHub Advanced Security | Aqua / Trivy | Anchore / Grype (Anchore Enterprise) | Prisma Cloud | +| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Built-in UI exports** | Reporting UI lets you export reports as PDF and table data as CSV; you can also download CSV from several report views. ([docs.snyk.io][34]) | UI is mostly “view only”; some security overview exports are available, but reports are usually built via API or marketplace actions (e.g., PDF report GitHub Action). ([GitHub Docs][35]) | Aqua Platform provides CSV exports and API integrations for vulnerability reports; on-prem deployments can export “user policy data, vulnerability data and system settings data.” ([Aqua][36]) | SBOM/Vulnerability views have an **Export CSV** button; the Reports service aggregates via GraphQL into CSV/JSON from pre-built templates and dashboards. ([docs.anchore.com][37]) | Vulnerability Explorer and scan reports allow CSV export; code-build issues can be exported with filters as CSV from the AppSec UI. ([docs.prismacloud.io][16]) | +| **Machine-readable exports / APIs** | REST Issues API; Export API (GA) to create CSV exports via async jobs; SBOM CLI (`snyk sbom`) for CycloneDX/SPDX; audit log export for user actions. ([docs.snyk.io][38]) | SARIF input/output; REST & GraphQL APIs for code scanning & Dependabot; many third-party actions to transform alerts into PDFs/CSVs or send to other systems. ([GitHub Docs][11]) | Trivy outputs JSON, SARIF, CycloneDX, SPDX, SPDX-JSON, GitHub formats; these can be uploaded into GHAS or other platforms. Aqua exposes vulnerability data via API. ([Trivy][39]) | Grype outputs JSON, table, CycloneDX, SARIF, plus Go-template based CSV; Anchore Enterprise exposes APIs for vulnerabilities, policies, and reports. ([Chainguard Academy][4]) | CSPM/AppSec APIs: `vulnerabilities/search` (JSON) and `.../search/download` (GZIP CSV) for RQL queries; broader Prisma Cloud API for automation/integration. ([pan.dev][40]) | +| **VEX / “authoritative statement” export** | No native VEX export; at best you can export SBOMs and issues and post-process into VEX with external tooling. ([Snyk][26]) | None. Vendors can publish VEX separately, but GHAS doesn’t emit VEX. | Trivy consumes VEX but does not itself author VEX; Aqua operates VEX Hub as a central repository of vendor VEX documents rather than as a generator. ([Trivy][27]) | Anchore Enterprise 5.22/5.23 is currently the most “productized” VEX exporter: OpenVEX and CycloneDX VEX including annotations, PURLs, metadata about the scanned image. ([docs.anchore.com][41]) | No VEX export; risk posture is expressed via internal policies, not standardized exploitability statements. | +| **Suppression / ignore audit** | Ignored issues can be exported by filtering for “Ignored” in Reporting and downloading CSV; APIs expose ignore reasons; audit log export records user actions such as ignoring issues. ([docs.snyk.io][42]) | Code scanning dismissal requests are full audit records (who requested, who approved, reason, timestamps). Dependabot dismissal comments & auto-dismissed alerts are queryable via GraphQL/REST and UI filters. ([GitHub Docs][43]) | Auditability is repo-driven: `.trivyignore`/VEX/rego live in version control; Aqua’s own reporting/APIs can include current vulnerability state but do not (publicly) expose a dedicated VEX change log. ([aquasecurity.github.io][19]) | Annotations are first-class objects, controlled via RBAC and exportable as OpenVEX/CycloneDX VEX; this effectively *is* an audit log of “why we suppressed or downgraded this CVE” over time. ([docs.anchore.com][23]) | Suppression justification APIs provide a structured record of why and where policies are suppressed; combined with CSV/API exports of vulns, this gives a workable audit trail for regulators. ([pan.dev][24]) | + +--- + +## 4. What this suggests for StellaOps UX & data-model + +### Patterns worth copying + +1. **Data-flow & path-level “evidence” as the default detail view** + + * Snyk Code’s taint-flow UI and GitHub’s “Show paths” for CodeQL alerts make a single issue *explain itself* without leaving the tool. ([docs.snyk.io][1]) + * For StellaOps: + + * For code: provide a path graph (source → sanitizers → sinks) with line-level jumps. + * For SBOM/containers: mirror Snyk’s Project Issue Paths & Grype/Syft by showing a dependency path or image-layer path for each CVE. + +2. **VEX-first suppression semantics** + + * Trivy and Grype treat VEX as the *canonical* suppression mechanism; statuses like `not_affected` / `fixed` hide findings by default, with CLI flags to show suppressed. ([Trivy][27]) + * Anchore Enterprise adds a full annotation workflow and emits OpenVEX/CycloneDX VEX as the authoritative vendor statement. ([docs.anchore.com][23]) + * For StellaOps: make VEX status the **single source of truth** for suppression across all modules, and always be able to reconstruct a proof tree: SBOM → vuln → VEX statement → policy decision. + +3. **Rich suppression metadata with explicit justification & expiry** + + * Snyk captures reason + expiry; GitHub uses structured reasons + free-form comment; Prisma provides a dedicated suppression-justification API. ([docs.snyk.io][17]) + * For StellaOps: require a reason taxonomy + free-text + optional “proof link” (e.g., internal ticket, upstream vendor VEX) for any “Not Affected” or “Won’t Fix,” and store that as part of the public proof spine. + +4. **Strong export & API stories for regulators** + + * Snyk’s Export API, Anchore’s Reports service, and Prisma’s RQL+CSV APIs give customers reliable machine-readable exports for audits and custom BI. ([updates.snyk.io][44]) + * Anchore’s CycloneDX VEX export is the best current model for “signable, portable, regulator-friendly” proofs. ([Security Boulevard][45]) + * For StellaOps: + + * Treat CSV/JSON/SBOM/VEX exports as *first-class* features, not add-ons. + * Tie them to deterministic scan manifests and graph revision IDs (your “trust algebra” backbone) so an auditor can replay any historical report. + +5. **Repo-local suppression files as “policy as code”** + + * `.snyk`, `.trivyignore`, Rego policies, and VEX docs living alongside source are proven patterns for reproducible suppression. ([docs.snyk.io][46]) + * For StellaOps: mirror this with a small set of declarative files (`stellaops.vexmap.yaml`, `trust-lattice.yaml`) that your engine and UI both honor. + +### Patterns to deliberately avoid + +1. **Fragmented ignore semantics by scan type** + + * Snyk’s different behavior across Code vs OSS vs Container vs IaC (e.g., `.snyk` not honored for Snyk Code) is a recurring source of confusion. ([docs.snyk.io][31]) + * For StellaOps: a single suppression model (VEX+policy) must apply consistently whether the evidence came from SAST, SBOM, container scan, or runtime telemetry. + +2. **Making suppressed issues effectively invisible** + + * Grype and GitHub both hide suppressed/dismissed issues by default and require extra flags/filters to see them. ([artifacthub.io][30]) + * For StellaOps: + + * Default UI for any artifact should show a small, always-visible “suppressed” counter with a click-through view. + * Every export format should have an option to include suppressed entries with their VEX status and reasoning. + +3. **Lack of a standard “authoritative statement” artifact** + + * Snyk, GitHub, Prisma Cloud all rely on internal notions of suppression and policy without emitting a standardized VEX or equivalent artifact. ([Snyk][26]) + * For StellaOps: your “Proof-of-Integrity Graph” and VEX exports should be the primary way vendors and customers communicate risk, not just internal UI states. + +4. **Over-reliance on informal text for serious decisions** + + * Many tools lean heavily on free-form comments (GitHub dismissal comments, Snyk ignore reasons) without tying them to structured policies or verifiable upstream evidence. ([GitHub Docs][22]) + * For StellaOps: keep free text, but anchor it in structured fields (VEX status, justification category, linked upstream VEX/SBOM, ticket ID) and make these queryable across the entire system. + +If you want, next step can be a **StellaOps UX spec** that maps each of these rows to concrete screens and micro-interactions (e.g., “Finding details drawer,” “Suppression dialog,” “Export / attest dialog”) so your .NET/Angular team has something they can implement directly. + +[1]: https://docs.snyk.io/scan-with-snyk/snyk-code/manage-code-vulnerabilities/breakdown-of-code-analysis?utm_source=chatgpt.com "Breakdown of Code analysis" +[2]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/about-code-scanning-alerts?utm_source=chatgpt.com "About code scanning alerts" +[3]: https://trivy.dev/docs/v0.56/scanner/misconfiguration/?utm_source=chatgpt.com "Misconfiguration Scanning" +[4]: https://edu.chainguard.dev/chainguard/chainguard-images/staying-secure/working-with-scanners/grype-tutorial/?utm_source=chatgpt.com "Using Grype to Scan Software Artifacts" +[5]: https://www.paloaltonetworks.com/prisma/cloud/cloud-code-security?utm_source=chatgpt.com "Cloud Code Security" +[6]: https://docs.snyk.io/snyk-api/api-endpoints-index-and-tips/project-issue-paths-api-endpoints?utm_source=chatgpt.com "Project issue paths API endpoints" +[7]: https://docs.github.com/en/code-security/dependabot/dependabot-alerts/viewing-and-updating-dependabot-alerts?utm_source=chatgpt.com "Viewing and updating Dependabot alerts" +[8]: https://github.com/aquasecurity/trivy?utm_source=chatgpt.com "aquasecurity/trivy: Find vulnerabilities, misconfigurations, ..." +[9]: https://docs.prismacloud.io/en/compute-edition/32/admin-guide/vulnerability-management/scan-reports?utm_source=chatgpt.com "Vulnerability Scan Reports - Prisma Cloud Documentation" +[10]: https://docs.snyk.io/developer-tools/snyk-cli/commands/sbom?utm_source=chatgpt.com "SBOM" +[11]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning?utm_source=chatgpt.com "SARIF support for code scanning" +[12]: https://edu.chainguard.dev/chainguard/chainguard-images/staying-secure/working-with-scanners/trivy-tutorial/?utm_source=chatgpt.com "Using Trivy to Scan Software Artifacts" +[13]: https://docs.anchore.com/current/docs/?utm_source=chatgpt.com "Anchore Enterprise Documentation" +[14]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues/consistent-ignores-for-snyk-code?utm_source=chatgpt.com "Consistent Ignores for Snyk Code" +[15]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/assessing-code-scanning-alerts-for-your-repository?utm_source=chatgpt.com "Assessing code scanning alerts for your repository" +[16]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/runtime-security/vulnerability-management/vulnerability-explorer?utm_source=chatgpt.com "Vulnerability Explorer - Prisma Cloud Documentation" +[17]: https://docs.snyk.io/developer-tools/snyk-cli/commands/ignore?utm_source=chatgpt.com "Ignore | Snyk User Docs" +[18]: https://docs.github.com/en/code-security/code-scanning/managing-code-scanning-alerts/resolving-code-scanning-alerts?utm_source=chatgpt.com "Resolving code scanning alerts" +[19]: https://aquasecurity.github.io/trivy-operator/v0.13.2/docs/vulnerability-scanning/trivy/?utm_source=chatgpt.com "Trivy Scanner - Trivy Operator - Aqua Security" +[20]: https://github.com/anchore/grype?utm_source=chatgpt.com "anchore/grype: A vulnerability scanner for container ..." +[21]: https://docs.prismacloud.io/en/enterprise-edition/content-collections/application-security/risk-management/monitor-and-manage-code-build/suppress-code-issues?utm_source=chatgpt.com "Suppress Code Issues - Prisma Cloud Documentation" +[22]: https://docs.github.com/en/rest/code-scanning/code-scanning?utm_source=chatgpt.com "REST API endpoints for code scanning" +[23]: https://docs.anchore.com/current/docs/vulnerability_management/vuln_annotations/?utm_source=chatgpt.com "Vulnerability Annotations and VEX" +[24]: https://pan.dev/prisma-cloud/api/code/get-suppressions-justifications/?utm_source=chatgpt.com "Get Suppressions Justifications by Policy ID and Query ..." +[25]: https://snyk.io/blog/ignoring-vulnerabilities-with-snyk/?utm_source=chatgpt.com "Ignoring vulnerabilities with Snyk" +[26]: https://snyk.io/blog/advancing-sbom-standards-snyk-spdx/?utm_source=chatgpt.com "Advancing SBOM standards: Snyk and SPDX" +[27]: https://trivy.dev/docs/v0.50/supply-chain/vex/?utm_source=chatgpt.com "VEX" +[28]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues?utm_source=chatgpt.com "Ignore issues | Snyk User Docs" +[29]: https://docs.github.com/en/code-security/dependabot/dependabot-auto-triage-rules/managing-automatically-dismissed-alerts?utm_source=chatgpt.com "Managing automatically dismissed alerts - Dependabot" +[30]: https://artifacthub.io/packages/container/grype/grype?utm_source=chatgpt.com "grype latest · wagoodman@gmail.com/grype" +[31]: https://docs.snyk.io/manage-risk/prioritize-issues-for-fixing/ignore-issues/exclude-files-and-ignore-issues-faqs?utm_source=chatgpt.com "Exclude files and ignore issues FAQs" +[32]: https://github.com/aquasecurity/trivy/discussions/7974?utm_source=chatgpt.com ".trivyignore.yaml not allow to ignore multiple vulnerabilities ..." +[33]: https://github.com/anchore/grype/issues/2471?utm_source=chatgpt.com "Support using VEX documents with directory scans and ..." +[34]: https://docs.snyk.io/manage-risk/reporting?utm_source=chatgpt.com "Reporting | Snyk User Docs" +[35]: https://docs.github.com/en/code-security/security-overview/filtering-alerts-in-security-overview?utm_source=chatgpt.com "Filtering alerts in security overview" +[36]: https://www.aquasec.com/blog/container-vulnerability-management-best-practices/?utm_source=chatgpt.com "Cloud Native Security Best Practices: Vulnerability Management" +[37]: https://docs.anchore.com/current/docs/sbom_management/?utm_source=chatgpt.com "SBOM Management" +[38]: https://docs.snyk.io/snyk-api/using-specific-snyk-apis/issues-list-issues-for-a-package?utm_source=chatgpt.com "Issues: List issues for a package" +[39]: https://trivy.dev/docs/latest/configuration/reporting/?utm_source=chatgpt.com "Reporting" +[40]: https://pan.dev/prisma-cloud/api/cspm/vulnerabilities-search-api/?utm_source=chatgpt.com "Get Vulnerabilities by RQL | Develop with Palo Alto Networks" +[41]: https://docs.anchore.com/current/docs/release_notes/enterprise/5220/?utm_source=chatgpt.com "Anchore Enterprise Release Notes - Version 5.22.0" +[42]: https://docs.snyk.io/manage-risk/reporting/available-snyk-reports?utm_source=chatgpt.com "Available Snyk reports | Snyk User Docs" +[43]: https://docs.github.com/en/enterprise-cloud%40latest/rest/code-scanning/alert-dismissal-requests?utm_source=chatgpt.com "REST API endpoints for code scanning alert dismissal ..." +[44]: https://updates.snyk.io/export-api-ga-release-321290/?utm_source=chatgpt.com "Export API GA Release" +[45]: https://securityboulevard.com/2025/11/anchore-enterprise-5-23-cyclonedx-vex-and-vdr-support/?utm_source=chatgpt.com "Anchore Enterprise 5.23: CycloneDX VEX and VDR Support" +[46]: https://docs.snyk.io/manage-risk/policies/the-.snyk-file?utm_source=chatgpt.com "The .snyk file | Snyk User Docs" + +I’ll treat this as a concrete UX blueprint your .NET/Angular team can build against, centered on three pillars: + +1. Evidence views (how a single finding “explains itself”) +2. Suppression/VEX (how risk decisions are captured) +3. Audit/export (how you prove those decisions later) + +--- + +## 1. Design principles specific to StellaOps + +1. **Graph-first:** Every view is anchored to a graph snapshot ID from your trust algebra / proof engine. Users always know “which build / which graph revision” they’re looking at. +2. **Single suppression model:** One suppression semantics layer (VEX + policy) across code, SBOM, containers, and runtime. +3. **Evidence before opinion:** Detail views always show raw evidence (paths, SBOM lines, runtime traces) before risk ratings, policies, or comments. +4. **Audit-ready by default:** Any action that changes risk posture automatically leaves a machine-readable trail that can be exported. + +--- + +## 2. Core information architecture + +Primary entities in the UX (mirroring your backend): + +* **Organization / Workspace** +* **Application / Service** +* **Artifact** + + * Build (commit, tag, CI run) + * Container image + * Binary / package + * SBOM document +* **Scan Result** + + * Scanner type (SAST, SCA, container, SBOM-only, IaC, runtime) + * Graph snapshot ID +* **Finding** + + * Vulnerability (CVE, GHSA, etc.) + * Misconfiguration / policy violation + * Supply-chain anomaly +* **Evidence** + + * Code path(s) + * Dependency path(s) + * SBOM node(s) + * Runtime event(s) +* **VEX Statement** + + * Status (`affected`, `not_affected`, `fixed`, `under_investigation`, etc.) + * Justification (standardized + free text) +* **Suppression Decision** + + * “Suppressed by VEX”, “De-prioritized by policy”, “Won’t fix”, etc. +* **Export Job / Proof Bundle** +* **Audit Event** + + * Who, what, when, against which graph snapshot + +The UI should surface these without drowning the user in jargon: names can be friendlier but map cleanly to this model. + +--- + +## 3. UX spec by primary flows + +### 3.1 Landing: Inventory & posture overview + +**Screen: “Inventory & Risk Overview”** + +Purpose: Secure default entry point for security engineers and auditors. + +Layout: + +* **Header bar** + + * Org selector + * Global search (by application, artifact, CVE, VEX ID, SBOM component) + * “Exports & Proofs” shortcut + * User menu + +* **Left nav (primary)** + + * Inventory + * Findings + * VEX & Policies + * Exports & Proofs + * Settings + +* **Main content: 3 panels** + + 1. **Applications list** + + * Columns: Name, Owner, #Artifacts, Open findings, Suppressed findings, Last build, Risk score. + * Row click → Application view. + 2. **Top risks** + + * Cards: “Critical vulns”, “Unsigned artifacts”, “Unknown SBOM coverage”, each showing counts and a “View findings” link (pre-filtered). + 3. **Compliance viewpoint** + + * Tabs: “Operational”, “Regulatory”, “Attestations”. + * Simple status indicators per framework (e.g., SLSA level, NIST SSDF coverage) if you support that. + +Micro-interactions: + +* Clicking any numeric count (e.g., “12 suppressed”) opens a pre-filtered Findings list. +* Hover tooltip on risk counts shows breakdown by source (SAST vs SBOM vs runtime). + +--- + +### 3.2 Application & artifact detail: evidence-centric view + +**Screen: “Application detail”** + +Purpose: Navigate from high level risk to specific artifacts and findings. + +Layout: + +* Tabs: Overview | Artifacts | Findings | SBOM graph | History + +**Overview tab:** + +* Top: Application summary (owners, repos, deployment targets). +* Middle: Tiles for “Latest build”, “Prod artifacts”, “SBOM coverage”. +* Bottom: “Recent decisions” timeline (suppression, VEX updates, exports). + +**Artifacts tab:** + +* Table with: + + * Artifact type (image, binary, SBOM-only) + * Identifier (tag/digest, CI job ID) + * Environment (dev, staging, prod) + * Graph snapshot ID + * Findings (open / suppressed chips) + * Last scanned + +Selecting a row opens **Artifact detail**. + +--- + +### 3.3 Artifact detail & Finding details drawer + +**Screen: “Artifact detail”** + +Layout: + +* Header: + + * Artifact identity (name, version/tag, digest) + * Environment badges + * Graph snapshot ID with “Copy” control + * Primary actions: “Download SBOM”, “Export proof bundle” + +* Sub-tabs: + + * Findings + * Evidence graph + * SBOM tree + * History + +**Findings sub-tab** + +* Filters on left: + + * Severity, status (Open / Suppressed / Only suppressed), source (SAST/SBOM/container/runtime), VEX status, CWE/CVE. +* Table in center: + + * Columns: Severity, Type, ID (CVE-XXXX-XXXX), Component/Code location, Status (Open/Suppressed), VEX status, Last updated. +* Row click opens a **Finding details drawer** sliding from the right. + +**Finding details drawer (core evidence UX)** + +This is the primary place where “evidence explains itself.” + +Structure: + +1. **Header area** + + * Title: “CVE-2023-XXXX in `openssl`” + * Badges: severity, status (Open / Suppressed by VEX), VEX status (`not_affected`, `fixed`, etc.) + * Scope indicator: where this finding appears (e.g., “1 artifact (this), 3 environments, 2 images”). + +2. **Tabs within drawer** + + * **Evidence** (default) + + * Section: “Where this comes from” + + * Scanner source, scan timestamp, graph snapshot ID. + * Section: “Code path” (for SAST) + + * A vertical list of steps: `source` → `propagation` → `sink`. + * Clicking a step shows code snippet with line highlighting. + * “Open in Git” and “Open in IDE (deeplink)” actions. + * Section: “Dependency path” (for SCA / SBOM) + + * Path view: `app -> module -> transitive dependency -> vulnerable package`. + * Option to switch between “tree view” and “collapsed chain”. + * Section: “Runtime signals” (if available) + + * Call-site frequency, exploit attempts, telemetry tags. + + * **Impact & reach** + + * A small graph panel: how many artifacts, environments, and applications share this finding. + * Link: “View all affected artifacts” → filtered Findings view. + + * **VEX & decisions** + + * Shows current VEX statements attached: + + * Table: Status, Justification (standardized), Source (vendor vs internal), Effective scope (artifact / app / org), Last changed by. + * Shows local suppression decisions beyond VEX (e.g., “De-prioritized due to compensating controls”). + * Primary action: “Update status / Add VEX decision”. + + * **History** + + * Timeline of events for this finding: + + * Detected + * VEX attached/updated + * Suppression added/edited + * Exported in bundles (with links) + * Each item shows user, timestamp, and delta. + +Micro-interactions: + +* The drawer should support deep-linking: URL reflects `artifactId + findingId + snapshotId`. +* Keyboard: `Esc` closes drawer, `←` / `→` cycles findings in the table. + +--- + +## 4. Suppression & VEX UX + +Goal: one consistent model across all scan types, anchored in VEX semantics. + +### 4.1 “Update status / Add VEX decision” dialog + +Entry points: + +* From Finding details drawer > “VEX & decisions” tab > primary button. +* Bulk selection from Findings table > “Update status”. + +Dialog steps (single modal with progressive disclosure): + +1. **Select decision type** + + Radio options: + + * “System is affected” + + * Sub-options: `under_investigation`, `affected_no_fix_available`, `affected_fix_available`. + * “System is *not* affected” + + * Sub-options (justification): `vulnerable_code_not_present`, `vulnerable_code_not_in_execute_path`, `vulnerable_configuration_not_present`, `inline_mitigation_applied`, `other`. + * “Fixed” + + * Sub-options: `fixed_in_this_version`, `fixed_in_downstream`, `fixed_by_vendor`. + * “Won’t fix / accepted risk” + + * Sub-options: `business_acceptance`, `low_likelihood`, `temporary_exception`. + +2. **Scope definition** + + Checkboxes + selectors: + + * Scope: + + * [x] This artifact only + * [ ] All artifacts of this application + * [ ] All artifacts in this org with the same component & version + * For each, display a short estimate: “This will apply to 3 artifacts (click to preview).” + +3. **Metadata** + + * Required: + + * Justification category (if not chosen above). + * Free-text “Reason / supporting evidence” (min length e.g., 20 characters). + * Optional: + + * Expiry date (for temporary exceptions). + * Link to ticket / document (URL). + * Attach external VEX file ID or reference (if you’ve ingested vendor VEX). + +4. **Output preview** + + A read-only JSON-like preview of the VEX statement(s) to be generated, with a friendly label: “These statements will be added to the proof graph for snapshot XYZ.” + This makes the model transparent to power users. + +On submit: + +* Show a small toast: “Status updated · VEX statement anchored to snapshot [ID] · Undo” (undo limited to a short window). +* The Finding row and drawer update immediately (status badges, history timeline). + +### 4.2 Bulk suppression & policy overlay + +**Screen: “VEX & Policies”** + +Tabs: + +1. **VEX statements** + + * Table: VEX ID, CVE/ID, Status, Scope, Created by, Last updated, Source (vendor/internal). + * Filters: status, scope, target (component, application, artifact). + * Bulk actions: Export VEX (OpenVEX / CycloneDX VEX), disable / supersede. + +2. **Policies** + + * Declarative rules like: “Treat `vulnerable_code_not_present` from vendor X as authoritative” or “Do not auto-suppress based on `inline_mitigation_applied` without internal confirmation.” + * This expresses how raw VEX translates into “suppressed” vs “needs review” in the UI. + +UX pattern: + +* Each VEX row includes a small indicator: `S` (suppresses findings under current policy) or `R` (requires review). +* Clicking a row opens a side drawer with: + + * Statements content (normalized). + * Affected findings count. + * Policy evaluation result (“This statement currently suppresses 47 findings”). + +--- + +## 5. Findings list: global “inbox” for risk + +**Screen: “Findings”** + +This is the cross-application queue, similar to Snyk’s project list + GitHub’s security tab, but with VEX semantics. + +Layout: + +* Filters on the left: + + * Application / artifact + * Severity + * Status (Open / Suppressed / Only suppressed / Needs review) + * VEX status + * Source (SAST, SBOM, container, runtime) +* Table in center: + + * Columns: Severity, ID, Application, Artifact, Status, VEX status, “Decision owner” (user or team), Age. +* Top bar: + + * “Assign to…” for workflow + * “Update status / Add VEX decision” for bulk + * “Export current view” (CSV/JSON) + +Micro-interactions: + +* “Status” column clearly distinguishes: + + * Open (no VEX) + * Open (VEX says `affected`) + * Suppressed (VEX says `not_affected` / `fixed`) + * Suppressed (policy exception: “Won’t fix”) +* Hover on “Suppressed” shows a tooltip with the justification and who made the decision. + +--- + +## 6. Audit & export / Proof bundles + +### 6.1 Exports list + +**Screen: “Exports & Proofs”** + +Purpose: centralized, auditable place for generating and revisiting proofs. + +Layout: + +* Top actions: + + * “New export / proof bundle” + * Filters: type (CSV, JSON, SBOM, VEX, composite bundle), created by, status. + +* Table: + + * Name / description + * Type (e.g., “OpenVEX for app ‘payments’”, “Proof bundle for prod rollout 2025-03-01”) + * Scope (org / app / artifact / snapshot range) + * Includes suppressed? (Yes/No) + * Status (Complete / Failed) + * Created by, Created at + * Download button + +### 6.2 “New export / proof bundle” flow + +1. **What do you need?** + + Choices: + + * SBOM export (CycloneDX/SPDX) + * VEX export (OpenVEX / CycloneDX VEX) + * Findings export (CSV / JSON) + * Proof bundle (zip containing SBOM + VEX + findings + manifest) + +2. **Scope** + + * Radio: + + * A single artifact (picker) + * All artifacts of an application (picker) + * Entire org, limited by: + + * Time window (from..to) + * Environments (prod only / all) + * Optional: choose graph snapshot(s) (latest vs explicit ID). + +3. **Options** + + * Include suppressed findings: + + * [x] Yes, include them, with reasons + * [ ] No, only unresolved + * Mask sensitive fields? (e.g., repository URLs) + * Include audit log excerpt: + + * [x] VEX & suppression decisions + * [ ] All user actions + +4. **Review & create** + + * Show a summary: + + * N artifacts, M findings, K VEX statements. + * Button: “Create export”. + +Once complete: + +* Row in the table is updated with a download button. +* Clicking a row opens an **Export detail drawer**: + + * Manifest preview (JSON-like). + * Link to the associated graph snapshot IDs. + * Embedded notice that “Regulators can verify this bundle by validating manifest signature and graph snapshot hash” (if you sign them). + +Micro-interaction: + +* For artifacts and applications, the Artifact detail and Application detail pages should have contextual buttons: + + * “Export SBOM” + * “Export VEX” + * “Download proof bundle” + +These open the same export dialog pre-populated with the relevant scope. + +--- + +## 7. History & audit trail UX + +Audit must be first-class, not buried. + +**Pattern: Timeline component** + +Used in: + +* Application > History +* Artifact > History +* Finding > History +* VEX statement > History +* Export > Details + +Timeline item shape: + +* Icon (type of action: scan, decision, export) +* Title (short, human readable) +* Metadata: + + * User + * Timestamp + * Graph snapshot ID +* Detail: + + * Before/after summary (e.g., “VEX status: `affected` → `not_affected`”) + +Filters: + +* By actor (user / system) +* By action type (scan, VEX change, suppression, export, policy change) +* By time range + +Long-term: this timeline can become your “forensic report” generator. + +--- + +## 8. Role-based slices + +You can keep a single UX codebase but offer different “home” experiences per role. + +1. **Security engineer** + + * Default landing: “Findings” with open & needs-review filters. + * Emphasis on Evidence & VEX decisions. + * Quick actions: create VEX, adjust policies. + +2. **Developer** + + * Default landing: “My assignments” (subset of Findings where they’re the owner). + * Simplified VEX dialog: + + * Emphasis on “Fixed in commit X” and “Provide more evidence” rather than full-blown VEX authoring. + * Tight links to repo/IDE. + +3. **Auditor / Compliance** + + * Default landing: “Exports & Proofs”. + * Views oriented around applications and time windows, not individual findings. + * Read-only access to VEX & policies; can request evidence rather than editing it. + +Role behavior is enforced by RBAC, but the UX is guided by ship-ready presets. + +--- + +## 9. Implementation checklist for your .NET/Angular team + +To make this action-ready: + +1. **Routes / pages** + + * `/inventory` + * `/applications/:appId` + * `/artifacts/:artifactId` + * `/findings` + * `/vex` + * `/exports` + * `/settings` + +2. **Core reusable components** + + * Graph snapshot badge (with copy & deeplink). + * Findings table (configurable columns, selection). + * Finding details drawer (tabs: Evidence / Impact / VEX & decisions / History). + * VEX status badge. + * Timeline component. + * Export job dialog & list. + +3. **API contracts to align with UX** + + * `GET /artifacts/:id/findings?snapshot=...` + * `GET /findings/:id?includeEvidence=true` + * `POST /vex/statements` (supports scoped statements). + * `GET /vex/statements?filters...` + * `POST /exports` + `GET /exports/:id` + * `GET /audit?scopeType=&scopeId=&range=...` + +4. **Deep-linking** + + * URLs should embed at least `snapshotId` for deterministic views: + + * `/artifacts/:artifactId/findings/:findingId?snapshot=:snapshotId` + +5. **Telemetry for UX refinement** + + * Track frequency of: + + * Evidence tab switches (Code vs Dependency vs Runtime). + * VEX edits per finding. + * Export jobs by type. + * Use this to prioritize which evidence views to optimize first. + +--- + +If you want a next step, I can translate this into: + +* A low-fidelity screen map (per route, per component), or +* An example JSON schema for VEX statements + proof bundles that matches the UI’s expectations, + +so your backend and frontend teams can converge on a single contract. diff --git a/docs/product-advisories/30-Nov-2025 - Ecosystem Reality Test Cases.md b/docs/product-advisories/30-Nov-2025 - Ecosystem Reality Test Cases.md new file mode 100644 index 000000000..e490b2cdb --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Ecosystem Reality Test Cases.md @@ -0,0 +1,722 @@ +I thought you might appreciate a quick reality-check of public evidence backing up five concrete test cases you could drop into the StellaOps acceptance suite **today** — each rooted in a real issue or behavior in the ecosystem. + +--- + +## 🔎 Recent public incidents & tests matching your list + +### • Credential-leak via Grype JSON output + +* CVE-2025-65965 / GHSA-6gxw-85q2-q646 allows registry credentials to be written unsanitized into `--output json=…`. ([GitHub][1]) +* Affects grype 0.68.0–0.104.0; patched in 0.104.1. ([GitHub][2]) +* Workaround: avoid JSON file output or upgrade. ([GitHub][1]) + +**Implication for StellaOps**: run Grype with credentials + JSON output + scan the JSON for secrets; the test catches the leak. + +--- + +### • Air-gap / old DB schema issues with Trivy + +* `--skip-db-update` supports offline mode. ([Trivy][3]) +* Using an old/offline DB with mismatched schema errors out instead of falling back. ([GitHub][4]) + +**Implication**: capture that deterministic failure (old DB + skip update) or document the exit code as part of offline gating. + +--- + +### • SBOM mismatch between native binary vs container builds (Syft + friends) + +* Mixing SBOM sources (Syft vs Trivy) yields wildly different vulnerability counts when fed into Grype. ([GitHub][5]) + +**Implication**: compare SBOMs from native builds and containers for the same artifact (digests, component counts) to detect provenance divergence. + +--- + +### • Inconsistent vulnerability detection across Grype versions + +* The same SBOM under Grype v0.87.0 reported multiple critical+high findings; newer versions reported none. ([GitHub][6]) + +**Implication**: ingest a stable SBOM/VEX set under multiple DB/scanner versions to detect regressions or nondeterministic outputs. + +--- + +## ✅ Publicly verified vs speculative + +| ✅ Publicly verified | ⚠️ Needs controlled testing / assumption-driven | +| ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | +| Credential leak in Grype JSON output (CVE-2025-65965) ([GitHub][2]) | Exact SBOM-digest parity divergence between native & container Syft runs (no formal bug yet) | +| Trivy offline DB schema error ([Trivy][3]) | Custom CVSS/VEX sorting in patched grype-dbs or Snyk workarounds (not publicly reproducible) | +| Grype version divergence on the same SBOM ([GitHub][6]) | VEX evidence mappings from tools like Snyk - plausible but not documented reproducibly | + +--- + +## 🎯 Why this matters for StellaOps + +Your goals are deterministic, audit-ready SBOM + VEX pipelines. These public incidents show how fragile tooling can be. Embedding the above tests ensures reliability and reproducibility even under credential leaks, offline scans, or DB/schema churn. + +Want a bash/pseudo spec for all five cases (commands + assertions)? I can drop it into your repo as a starting point. + +[1]: https://github.com/anchore/grype/security/advisories/GHSA-6gxw-85q2-q646?utm_source=chatgpt.com "Credential disclosure vulnerability in Grype JSON output" +[2]: https://github.com/advisories/GHSA-6gxw-85q2-q646?utm_source=chatgpt.com "Grype has a credential disclosure vulnerability in its JSON ..." +[3]: https://trivy.dev/docs/v0.55/guide/advanced/air-gap/?utm_source=chatgpt.com "Advanced Network Scenarios" +[4]: https://github.com/aquasecurity/trivy/discussions/4838?utm_source=chatgpt.com "trivy offline-db · aquasecurity trivy · Discussion #4838" +[5]: https://github.com/aquasecurity/trivy/discussions/6325?utm_source=chatgpt.com "and of those detected Trivy sometimes does not report a ..." +[6]: https://github.com/anchore/grype/issues/2628?utm_source=chatgpt.com "Grype false negatives in versions v0.88.0 and later leading ..." + +Here’s a concrete package of **guidelines + five acceptance test specs** for StellaOps implementors, based on the incidents and behaviors we walked through (Grype leak, Trivy offline DB, Syft parity, grype-db / VEX, Snyk exploit maturity). + +I’ll keep it tool/framework‑agnostic so you can drop this into whatever test runner you’re using. + +--- + +## 1. Implementation guidelines for StellaOps integrators + +These are the “rules of the road” that every new scanner / SBOM / VEX adapter in StellaOps should follow. + +### 1.1. Test layering + +For each external tool (Grype, Trivy, Syft, Snyk, …) you should have: + +1. **Unit tests** + + * Validate argument construction, environment variables, and parsing of raw tool output. +2. **Adapter integration tests** + + * Run the real tool (or a pinned container image) on small deterministic fixtures. +3. **Acceptance tests (this doc)** + + * Cross-tool workflows and critical invariants: secrets, determinism, offline behavior, VEX mapping. +4. **Golden outputs** + + * For key paths, store canonical JSON/CSAF/CycloneDX outputs as fixtures and diff against them. + +Each new adapter should add at least one test in each layer. + +--- + +### 1.2. Determinism & reproducibility + +**Rule:** *Same inputs (artifact, SBOM, VEX, tool versions) → bit‑for‑bit identical StellaOps results.* + +Guidelines: + +* Pin external tool versions in tests (`GRYPE_VERSION`, `TRIVY_VERSION`, `SYFT_VERSION`, `SNYK_CLI_VERSION`). +* Pin vulnerability DB snapshots (e.g. `GRYPE_DB_SNAPSHOT_ID`, `TRIVY_DB_DIR`). +* Persist the “scan context” alongside results: scanner version, db snapshot id, SBOM hash, VEX hash. +* For sorted outputs, define and test a **stable global sort order**: + + * e.g. `effectiveSeverity DESC, exploitEvidence DESC, vulnerabilityId ASC`. + +--- + +### 1.3. Secrets and logs + +Recent issues show scanners can leak sensitive data to JSON or logs: + +* **Grype**: versions v0.68.0–v0.104.0 could embed registry credentials in JSON output files when invoked with `--file` or `--output json=` and credentials are configured. ([GitLab Advisory Database][1]) +* **Trivy / Snyk**: both have had edge cases where DB/CLI errors or debug logs printed sensitive info. ([Trivy][2]) + +Guidelines: + +* **Never** enable DEBUG/TRACE logging for third‑party tools in production mode. +* **Never** use scanner options that write JSON reports directly to disk unless you control the path and sanitize. +* Treat logs and raw reports as **untrusted**: run them through secret‑scanning / redaction before persistence. +* Build tests that use **fake-but-realistic secrets** (e.g. `stella_USER_123`, `stella_PASS_456`) and assert they never appear in: + + * DB rows + * API responses + * UI + * Stored raw reports + +--- + +### 1.4. Offline / air‑gapped behavior + +Trivy’s offline mode is a good example of fragile behavior: + +* Using an old offline DB with `--skip-db-update` can produce fatal “old schema” errors like + `The local DB has an old schema version… --skip-update cannot be specified with the old DB schema`. ([GitHub][3]) +* Air‑gap workflows require explicit flags like `--skip-db-update` and `--skip-java-db-update`. ([aquasecurity.github.io][4]) + +Guidelines: + +* All scanner adapters must have an **explicit offline mode** flag / config. +* In offline mode: + + * Do **not** attempt any network DB updates. + * Treat **DB schema mismatch / “old DB schema”** as a *hard* scanner infrastructure error, *not* “zero vulns”. +* Surface offline issues as **typed StellaOps errors**, e.g. `ScannerDatabaseOutOfDate`, `ScannerFirstRunNoDB`. + +--- + +### 1.5. External metadata → internal model + +Different tools carry different metadata: + +* CVSS v2/v3/v3.1/v4.0 (score & vectors). +* GHSA vs CVE vs SNYK‑IDs. +* Snyk’s `exploitMaturity` field (e.g. `no-known-exploit`, `proof-of-concept`, `mature`, `no-data`). ([Postman][5]) + +Guidelines: + +* Normalize all of these into **one internal vulnerability model**: + + * `primaryId` (CVE, GHSA, Snyk ID, etc.) + * `aliases[]` + * `cvss[]` (list of metrics with `version`, `baseScore`, `baseSeverity`) + * `exploitEvidence` (internal enum derived from `exploitMaturity`, EPSS, etc.) +* Define **deterministic merge rules** when multiple sources describe the same vuln (CVE+GHSA+SNYK). + +--- + +### 1.6. Contract & fixture hygiene + +* Prefer **static fixtures** over dynamic network calls in tests. +* Version every fixture with a semantic name and version, e.g. + `fixtures/grype/2025-credential-leak-v1.json`. +* When you change parsers or normalizers, add a new version of the fixture, but **keep the old one** to guard regressions. + +--- + +## 2. Acceptance test specs (for implementors) + +Below are five concrete test cases you can add to a `tests/acceptance/` suite. + +I’ll name them `STELLA-ACC-00X` so you can turn them into tickets if you want. + +--- + +### STELLA-ACC-001 — Grype JSON credential leak guard + +**Goal** +Guarantee that StellaOps never stores or exposes registry credentials even if: + +* Grype itself is (or becomes) vulnerable, or +* A user uploads a raw Grype JSON report that contains creds. + +This is directly motivated by CVE‑2025‑65965 / GHSA‑6gxw‑85q2‑q646. ([GitLab Advisory Database][1]) + +--- + +#### 001A – Adapter CLI usage (no unsafe flags) + +**Invariant** + +> The Grype adapter must *never* use `--file` or `--output json=` — only `--output json` to stdout. + +**Setup** + +* Replace `grype` in PATH with a small wrapper script that: + + * Records argv and env to a temp file. + * Exits 0 after printing a minimal fake JSON vulnerability report to stdout. + +**Steps** + +1. Run a standard StellaOps Grype‑based scan through the adapter on a dummy image/SBOM. +2. After completion, read the temp “spy” file written by the wrapper. + +**Assertions** + +* The recorded arguments **do not** contain: + + * `--file` + * `--output json=` +* There is exactly one `--output` argument and its value is `json`. + +> This test is purely about **command construction**; it never calls the real Grype binary. + +--- + +#### 001B – Ingestion of JSON with embedded creds + +**Invariant** + +> If a Grype JSON report contains credentials, StellaOps must either reject it or scrub credentials before storage/exposure. + +**Fixture** + +* `fixtures/grype/credential-leak.json`, modeled roughly like the CVE report: + + * Include a fake Docker config snippet Grype might accidentally embed, e.g.: + + ```json + { + "config": { + "auths": { + "registry.example.com": { + "username": "stella_USER_123", + "password": "stella_PASS_456" + } + } + } + } + ``` + + * Plus one small vulnerability record so the ingestion pipeline runs normally. + +**Steps** + +1. Use a test helper to ingest the fixture as if it were Grype output: + + * e.g. `stellaIngestGrypeReport("credential-leak.json")`. +2. Fetch the stored scan result via: + + * direct DB access, or + * StellaOps internal API. + +**Assertions** + +* Nowhere in: + + * stored raw report blobs, + * normalized vulnerability records, + * metadata tables, + * logs captured by the test runner, + + should the substrings `stella_USER_123` or `stella_PASS_456` appear. +* The ingestion should: + + * either succeed and **omit/sanitize** the auth section, + * or fail with a **clear, typed error** like `ReportContainsSensitiveSecrets`. + +**Implementation guidelines** + +* Implement a **secret scrubber** that runs on: + + * any external JSON you store, + * any logs when a scanner fails verbosely. +* Add a generic helper assertion in your test framework: + + * `assertNoSecrets(ScanResult, ["stella_USER_123", "stella_PASS_456"])`. + +--- + +### STELLA-ACC-002 — Syft SBOM manifest digest parity (native vs container) + +**Goal** +Ensure StellaOps produces the same **artifact identity** (image digest / manifest digest / tags) when SBOMs are generated: + +* By Syft installed natively on the host vs +* By Syft run as a container image. + +This is about keeping VEX, rescan, and historical analysis aligned even if environments differ. + +--- + +#### Setup + +1. Build a small deterministic test image: + + ```bash + docker build -t stella/parity-image:1.0 tests/fixtures/images/parity-image + ``` + +2. Make both Syft variants available: + + * Native binary: `syft` on PATH. + * Container image: `anchore/syft:` already pulled into your local registry/cache. + +3. Decide where you store canonical SBOMs in the pipeline, e.g. under `Scan.artifactIdentity`. + +--- + +#### Steps + +1. **Generate SBOM with native Syft** + + ```bash + syft packages --scope Squashed stella/parity-image:1.0 -o json > sbom-native.json + ``` + +2. **Generate SBOM with containerized Syft** + + ```bash + docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + anchore/syft: \ + packages --scope Squashed stella/parity-image:1.0 -o json \ + > sbom-container.json + ``` + +3. Import each SBOM into StellaOps as if they were separate scans: + + * `scanNative = stellaIngestSBOM("sbom-native.json")` + * `scanContainer = stellaIngestSBOM("sbom-container.json")` + +4. Extract the normalized artifact identity: + + * `scanNative.artifactId` + * `scanNative.imageDigest` + * `scanNative.manifestDigest` + * `scanContainer.*` same fields. + +--- + +#### Assertions + +* `scanNative.artifactId == scanContainer.artifactId` +* `scanNative.imageDigest == scanContainer.imageDigest` +* If you track manifest digest separately: + `scanNative.manifestDigest == scanContainer.manifestDigest` +* Optional: for extra paranoia, assert that the **set of package coordinates** is identical (ignoring ordering). + +**Implementation guidelines** + +* Don’t trust SBOM metadata blindly; if Syft metadata differs, compute a **canonical image ID** using: + + * container runtime inspect, + * or an independent digest calculator. +* Store artifact IDs in a **single, normalized format** across all scanners and SBOM sources. + +--- + +### STELLA-ACC-003 — Trivy air‑gapped DB schema / skip behavior + +**Goal** +In offline mode, Trivy DB schema mismatches must produce a clear, typed failure in StellaOps — never silently “0 vulns”. + +Trivy shows specific messages for old DB schema when combined with `--skip-update/--skip-db-update`. ([GitHub][3]) + +--- + +#### Fixtures + +1. `tests/fixtures/trivy/db-old/` + + * A Trivy DB with `metadata.json` `Version: 1` where your pinned Trivy version expects `Version: 2` or greater. + * You can: + + * download an old offline DB archive, or + * copy a modern DB and manually set `"Version": 1` in `metadata.json` for test purposes. +2. `tests/fixtures/trivy/db-new/` + + * A DB snapshot matching the current Trivy version (pass case). + +--- + +#### 003A – Old schema + `--skip-db-update` gives typed error + +**Steps** + +1. Configure the Trivy adapter for offline scan: + + * `TRIVY_CACHE_DIR = tests/fixtures/trivy/db-old` + * Add `--skip-db-update` (or `--skip-update` depending on your adapter) to the CLI args. + +2. Run a StellaOps scan using Trivy on a small test image. + +3. Capture: + + * Trivy exit code. + * Stdout/stderr. + * StellaOps internal `ScanRun` / job status. + +**Assertions** + +* Trivy exits non‑zero. +* Stderr contains both: + + * `"The local DB has an old schema version"` and + * `"--skip-update cannot be specified with the old DB schema"` (or localized equivalent). ([GitHub][3]) +* StellaOps marks the scan as **failed with error type** `ScannerDatabaseOutOfDate` (or equivalent internal enum). +* **No** vulnerability records are saved for this run. + +--- + +#### 003B – New schema + offline flags succeeds + +**Steps** + +1. Same as above, but: + + * `TRIVY_CACHE_DIR = tests/fixtures/trivy/db-new` + +2. Run the scan. + +**Assertions** + +* Scan succeeds. +* A non‑empty set of vulnerabilities is stored (assuming the fixture image is intentionally vulnerable). +* Scan metadata records: + + * `offlineMode = true` + * `dbSnapshotId` or a hash of `metadata.json`. + +**Implementation guidelines** + +* Parse known Trivy error strings into **structured error types** instead of treating all non‑zero exit codes alike. +* Add a small helper in the adapter like: + + ```go + func classifyTrivyError(stderr string, exitCode int) ScannerErrorType + ``` + + and unit test it with copies of real Trivy messages from docs/issues. + +--- + +### STELLA-ACC-004 — grype‑db / CVSS & VEX sorting determinism + +**Goal** +Guarantee that StellaOps: + +1. Merges and prioritizes CVSS metrics (v2/v3/v3.1/4.0) deterministically, and +2. Produces a stable, reproducible vulnerability ordering after applying VEX. + +This protects you from “randomly reshuffled” critical lists when DBs update or scanners add new metrics. + +--- + +#### Fixture + +`fixtures/grype/cvss-vex-sorting.json`: + +* Single artifact with three vulnerabilities on the same package: + + 1. `CVE-2020-AAAA` + + * CVSS v2: 7.5 (High) + * CVSS v3.1: 5.0 (Medium) + 2. `CVE-2021-BBBB` + + * CVSS v3.1: 8.0 (High) + 3. `GHSA-xxxx-yyyy-zzzz` + + * Alias of `CVE-2021-BBBB` but only v2 score 5.0 (Medium) + +* A companion VEX document (CycloneDX VEX or CSAF) that: + + * Marks `CVE-2020-AAAA` as `not_affected` for the specific version. + * Leaves `CVE-2021-BBBB` as `affected`. + +You don’t need real IDs; you just need consistent internal expectations. + +--- + +#### Steps + +1. Ingest the Grype report and the VEX document together via StellaOps, producing `scanId`. + +2. Fetch the normalized vulnerability list for that artifact: + + * e.g. `GET /artifacts/{id}/vulnerabilities?scan={scanId}` or similar internal call. + +3. Map it to a simplified view inside the test: + + ```json + [ + { "id": "CVE-2020-AAAA", "effectiveSeverity": "...", "status": "..."}, + { "id": "CVE-2021-BBBB", "effectiveSeverity": "...", "status": "..."}, + ... + ] + ``` + +--- + +#### Assertions + +* **Deduplication**: + + * `GHSA-xxxx-yyyy-zzzz` is **merged** into the same logical vuln as `CVE-2021-BBBB` (i.e. appears once with aliases including both IDs). + +* **CVSS selection**: + + * For `CVE-2020-AAAA`, `effectiveSeverity` is based on: + + * v3.1 over v2 if present, else highest base score. + * For `CVE-2021-BBBB`, `effectiveSeverity` is “High” (from v3.1 8.0). + +* **VEX impact**: + + * `CVE-2020-AAAA` is marked as `NOT_AFFECTED` (or your internal equivalent) and should: + + * either be excluded from the default list, or + * appear but clearly flagged as `not_affected`. + +* **Sorting**: + + * The vulnerability list is ordered stably, e.g.: + + 1. `CVE-2021-BBBB` (High, affected) + 2. `CVE-2020-AAAA` (Medium, not_affected) — if you show not_affected entries. + +* **Reproducibility**: + + * Running the same test multiple times yields identical JSON for: + + * the vulnerability list (after sorting), + * the computed `effectiveSeverity` values. + +**Implementation guidelines** + +* Implement explicit merge logic for per‑vuln metrics: + + ```text + 1. Prefer CVSS v3.1 > 3.0 > 2.0 when computing effectiveSeverity. + 2. If multiple metrics of same version exist, pick highest baseScore. + 3. When multiple sources (NVD, GHSA, vendor) disagree on baseSeverity, + define your precedence and test it. + ``` + +* Keep VEX application deterministic: + + * apply VEX status before sorting, + * optionally remove `not_affected` entries from the “default” list, but still store them. + +--- + +### STELLA-ACC-005 — Snyk exploit‑maturity → VEX evidence mapping + +**Goal** +Map Snyk’s `exploitMaturity` metadata into a standardized StellaOps “exploit evidence” / VEX semantic and make this mapping deterministic and testable. + +Snyk’s APIs and webhooks expose an `exploitMaturity` field (e.g. `no-known-exploit`, `proof-of-concept`, etc.). ([Postman][5]) + +--- + +#### Fixture + +`fixtures/snyk/exploit-maturity.json`: + +* Mimic a Snyk test/report response with at least four issues, one per value: + + ```json + [ + { + "id": "SNYK-JS-AAA-1", + "issueType": "vuln", + "severity": "high", + "issueData": { + "exploitMaturity": "no-known-exploit", + "cvssScore": 7.5 + } + }, + { + "id": "SNYK-JS-BBB-2", + "issueData": { + "exploitMaturity": "proof-of-concept", + "cvssScore": 7.5 + } + }, + { + "id": "SNYK-JS-CCC-3", + "issueData": { + "exploitMaturity": "mature", + "cvssScore": 5.0 + } + }, + { + "id": "SNYK-JS-DDD-4", + "issueData": { + "exploitMaturity": "no-data", + "cvssScore": 9.0 + } + } + ] + ``` + +(Names/IDs don’t matter; internal mapping does.) + +--- + +#### Internal mapping (proposed) + +Define a StellaOps enum, e.g. `ExploitEvidence`: + +* `EXPLOIT_NONE` ← `no-known-exploit` +* `EXPLOIT_POC` ← `proof-of-concept` +* `EXPLOIT_WIDESPREAD` ← `mature` +* `EXPLOIT_UNKNOWN` ← `no-data` or missing + +And define how this influences risk (e.g. by factors or direct overrides). + +--- + +#### Steps + +1. Ingest the Snyk fixture as a Snyk scan for a dummy project/image. +2. Fetch normalized vulnerabilities from StellaOps for that scan. + + In the test, map each vuln to: + + ```json + { + "id": "SNYK-JS-AAA-1", + "exploitEvidence": "...", + "effectiveSeverity": "..." + } + ``` + +--- + +#### Assertions + +* Mapping: + + * `SNYK-JS-AAA-1` → `exploitEvidence == EXPLOIT_NONE` + * `SNYK-JS-BBB-2` → `exploitEvidence == EXPLOIT_POC` + * `SNYK-JS-CCC-3` → `exploitEvidence == EXPLOIT_WIDESPREAD` + * `SNYK-JS-DDD-4` → `exploitEvidence == EXPLOIT_UNKNOWN` + +* Risk impact (example; adjust to your policy): + + * For equal CVSS, rows with `EXPLOIT_WIDESPREAD` or `EXPLOIT_POC` must rank **above** `EXPLOIT_NONE` in any prioritized listing (e.g. patch queue). + * A high CVSS (9.0) with `EXPLOIT_UNKNOWN` must not be treated as lower risk than a lower CVSS with `EXPLOIT_WIDESPREAD` unless your policy explicitly says so. + + Your sorting rule might look like: + + ```text + ORDER BY + exploitEvidenceRank DESC, -- WIDESPREAD > POC > UNKNOWN > NONE + cvssBaseScore DESC, + vulnerabilityId ASC + ``` + + and the test should assert on the resulting order. + +* Any Snyk issue missing `exploitMaturity` explicitly is treated as `EXPLOIT_UNKNOWN`, not silently defaulted to `EXPLOIT_NONE`. + +**Implementation guidelines** + +* Centralize the mapping in a single function and unit test it: + + ```ts + function mapSnykExploitMaturity(value: string | null): ExploitEvidence + ``` + +* If you emit VEX (CycloneDX/CSAF) from StellaOps, propagate the internal `ExploitEvidence` into the appropriate field (e.g. as part of justification or additional evidence object) and add a small test that round‑trips this. + +--- + +## 3. How to use this as an implementor checklist + +When you add or modify StellaOps integrations, treat this as a living checklist: + +1. **Touching Grype?** + + * Ensure `STELLA-ACC-001` still passes. +2. **Touching SBOM ingestion / artifact identity?** + + * Run `STELLA-ACC-002`. +3. **Touching Trivy adapter or offline mode?** + + * Run `STELLA-ACC-003`. +4. **Changing vulnerability normalization / severity logic?** + + * Run `STELLA-ACC-004` and `STELLA-ACC-005`. +5. **Adding a new scanner?** + + * Clone these patterns: + + * one **secret-leak** test, + * one **offline / DB drift** test (if relevant), + * one **identity parity** or **determinism** test, + * one **metadata‑mapping** test (like exploit maturity). + +If you want, next step I can help you translate these into a concrete test skeleton (e.g. Gherkin scenarios or Jest/Go test functions) for the language you’re using in StellaOps. + +[1]: https://advisories.gitlab.com/pkg/golang/github.com/anchore/grype/CVE-2025-65965/?utm_source=chatgpt.com "Grype has a credential disclosure vulnerability in its JSON ..." +[2]: https://trivy.dev/docs/latest/references/troubleshooting/?utm_source=chatgpt.com "Troubleshooting" +[3]: https://github.com/aquasecurity/trivy-db/issues/186?utm_source=chatgpt.com "trivy-db latest Version has an old schema · Issue #186" +[4]: https://aquasecurity.github.io/trivy/v0.37/docs/advanced/air-gap/?utm_source=chatgpt.com "Air-Gapped Environment" +[5]: https://www.postman.com/api-evangelist/snyk/collection/mppgu5u/snyk-api?utm_source=chatgpt.com "Snyk API | Get Started" diff --git a/docs/product-advisories/30-Nov-2025 - Implementor Guidelines for Stella Ops.md b/docs/product-advisories/30-Nov-2025 - Implementor Guidelines for Stella Ops.md new file mode 100644 index 000000000..96c476c98 --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Implementor Guidelines for Stella Ops.md @@ -0,0 +1,328 @@ +Here’s a compact, diagram‑first blueprint that shows how to turn a CycloneDX SBOM into signed, replay‑safe proofs across DSSE/in‑toto, Rekor v2 (tile‑backed) receipts, and VEX—plus how to run this with the public instance or fully offline. + +--- + +## 1) Mental model (one line per hop) + +``` +[SBOM: CycloneDX JSON] + └─(wrap as DSSE payload; predicate = CycloneDX) + └─(optional: in‑toto statement for context) + └─(sign → cosign/fulcio or your own CA) + └─(log entry → Rekor v2 / tiles) + └─(checkpoint + inclusion proof + receipt) + └─(VEX attestation references SBOM/log) + └─(Authority anchors/keys + policies) +``` + +* **CycloneDX SBOM** is your canonical inventory. ([cyclonedx.org][1]) +* **DSSE** provides a minimal, standard signing envelope; in‑toto statements add supply‑chain context. ([JFrog][2]) +* **Rekor v2** stores a hash of your attestation in a **tile‑backed transparency log** and returns **checkpoint + inclusion proof** (small, verifiable). ([Sigstore Blog][3]) +* **VEX** conveys exploitability (e.g., “not affected”) and should reference the SBOM and, ideally, the Rekor receipt. ([cyclonedx.org][4]) + +--- + +## 2) Exact capture points (what to store) + +* **SBOM artifact**: `sbom.cdx.json` (canonicalized bytes + SHA256). ([cyclonedx.org][1]) +* **DSSE envelope** over SBOM (or in‑toto statement whose predicate is CycloneDX): keep the full JSON + signature. ([JFrog][2]) +* **Rekor v2 receipt**: + + * **Checkpoint** (signed tree head) + * **Inclusion proof** (audit path) + * **Entry leaf hash / UUID** + Persist these with your build to enable offline verification. ([Sigstore Blog][3]) +* **VEX attestation** (CycloneDX VEX): include references (by digest/URI) to the **SBOM** and the **Rekor entry/receipt** used for the SBOM attestation. ([cyclonedx.org][4]) +* **Authority anchors**: publish the verifying keys (or TUF‑root if using Sigstore public good), plus your policy describing accepted issuers and algorithms. ([Sigstore][5]) + +--- + +## 3) Field‑level linkage (IDs you’ll wire together) + +* **`subject.digest`** in DSSE/in‑toto ↔ **SBOM SHA256**. ([OpenSSF][6]) +* **Rekor entry** ↔ **DSSE envelope digest** (leaf/UUID recorded in receipt). ([GitHub][7]) +* **VEX `affects` / `analysis`** entries ↔ **components in SBOM** (use purl/coordinates) and include **`evidence`/`justification`** with **Rekor proof URI**. ([cyclonedx.org][4]) + +--- + +## 4) Verification flow (online or air‑gapped) + +**Online (public good):** + +1. Verify DSSE signature against accepted keys/issuers. ([Sigstore][5]) +2. Verify Rekor **checkpoint signature** and **inclusion proof** for the logged DSSE digest. ([Go Packages][8]) +3. Validate VEX against the same SBOM digest (and optionally that its own attestation is also logged). ([cyclonedx.org][4]) + +**Air‑gapped / sovereign:** + +* Mirror/export **Rekor tiles + checkpoints** on a courier medium; keep receipts small by shipping only tiles covering the ranges you need. +* Run **self‑hosted Rekor v2** or a **local tile cache**; verifiers check **checkpoint signatures** and **consistency proofs** exactly the same way. ([Sigstore Blog][3]) + +--- + +## 5) Public instance vs self‑hosted (decision notes) + +* **Public**: zero‑ops, audited community infra; you still archive receipts with your releases. ([Sigstore][5]) +* **Self‑hosted Rekor v2 (tiles)**: cheaper/simpler than v1, tile export makes **offline kits** practical; publish your **root keys** as organization anchors. ([Sigstore Blog][3]) + +--- + +## 6) Minimal CLI recipe (illustrative) + +* Generate SBOM → wrap → attest → log → emit receipt: + + * Create CycloneDX JSON; compute digest. ([cyclonedx.org][1]) + * Create **DSSE** or **in‑toto** attestation for the SBOM; sign (cosign or your CA). ([JFrog][2]) + * Submit to **Rekor v2**; store **checkpoint + inclusion proof + UUID** with the build. ([Sigstore Blog][3]) + * Emit **VEX** referencing the SBOM digest **and** the Rekor entry (URI/UUID). ([cyclonedx.org][4]) + +--- + +## 7) Developer guardrails (to keep proofs replay‑safe) + +* **Canonical bytes only** (stable JSON ordering/whitespace) before hashing/signing. ([JFrog][2]) +* **Pin algorithms** (e.g., SHA‑256 + key types) in policy; reject drift. ([Sigstore][5]) +* **Always persist**: SBOM, DSSE envelope, Rekor receipt, VEX, and your **accepted‑keys manifest** with version. ([Sigstore Blog][3]) +* **Test offline**: verification must pass using only tiles + receipts you ship. ([Go Packages][9]) + +--- + +## 8) Optional niceties + +* Gate deployments on “image must have **signed SBOM** (attestation)”. Sigstore Policy Controller example exists. ([Stackable Documentation][10]) +* Track CVE status via **CycloneDX VEX** in your UI (“affected/not affected” with evidence links to Rekor). ([cyclonedx.org][4]) + +--- + +If you want, I can turn this into a **Stella Ops** diagram + drop‑in `docs/blueprints/sbom‑to‑vex‑rekor.md` with exact JSON stubs for: DSSE envelope, in‑toto statement, Rekor receipt example, and a CycloneDX VEX snippet wired to the receipt. + +[1]: https://cyclonedx.org/specification/overview/?utm_source=chatgpt.com "Specification Overview" +[2]: https://jfrog.com/blog/introducing-dsse-attestation-online-decoder/?utm_source=chatgpt.com "Introducing the DSSE Attestation Online Decoder" +[3]: https://blog.sigstore.dev/rekor-v2-ga/?utm_source=chatgpt.com "Rekor v2 GA - Cheaper to run, simpler to maintain" +[4]: https://cyclonedx.org/capabilities/vex/?utm_source=chatgpt.com "Vulnerability Exploitability eXchange (VEX)" +[5]: https://docs.sigstore.dev/logging/overview/?utm_source=chatgpt.com "Rekor" +[6]: https://openssf.org/blog/2024/06/26/a-deep-dive-into-sbomit-and-attestations/?utm_source=chatgpt.com "A Deep Dive into SBOMit and Attestations" +[7]: https://github.com/sigstore/rekor?utm_source=chatgpt.com "sigstore/rekor: Software Supply Chain Transparency Log" +[8]: https://pkg.go.dev/github.com/sigstore/rekor-tiles/v2/pkg/verify?utm_source=chatgpt.com "verify package - github.com/sigstore/rekor-tiles/v2/pkg/verify" +[9]: https://pkg.go.dev/github.com/sigstore/rekor-tiles?utm_source=chatgpt.com "rekor-tiles module - github.com/sigstore/rekor-tiles" +[10]: https://docs.stackable.tech/home/stable/guides/viewing-and-verifying-sboms/?utm_source=chatgpt.com "Viewing and verifying SBOMs of the Stackable Data Platform" + +These guidelines are meant for people implementing or extending Stella Ops: core maintainers, external contributors, plug-in authors, and teams wiring Stella Ops into CI/CD and offline environments. They sit under the published docs (SRS, architecture, release playbook, etc.) and try to translate those into a practical checklist. ([Gitea: Git with a cup of tea][1]) + +--- + +## 1. Anchor yourself in the canonical docs + +Before writing code or designing a new module: + +* **Treat the SRS as the contract.** Any feature must trace back to a requirement (or add a new one via ADR + docs). Don’t ship behavior that contradicts quota rules, SBOM formats, NFRs, or API shapes defined there. ([Gitea: Git with a cup of tea][1]) +* **Respect the existing architecture split**: backend API, CLI, Zastava agent, internal registry, web UI, plug-ins. Don’t blur responsibilities (e.g., don’t make the UI talk directly to Redis). ([Gitea: Git with a cup of tea][1]) +* **Keep docs and code in lock-step.** If you change an endpoint, CLI flag, plug-in contract, or quota behavior, update the corresponding doc page / module dossier before merge. ([Stella Ops][2]) + +Think of the docs (`docs/`, `modules/`, ADRs) as **source of truth**; code is the implementation detail. + +--- + +## 2. Core principles you must preserve + +### 2.1 SBOM-first & VEX-first + +Stella Ops exists to be an **SBOM-first, VEX-policy-driven container security platform**. ([Stella Ops][3]) + +Implementor rules: + +* **SBOMs are the system of record.** All new scanners, checks, and policies should operate on SBOMs (Trivy JSON, SPDX JSON, CycloneDX JSON) rather than ad-hoc image inspections. ([Gitea: Git with a cup of tea][1]) +* **VEX/OpenVEX is how you express exploitability.** Any feature that mutates “this thing is risky / safe” must do so via VEX-style statements (statuses like *affected*, *not affected*, *fixed*, *under investigation* + justification), not bespoke flags. ([cyclonedx.org][4]) + +### 2.2 Deterministic & replayable + +Docs and the release playbook emphasise **deterministic replay**: given the same SBOM + advisory snapshot + policies, you must get the same verdict, even months later. ([Stella Ops][2]) + +* No hidden network calls that change behavior over time. If you need remote data, it must be: + * Snapshotted and imported via the Offline Update Kit (OUK), or + * Explicitly modeled as a versioned input in a replay manifest. ([Gitea: Git with a cup of tea][5]) +* Avoid nondeterminism: no reliance on wall-clock time (except where specified, e.g., quotas), random IDs in hashes, or unordered iteration when serializing JSON. + +### 2.3 Offline-first & sovereign + +Stella Ops must run **fully offline** with all dependencies vendored or mirrored, and allow **bring-your-own trust roots, vulnerability sources, and crypto profiles**. ([Gitea: Git with a cup of tea][6]) + +* Any new integration (scanner, advisory feed, trust store) **MUST have an offline path**: + * Downloaded + signed into OUK, or + * Mirrored into the internal registry / DB. +* Never hard-code URLs to cloud services in core paths. Gate optional online behavior behind explicit config flags, default-off. + +### 2.4 Performance, quotas, and transparency + +The SRS sets **P95 ≤ 5 s cold, ≤ 1 s warm** and a quota of `{{quota_token}}` scans per token/day with explicit `/quota` APIs and UI banners. ([Gitea: Git with a cup of tea][1]) + +Implementor rules: + +* Any change that touches scanning or advisory evaluation must: + * Preserve P95 targets under the reference hardware profile. + * Not add hidden throttling—only the documented soft quota mechanisms. ([Gitea: Git with a cup of tea][1]) +* If you introduce heavier logic (e.g., new reachability analysis), consider: + * Pre-computation in background jobs. + * Caching keyed by SBOM digest + advisory snapshot ID. + +### 2.5 Security & SLSA alignment + +The release guide already aims at **SLSA ≥ 2, targeting level 3**, with provenance via BuildKit and signed artefacts. ([Gitea: Git with a cup of tea][5]) + +Implementation rules: + +* All new build artefacts (containers, Helm charts, OUK tarballs, plug-ins) **MUST**: + * Have SBOMs attached. + * Be signed (Cosign or equivalent). + * Be compatible with future Rekor logging. ([Gitea: Git with a cup of tea][5]) +* Inter-service comms **MUST** use TLS or localhost sockets as per NFRs. ([Gitea: Git with a cup of tea][1]) +* Zero telemetry by default. Any metrics must be Prometheus-style, local, and opt-in for anything that could leak sensitive data. ([Gitea: Git with a cup of tea][1]) + +--- + +## 3. Cross-cutting implementation guidelines + +### 3.1 APIs, CLI, and schemas + +* **Respect existing API semantics.** `/scan`, `/layers/missing`, `/quota`, `/policy/*`, `/plugins` are canonical; new endpoints must follow the same patterns (Bearer auth, quota flags, clear error codes). ([Gitea: Git with a cup of tea][1]) +* **Keep CLI and API mirrors in sync.** If `stella scan` gains a flag, the `/scan` API should gain a corresponding field (or vice versa); update the API & CLI Reference doc. ([Stella Ops][2]) +* **Evolve schemas via versioned contracts.** Any change to SBOM or VEX-related internal schemas must add fields compatibly; never repurpose a field’s meaning silently. + +### 3.2 Data and storage + +* Use Mongo for durable state and Redis for caches as per SRS assumptions; don’t introduce new stateful infrastructure lightly. ([Gitea: Git with a cup of tea][1]) +* All cache keys must be explicitly invalidated when: + * OUK updates are applied. + * Policies, advisories, or SBOM parsing rules change. ([Gitea: Git with a cup of tea][5]) + +### 3.3 Observability + +* For every new long-running operation or critical decision path: + * Export Prometheus metrics (latency, error counts, queue depth where relevant). + * Emit structured audit events where user-visible risk changes (e.g., VEX status flips, OUK updates). ([Gitea: Git with a cup of tea][1]) + +--- + +## 4. Supply-chain evidence: SBOM → SLSA → DSSE → Rekor → VEX + +This is where prior work on SBOM/DSSE/Rekor/VEX connects directly to Stella Ops’ roadmap. ([Gitea: Git with a cup of tea][1]) + +### 4.1 SBOM handling + +* **Ingestion:** Implement parsers for Trivy JSON, SPDX JSON, and CycloneDX JSON, and always auto-detect when `sbomType` is omitted. ([Gitea: Git with a cup of tea][1]) +* **Generation:** When building images or plug-ins: + * Use `StellaOps.SBOMBuilder` or equivalent to produce at least SPDX and one additional format. + * Attach SBOMs as OCI artefacts and label images accordingly. ([Gitea: Git with a cup of tea][5]) +* **Delta SBOM:** Any new builder or scanner must preserve the delta-SBOM contract: compute layer digests, ask `/layers/missing`, generate SBOM only for missing layers, and hit the 1 s P95 warm scan target. ([Gitea: Git with a cup of tea][1]) + +### 4.2 SLSA provenance and DSSE + +* **Per-SBOM provenance:** For each generated SBOM, produce SLSA-compliant provenance describing builder, sources, inputs, and environment (Build L1+ at minimum, aiming for L2/L3 over time). ([SLSA][7]) +* **DSSE envelopes:** Wrap SBOMs and provenance in DSSE envelopes signed with the active crypto profile (Cosign keypair, HSM, or regional provider). ([Sigstore][8]) +* **Deterministic hashing:** Always hash canonicalized JSON bytes (stable ordering, no insignificant whitespace) so proofs remain stable no matter the emitter. + +### 4.3 Rekor integration (local mirror, future public) + +* **Local Rekor mirror first.** Implement the TODO from FR-REKOR-1 and the release playbook: run a Rekor v2 (tile-backed) instance inside the sovereign environment and submit DSSE/SBOM digests there. ([Gitea: Git with a cup of tea][1]) +* **Receipts & proofs:** + * Capture Rekor receipts: checkpoint, inclusion proof, and leaf hash/UUID. + * Store them alongside scan results and in replay manifests so they can be verified fully offline. ([Sigstore][8]) +* **Offline kits & tiles:** When OUKs are built, include: + * The minimal Rekor tile ranges and recent checkpoints needed to verify new entries. + * A verification job that proves entries are included/consistent before publishing the OUK. ([Gitea: Git with a cup of tea][5]) + +### 4.4 VEX / OpenVEX + +* **Use VEX for exploitability, not ad-hoc flags.** Represent vulnerability status with VEX, ideally OpenVEX (format-agnostic) plus CycloneDX-embedded VEX where useful. ([cyclonedx.org][4]) +* **Meet CISA minimum fields:** Ensure VEX documents include: + * Document metadata (ID, version, author, timestamps), + * Statement metadata (IDs, timestamps), + * Product + vulnerability identifiers, and + * Status + justification/impact, following the CISA “Minimum Requirements for VEX” and CycloneDX mappings. ([Medium][9]) +* **Link everything:** + * Each VEX statement must reference the SBOM component via BOM-ref/purl and the product/version it applies to. + * Where possible, attach Rekor UUIDs or bundle IDs as evidence so auditors can trace “why we claimed not-affected”. ([Gitea: Git with a cup of tea][1]) + +--- + +## 5. Plug-in and module implementors + +Stella Ops is designed to be **open & modular**: scanner plug-ins, attestors, registry helpers, etc. ([Gitea: Git with a cup of tea][6]) + +### 5.1 Plug-in behavior + +* **Lifecycle & safety:** + * Plug-ins must be hot-loadable (.NET plug-ins without restarting core services). + * Fail closed: if a plug-in crashes or times out, the verdict must be conservative or clearly marked partial. ([Gitea: Git with a cup of tea][1]) +* **Security:** + * No network access by default; if absolutely necessary, go through a vetted HTTP client that obeys offline mode and proxy settings. + * Validate all input from the core platform; treat SBOMs and advisory data as untrusted until parsed/validated. +* **Observability & quotas:** + * Export plug-in specific metrics (latency, error rate). + * Never bypass quotas; the host decides whether a scan is allowed, not the plug-in. + +### 5.2 New modules/services + +If you add a new service (e.g., runtime probe, custom advisory indexer): + +* Put the design through an ADR + “Module dossier” doc (API, data flow, failure modes). +* Integrate into the release pipeline: lint, tests, SBOM, signing, then (future) Rekor. ([Gitea: Git with a cup of tea][5]) +* Confirm the service can run **completely offline** in a lab setup using only OUK + mirrored registries. + +--- + +## 6. Release & CI/CD expectations for implementors + +The Release Engineering Playbook is canon for how code becomes a signed, air-gap-friendly release. ([Gitea: Git with a cup of tea][5]) + +As an implementor: + +* **Keep `main` always releasable.** Broken builds break the build, not the process: fix or revert quickly. +* **Wire your changes into the pipeline:** + * Lint and tests for any new language/area you introduce. + * SBOM stage updated to include any new image. + * Signing and OUK stages updated if they touch your artefacts. ([Gitea: Git with a cup of tea][5]) +* **Guard offline assumptions:** Add/keep tests that: + * Run the full stack without Internet. + * Verify that SBOMs, attestations, and OUK updates all validate successfully. + +--- + +## 7. Licensing, attribution, and governance + +Stella Ops is AGPL-3.0-or-later and the governance docs + release playbook set clear expectations. ([Stella Ops][10]) + +* **AGPL network clause:** If you run a modified Stella Ops for others over a network, you must provide the source of that exact version. Don’t introduce proprietary modules that violate this. ([Stella Ops][10]) +* **No hidden SaaS reselling:** Re-hosting as a service is disallowed without prior consent; design and documentation should not encourage “stealth SaaS” business models on top of Stella Ops. ([Gitea: Git with a cup of tea][5]) +* **Attribution:** Keep the original Stella Ops attribution in the UI and CLI, even in forks; don’t strip branding. ([Gitea: Git with a cup of tea][5]) +* **Contribution hygiene:** Ensure all MRs: + * Use DCO / Signed-off-by trailers, + * Include tests + docs, + * Record significant architectural changes in ADRs and module docs. ([Gitea: Git with a cup of tea][5]) + +--- + +## 8. A practical “before you merge” checklist + +When you’re about to merge a feature or module, you should be able to answer “yes” to: + +1. Does this feature align with a documented requirement or ADR? ([Gitea: Git with a cup of tea][1]) +2. Does it preserve offline-first behavior and deterministic replay? +3. Does it operate on SBOMs as the source of truth and, if relevant, express exploitability via VEX/OpenVEX? ([Gitea: Git with a cup of tea][1]) +4. Are provenance, DSSE attestations, and (where implemented) Rekor receipts generated or at least planned? ([Gitea: Git with a cup of tea][5]) +5. Are performance, quotas, and Prometheus metrics respected? ([Gitea: Git with a cup of tea][1]) +6. Are SBOMs, signatures, and licensing for any new dependencies in order? ([Gitea: Git with a cup of tea][5]) +7. Are docs updated (API/CLI reference, module dossier, or user guide where appropriate)? ([Stella Ops][2]) + +If you want, I can next turn this into a short `docs/dev/implementor-guidelines.md` skeleton you can drop straight into the repo. + +[1]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/10212d67c0dfa0dd849fd798c24502b773664749/docs/05_SYSTEM_REQUIREMENTS_SPEC.md "git.stella-ops.org/05_SYSTEM_REQUIREMENTS_SPEC.md at 10212d67c0dfa0dd849fd798c24502b773664749 - git.stella-ops.org - Gitea: Git with a cup of tea" +[2]: https://stella-ops.org/docs/ "Stella Ops – Open • Sovereign • Modular container security" +[3]: https://stella-ops.org/ "Stella Ops – Open • Sovereign • Modular container security" +[4]: https://cyclonedx.org/capabilities/vex/ "Vulnerability Exploitability eXchange (VEX) | CycloneDX" +[5]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/raw/commit/d0c95cf32874e5b2fe5a7b5fac15cd52f17022b1/docs/13_RELEASE_ENGINEERING_PLAYBOOK.md "git.stella-ops.org" +[6]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/a77eb92feeafb9d0dfc75507a01f9c659db19b08/docs/02_WHY.md "git.stella-ops.org/02_WHY.md at a77eb92feeafb9d0dfc75507a01f9c659db19b08 - git.stella-ops.org - Gitea: Git with a cup of tea" +[7]: https://slsa.dev/spec/v1.0/levels "SLSA • Security levels" +[8]: https://docs.sigstore.dev/logging/overview/ "Rekor - Sigstore" +[9]: https://medium.com/@interlynkblog/implementing-minimum-requirements-for-vex-622f073c2f74 "Implementing Minimum Requirements for VEX | by Interlynk | Medium" +[10]: https://stella-ops.org/license/ "Stella Ops – Signed Reachability · Deterministic Replay · Sovereign Crypto" diff --git a/docs/product-advisories/30-Nov-2025 - Reachability Benchmark Fixtures Snapshot.md b/docs/product-advisories/30-Nov-2025 - Reachability Benchmark Fixtures Snapshot.md new file mode 100644 index 000000000..c5081a021 --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Reachability Benchmark Fixtures Snapshot.md @@ -0,0 +1,582 @@ +I thought you might want a sharper picture of what’s out there — and what’s credible — for building a compact “golden set” of redistributable fixtures to drop into StellaOps’ deterministic reachability benchmark. Here’s a quick breakdown of the main external benchmark sources that map closely to your list, and what you’d get from integrating them. + +--- + +## 🔎 Key benchmark suites & what they bring + +### **SV-COMP (C reachability / verification tasks)** + +* SV-COMP is the canonical benchmark for software verification tools, providing curated C programs + reachability specs (safety, mem-safety, overflow, termination). ([SV-COMP][1]) +* Tasks are published via tagged git releases (e.g., `svcomp25`), so you can reproducibly fetch exact sources and property definitions. +* These tasks give deterministic ground truth about reachability/unreachability. + +**Why it matters for StellaOps:** plug in real-world-like C code + formal specs. + +### **OSS-Fuzz (reproducer corpus for fuzzed bugs)** + +* Each OSS-Fuzz issue includes a deterministic reproducer file. ([Google GitHub][2]) +* Replaying the reproducer yields the same crash if the binary & sanitizers are consistent. +* Public corpora can become golden fixtures. + +**Why it matters:** covers real-world bugs beyond static properties. + +--- + +## ❌ Not perfectly covered (Tier-2 options) + +* Juliet / OWASP / Java/Python suites lack a single authoritative, stable distribution. +* Package snapshots (Debian/Alpine) need manual CVE + harness mapping. +* Curated container images (Vulhub) require licensing vetting and orchestration. +* Call-graph corpora (NYXCorpus, SWARM) have no guaranteed stable labels. + +**Implication:** Tier-2 fixtures need frozen versions, harness engineering, and license checks. + +--- + +## ✅ Compact “golden set” candidate + +| # | Fixture source | Ground truth | +|---|----------------|--------------| +| 1 | SV-COMP ReachSafety / MemSafety / NoOverflows | C programs + formal reachability specs (no `reach_error()` calls, no overflows). | +| 2 | OSS-Fuzz reproducer corpus (C/C++) | Deterministic crash inputs triggering CVEs. | +| 3 | OSS-Fuzz seed corpus | Known-safe vs bug-triggering inputs for stability comparisons. | +| 4 | Additional SV-COMP categories (Heap, Bitvector, Float) | Broader token coverage. | +| 5 | Placeholder: Debian/Alpine package snapshots | Manual metadata + CVEs; build harnesses. | +| 6 | Placeholder: Java/Python OWASP/Juliet-inspired fixtures | Dynamic languages coverage; custom instrumentation required. | +| 7 | Placeholder: Curated vulnerable container images (Vulhub) | Real-world deployment exposures. | +| 8 | Placeholder: Call-graph corpora (NYXCorpus/SWARM) | Dataflow reachability under complex graphs; requires tooling. | + +Start with SV-COMP + OSS-Fuzz as Tier-1; add Tier-2 once harnesses & snapshots are ready. + +--- + +## ⚠️ Watch-outs for Tier-2 + +* Corpus maintenance (OSS-Fuzz seeds can change) requires freezing sources. +* Label ambiguity: container/Java fixtures must have precise success/failure definitions. +* Licensing/distribution for some corpa/containers must be vetted. +* Call-graph/dynamic cases demand instrumentation to produce deterministic labels. + +--- + +## 🎯 Recommendation + +Adopt a “Tier-1 Cadre” (SV-COMP + OSS-Fuzz) for deterministic ground truth. Expand Tier-2 (packages, Java/Python, containers, call graphs) as separate projects with frozen fixtures, metadata (CWE/CVE, PURLs, CycloneDX), and verified harnesses. + +Want me to draft an 8–12 fixture “starter pack” (paths, metadata, PURLs) you can copy into StellaOps? Let me know. + +[1]: https://sv-comp.sosy-lab.org/2025/benchmarks.php?utm_source=chatgpt.com "Benchmark Verification Tasks - SV-COMP" +[2]: https://google.github.io/oss-fuzz/advanced-topics/reproducing/?utm_source=chatgpt.com "Reproducing OSS-Fuzz issues" +[3]: https://github.com/google/oss-fuzz/discussions/12278?utm_source=chatgpt.com "Corpus management in OSS-Fuzz" +Good, let’s design this as a real “v0.1 Golden Set” spec your team can implement, not just a loose starter pack. + +I’ll give you: + +1. Design goals and constraints +2. Repository layout for the benchmark +3. Fixture metadata schema (YAML) +4. A concrete Core-10 Golden Fixture set (each with purpose & ground truth) +5. How to wire this into StellaOps (Scanner/Sbomer/Vexer/UnknownsRegistry/Authority) +6. A short implementation plan for your team + +--- + +## 1. Design goals & constraints + +Non-negotiables: + +* **Deterministic**: same input → same graph, same verdicts, same logs; no network, no time, no randomness. +* **Compact**: ~10 tiny fixtures, each buildable and runnable in seconds, small SBOMs. +* **Redistributable**: avoid licensing traps by: + + * Preferring **synthetic code** owned by you (MIT/BSD-style). + * For “realistic” CVEs, use **fake local IDs** (e.g. `FAKE-CVE-2025-0001`) and local feeds, not NVD data. +* **Complete chain**: every fixture ships with: + + * Source + build recipe + * Binary/container + * CycloneDX SBOM + * Local vulnerability feed entries (OSV-like or your own schema) + * Reference VEX document (OpenVEX or CycloneDX VEX) + * Expected graph revision ID + reachability verdicts +* **Coverage of patterns**: + + * Safe vs unsafe variants (Not Affected vs Affected) + * Intra-procedural, inter-procedural, transitive dep + * OS package vs app-level deps + * Multi-language (C, .NET, Java, Python) + * Containerized vs bare-metal + +--- + +## 2. Scope of v0: Core-10 Golden Fixtures + +Two tiers, but all small: + +* **Core-10** (what you ship and depend on for regression): + + * 5 native/C fixtures (classic reachability & library shape) + * 1 .NET fixture + * 1 Java fixture + * 1 Python fixture + * 2 container fixtures (OS package style) +* **Extended-X** (optional later): fuzz-style repros, dynamic imports, concurrency, etc. + +Below I’ll detail the Core-10 so your team can actually implement them. + +--- + +## 3. Repository layout + +Recommended layout inside `stella-ops` mono-repo: + +```text +benchmarks/ + reachability/ + golden-v0/ + fixtures/ + C-REACH-UNSAFE-001/ + C-REACH-SAFE-002/ + C-MEM-BOUNDS-003/ + C-INT-OVERFLOW-004/ + C-LIB-TRANSITIVE-005/ + CONTAINER-OSPKG-SAFE-006/ + CONTAINER-OSPKG-UNSAFE-007/ + JAVA-HTTP-UNSAFE-008/ + DOTNET-LIB-PAIR-009/ + PYTHON-IMPORT-UNSAFE-010/ + feeds/ + osv-golden.json # “FAKE-CVE-*” style vulnerabilities + authority/ + vex-reference-index.json # mapping fixture → reference VEX + expected verdict + README.md +``` + +Each fixture folder: + +```text +fixtures// + src/ # source code (C, C#, Java, Python, Dockerfile...) + build/ + Dockerfile # if containerised + build.sh # deterministic build commands + artifacts/ + binary/ # final exe/jar/dll/image.tar + sbom.cdx.json # CycloneDX SBOM (canonical, normalized) + vex.openvex.json # reference VEX verdicts + manifest.fixture.yaml + graph.reference.json # canonical normalized graph + graph.reference.sha256.txt # hash = "Graph Revision ID" + docs/ + explanation.md # human-readable root-cause + reachability explanation +``` + +--- + +## 4. Fixture metadata schema (`manifest.fixture.yaml`) + +```yaml +id: "C-REACH-UNSAFE-001" +name: "Simple reachable error in C" +version: "0.1.0" +language: "c" +domain: "native" +category: + - "reachability" + - "control-flow" +ground_truth: + vulnerable: true + reachable: true + cwes: ["CWE-754"] # Improper Check for Unusual or Exceptional Conditions + fake_cves: ["FAKE-CVE-2025-0001"] + verdict: + property_type: "reach_error_unreachable" + property_holds: false + explanation_ref: "docs/explanation.md" + +build: + type: "local" + environment: "debian:12" + commands: + - "gcc -O0 -g -o app main.c" + outputs: + binary: "artifacts/binary/app" + +run: + command: "./artifacts/binary/app" + args: [] + env: {} + stdin: "" + expected: + exit_code: 1 + stdout_contains: ["REACH_ERROR"] + stderr_contains: [] + # Optional coverage or traces you might add later + coverage_file: null + +sbom: + path: "artifacts/sbom.cdx.json" + format: "cyclonedx-1.5" + +vex: + path: "artifacts/vex.openvex.json" + format: "openvex-0.2" + statement_ids: + - "vex-statement-1" + +graph: + reference_path: "artifacts/graph.reference.json" + revision_id_sha256_path: "artifacts/graph.reference.sha256.txt" + +stellaops_tags: + difficulty: "easy" + focus: + - "control-flow" + - "single-binary" + used_by: + - "scanner.webservice" + - "sbomer" + - "vexer" + - "excititor" +``` + +Your team can add more, but this is enough to wire the benchmark into the pipeline. + +--- + +## 5. Core-10 Golden Fixtures (concrete proposal) + +I’ll describe each with: purpose, pattern, what the code does, and ground truth. + +### 5.1 `C-REACH-UNSAFE-001` – basic reachable error + +* **Purpose**: baseline reachability detection on a tiny C program. +* **Pattern**: single `main`, simple branch, error sink function `reach_error()`. +* **Code shape**: + + * `main(int argc, char** argv)` parses integer `x`. + * If `x == 42`, it calls `reach_error()`, which prints `REACH_ERROR` and exits 1. +* **Ground truth**: + + * Vulnerable: `true`. + * Reachable: `true` if run with `x=42` (you fix input in `run.args`). + * Property: “reach_error is unreachable” → `false` (counterexample exists). +* **Why it’s valuable**: + + * Exercises simple control flow; used as “hello world” of deterministic reachability. + +### 5.2 `C-REACH-SAFE-002` – safe twin of 001 + +* **Purpose**: same SBOM shape, but no reachable error, to test “Not Affected”. +* **Pattern**: identical to 001 but with an added guard. +* **Code shape**: + + * For example, check `x != 42` or remove path to `reach_error()`. +* **Ground truth**: + + * Vulnerable: false (no call to `reach_error` at all) *or* treat it as “patched”. + * Reachable: false. + * Property “reach_error is unreachable” → `true`. +* **Why**: + + * Used to verify that graph revision is different and VEX becomes “Not Affected” for the same fake CVE (if you model it that way). + +### 5.3 `C-MEM-BOUNDS-003` – out-of-bounds write + +* **Purpose**: exercise memory-safety property and CWE mapping. +* **Pattern**: fixed-size buffer + unchecked copy. +* **Code shape**: + + * `char buf[16];` + * Copies `argv[1]` into `buf` with `strcpy` or manual loop without bounds check. +* **Ground truth**: + + * Vulnerable: true. + * Reachable: true on any input with length > 15 (you fix a triggering arg). + * CWEs: `["CWE-119", "CWE-120"]`. +* **Expected run**: + + * With ASAN or similar, exit non-zero, mention heap/buffer overflow; for determinism, you can standardize to exit code 139 and not rely on sanitizer text in tests. + +### 5.4 `C-INT-OVERFLOW-004` – integer overflow + +* **Purpose**: test handling of arithmetic / overflow-related vulnerabilities. +* **Pattern**: multiplication or addition with insufficient bounds checking. +* **Code shape**: + + * Function `size_t alloc_size(size_t n)` that does `n * 16` without overflow checks, then allocates and writes. +* **Ground truth**: + + * Vulnerable: true. + * Reachable: true with crafted large `n`. + * CWEs: `["CWE-190", "CWE-680"]`. +* **Why**: + + * Lets you validate that your vulnerability feed (fake CVE) asserts “affected” on this component, and your reachability engine confirms the path. + +### 5.5 `C-LIB-TRANSITIVE-005` – vulnerable library, unreachable in app + +* **Purpose**: test the core SBOM→VEX story: component is vulnerable, but not used. +* **Pattern**: + + * `libvuln.a` with function `void do_unsafe(char* input)` containing the same OOB bug as 003. + * `app.c` links to `libvuln` but never calls `do_unsafe()`. +* **Code shape**: + + * Build static library from `libvuln.c`. + * Build `app` that uses only `do_safe()` from `libvuln.c` or that just links but doesn’t call anything from the “unsafe” TU. +* **SBOM**: + + * SBOM lists `component: "pkg:generic/libvuln@1.0.0"` with `fake_cves: ["FAKE-CVE-2025-0003"]`. +* **Ground truth**: + + * Vulnerable component present in SBOM: yes. + * Reachable vulnerable function: no. + * Correct VEX: “Not Affected: vulnerable code not in execution path for this product”. +* **Why**: + + * Canonical demonstration of correct VEX semantics on real-world pattern: vulnerable lib, harmless usage. + +--- + +### 5.6 `CONTAINER-OSPKG-SAFE-006` – OS package, unused binary + +* **Purpose**: simulate vulnerable OS package installed but unused, to test image scanning vs reachability. +* **Pattern**: + + * Minimal container (e.g. `debian:12-slim` or `alpine:3.x`) with installed package `vuln-tool` that is never invoked by the entrypoint. + * Your app is a trivial “hello” binary. +* **SBOM**: + + * OS-level components include `pkg:generic/vuln-tool@1.0.0`. +* **Ground truth**: + + * Vulnerable: the package is flagged by local feed. + * Reachable: false under the specified `CMD` and test scenario. + * VEX: “Not Affected – vulnerable code present but not invoked in product’s operational context.” +* **Why**: + + * Tests that StellaOps does not over-report image-level CVEs when nothing in the product’s execution profile uses them. + +### 5.7 `CONTAINER-OSPKG-UNSAFE-007` – OS package actually used + +* **Purpose**: same as 006 but positive case: vulnerability is reachable. +* **Pattern**: + + * Same base image and package. + * Entrypoint script calls `vuln-tool` with crafted input that triggers the bug. +* **Ground truth**: + + * Vulnerable: true. + * Reachable: true. + * This should flip the VEX verdict vs 006. +* **Why**: + + * Verifies that your reachability engine + runtime behaviour correctly distinguish “installed but unused” from “installed and actively exploited.” + +--- + +### 5.8 `JAVA-HTTP-UNSAFE-008` – vulnerable route in minimal Java service + +* **Purpose**: test JVM + HTTP + transitive dep reachability. +* **Pattern**: + + * Small Spring Boot or JAX-RS service with: + + * `/safe` endpoint using only safe methods. + * `/unsafe` endpoint calling a method in `vuln-lib` that has a simple bug (e.g. path traversal or unsafe deserialization). +* **SBOM**: + + * Component `pkg:maven/org.stellaops/vuln-lib@1.0.0` linked to `FAKE-CVE-2025-0004`. +* **Ground truth**: + + * For an HTTP call to `/unsafe`, vulnerability reachable. + * For `/safe`, not reachable. +* **Benchmark convention**: + + * Fixture defines `run.unsafe` and `run.safe` commands in manifest (two separate “scenarios” under one fixture ID, or two sub-cases in `manifest.fixture.yaml`). +* **Why**: + + * Exercises language-level dependency resolution, transitive calls, and HTTP entrypoints. + +--- + +### 5.9 `DOTNET-LIB-PAIR-009` – .NET assembly with safe & unsafe variants + +* **Purpose**: cover your home turf: .NET 10 / C# pipeline + SBOM & VEX. +* **Pattern**: + + * `Golden.Banking.Core` library with method: + + * `public void Process(string iban)` → suspicious string parsing / regex or overflow. + * Two apps: + + * `Golden.Banking.App.Unsafe` that calls `Process()` with unsafe behaviour. + * `Golden.Banking.App.Safe` that never calls `Process()` or uses a safe wrapper. +* **SBOM**: + + * Component `pkg:nuget/Golden.Banking.Core@1.0.0` tied to `FAKE-CVE-2025-0005`. +* **Ground truth**: + + * For `App.Unsafe`, vulnerability reachable. + * For `App.Safe`, not reachable. +* **Why**: + + * Validates your .NET tooling (Sbomer, scanner.webservice) and that your graphs respect assembly boundaries and call sites. + +--- + +### 5.10 `PYTHON-IMPORT-UNSAFE-010` – Python optional import pattern + +* **Purpose**: basic coverage for dynamic / interpreted language with optional module. +* **Pattern**: + + * `app.py`: + + * Imports `helper` which conditionally imports `vuln_mod` when `ENABLE_VULN=1`. + * When enabled, calling `/unsafe` function triggers, e.g., `eval(user_input)`. +* **SBOM**: + + * Component `pkg:pypi/vuln-mod@1.0.0` → `FAKE-CVE-2025-0006`. +* **Ground truth**: + + * With `ENABLE_VULN=0`, vulnerable module not imported → unreachable. + * With `ENABLE_VULN=1`, reachable. +* **Why**: + + * Simple but realistic test for environment-dependent reachability and Python support. + +--- + +## 6. Local vulnerability feed for the Golden Set + +To keep everything sovereign and deterministic, define a small internal OSV-like JSON feed, e.g. `benchmarks/reachability/golden-v0/feeds/osv-golden.json`: + +```json +{ + "vulnerabilities": [ + { + "id": "FAKE-CVE-2025-0001", + "summary": "Reachable error in sample C program", + "aliases": [], + "affected": [ + { + "package": { + "ecosystem": "generic", + "name": "C-REACH-UNSAFE-001" + }, + "ranges": [{ "type": "SEMVER", "events": [{ "introduced": "0" }] }] + } + ], + "database_specific": { + "stellaops_fixture_id": "C-REACH-UNSAFE-001" + } + } + // ... more FAKE-CVE defs ... + ] +} +``` + +Scanner/Feedser in “golden mode” should: + +* Use **only** this feed. +* Produce deterministic, closed-world graphs and VEX decisions. + +--- + +## 7. Integration hooks with StellaOps + +Make sure each module has a clear use of the golden set: + +* **Scanner.Webservice** + + * Input: SBOM + local feed for a fixture. + * Output: canonical graph JSON and SHA-256 revision. + * For each fixture, compare produced `revision_id` against `graph.reference.sha256.txt`. +* **Sbomer** + + * Rebuilds SBOM from source/binaries and compares it to `artifacts/sbom.cdx.json`. + * Fails test if SBOMs differ in canonicalized form. +* **Vexer / Excititor** + + * Ingests graph + local feed and produces VEX. + * Compare resulting VEX to `artifacts/vex.openvex.json`. +* **UnknownsRegistry** + + * For v0 you can keep unknowns minimal, but: + + * At least one fixture (e.g. Python or container) can contain a “deliberately un-PURL-able” file to confirm it enters Unknowns with expected half-life. +* **Authority** + + * Signs the reference artifacts: + + * SBOM + * Graph + * VEX + * Ensures deterministic attestation for the golden set (you can later publish these as public reference proofs). + +--- + +## 8. Implementation plan for your team + +You can drop this straight into a ticket or doc. + +1. **Scaffold repo structure** + + * Create `benchmarks/reachability/golden-v0/...` layout as above. + * Add a top-level `README.md` describing goals and usage. + +2. **Implement the 10 fixtures** + + * Each fixture: write minimal code, build scripts, and `manifest.fixture.yaml`. + * Keep code tiny (1–3 files) and deterministic (no network, no randomness, no wall time). + +3. **Generate SBOMs** + + * Use your Sbomer for each artifact. + * Normalize / canonicalize SBOMs and commit them as `artifacts/sbom.cdx.json`. + +4. **Define FAKE-CVE feed** + + * Create `feeds/osv-golden.json` with 1–2 entries per fixture. + * Map each entry to PURLs used in SBOMs. + +5. **Produce reference graphs** + + * Run Scanner in “golden mode” on each fixture’s SBOM + feed. + * Normalize graphs (sorted JSON, deterministic formatting). + * Compute SHA-256 → store in `graph.reference.sha256.txt`. + +6. **Produce reference VEX documents** + + * Run Vexer / Excititor with graphs + feed. + * Manually review results, edit as needed. + * Save final accepted VEX as `artifacts/vex.openvex.json`. + +7. **Write explanations** + + * For each fixture, add `docs/explanation.md`: + + * 5–10 lines explaining root cause, path, and why affected / not affected. + +8. **Wire into CI** + + * Add a `GoldenReachabilityTests` job that: + + * Builds all fixtures. + * Regenerates SBOM, graph, and VEX. + * Compares against reference artifacts. + * Fail CI if any fixture drifts. + +9. **Expose as a developer command** + + * Add a CLI command, e.g.: + + * `stellaops bench reachability --fixture C-REACH-UNSAFE-001` + * So developers can locally re-run single fixtures during development. + +--- + +If you want, next step I can: + +* Take 2–3 of these fixtures (for example `C-REACH-UNSAFE-001`, `C-LIB-TRANSITIVE-005`, and `DOTNET-LIB-PAIR-009`) and draft **actual code sketches + full `manifest.fixture.yaml`** so your devs can literally copy-paste and start implementing. diff --git a/docs/product-advisories/30-Nov-2025 - Rekor Receipt Checklist for Stella Ops.md b/docs/product-advisories/30-Nov-2025 - Rekor Receipt Checklist for Stella Ops.md new file mode 100644 index 000000000..553eb7caf --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Rekor Receipt Checklist for Stella Ops.md @@ -0,0 +1,409 @@ +Here’s a practical, Stella Ops–ready checklist for **what a Rekor v2 “receipt” must capture**, plus metadata for air-gapped delivery and deterministic re-verification—mapped to the module that must own each field and why it matters. + +*(Quick background: Sigstore’s Rekor v2 is GA and uses a redesigned, cheaper-to-run transparency log; clients (cosign v2.6+) and bundle format are updated. Receipts/inclusion proofs + bundles enable offline verification and replay.)* ([Sigstore Blog][1]) + +--- + +# Must-have (atomic, required) + +* **tlog URL (authority endpoint)** → **Authority** + Ensures we’re verifying against the exact Rekor instance (public or private mirror). Needed to fetch/validate inclusion proofs and checkpoints. ([Sigstore Blog][1]) + +* **Rekor log public key (or key ID/fingerprint)** → **Authority** + Trust root for verifying Signed Tree Heads (STHs)/checkpoints and inclusion proofs. Store pinned key for 2025 Rekor v2 (and rotation support). ([Sigstore Blog][1]) + +* **Log checkpoint at verification time (STH digest + tree size + timestamp)** → **Scheduler** + Proves append-only consistency window used during verification; enables later consistency proofs. ([Sigstore Blog][2]) + +* **Entry locator (UUID and/or log index)** → **Sbomer** + Stable handle to the exact log entry for the artifact’s signature/attestation. ([Chainguard Academy][3]) + +* **Inclusion proof (audit path/merkle hashes)** → **Authority** + Cryptographic proof that the entry is included in the log corresponding to the checkpoint. ([Sigstore][4]) + +* **Sigstore Bundle (embedded signature/attestation + verification material)** → **Sbomer** + Persist the **Sigstore bundle** emitted by clients (cosign, sigstore-python). It carries the DSSE envelope, certs, TSA timestamp, and (when applicable) transparency details for offline replay. ([Sigstore][5]) + +* **Fulcio certificate chain (PEM, short-lived)** → **Authority** + Needed to validate keyless identities tied to OIDC claims; keep chain alongside bundle for air-gap. ([Sigstore][6]) + +* **Subject digest(s) (e.g., image/artifact SHA-256)** → **Sbomer** + Binds the receipt to the exact bits we verified; required to re-prove later. ([OpenSSF][7]) + +* **Verification policy hash (the policy used at verify time)** → **Vexer** + Pin the exact policy (rules, identity matchers, Rekor requirement) so future re-checks are deterministic. ([OKD Documentation][8]) + +--- + +# Nice-to-have (repro, forensics, air-gap) + +* **TUF snapshot/version for Rekor/Fulcio bundles** → **Authority** + Records which distributed metadata set was trusted when verifying (key rotations, URL rollout timing). ([Sigstore Blog][1]) + +* **Client + version (cosign/sigstore-python), verify flags** → **Scheduler** + Replays the same client logic/version; helpful when formats/behaviors change across releases. ([Sigstore Blog][1]) + +* **Identity claims summary** → **Authority** + Compact snapshot (issuer, subject/email, SANs) for audit trails without re-parsing certs. ([Sigstore][6]) + +* **Timestamp Authority (TSA) evidence (token/counter-signature)** → **Authority** + Anchors signing time within cert validity; crucial for keyless flows. ([OpenSSF][7]) + +* **Local mirror info** → **Feedser/Concelier** + Mirror URL, sync height/tree size, mirror key for proving private mirror consistency with public instance. ([OpenSSF][9]) + +* **Repro inputs hash: policy + trust bundle + Rekor key + toolchain** → **Vexer** + One hash to assert the exact verifier environment used. + +* **Attestation payload digest + size (DSSE/intoto)** → **Sbomer** + Guard vs payload truncation and size pitfalls. ([GitHub][10]) + +* **Monitor evidence: last consistency check** → **Scheduler** + Records proof the log remained consistent between checkpoints. ([Sigstore Blog][2]) + +--- + +# Ownership map + +* **Authority**: Rekor URL/pkey, Fulcio chain/TSA/TUF refs, inclusion proofs, checkpoints, identity summaries. +* **Sbomer**: bundle blob, entry UUID/index, subject digests, attestation digests/sizes. +* **Vexer (Excititor)**: policy hash, repro hash, uses Authority/Sbomer data for verdicts. +* **Feedser/Concelier**: mirror URLs/heights, provenance of snapshots. +* **Scheduler**: client versions/flags, timestamps, monitor checks. +* **UnknownsRegistry**: tracks missing receipt elements or unverifiable fields for follow-up. + +--- + +# Storage conventions + +* **Receipt document**: `application/json` with canonical field order + normalized encodings (PEM → base64 DER) + explicit bytes hash (CID/sha256). +* **Bundle storage**: keep raw Sigstore bundle plus a normalized copy for indexing. ([Sigstore][5]) +* **Proof linkage**: store `checkpoint_hash`, `inclusion_proof_hash`, and `bundle_hash` so Vexer can verify all before marking “Verified”. + +--- + +# Air-gapped delivery flow + +1. **Seed trust**: ship Rekor public key, Fulcio bundle, TSA root, pinned checkpoint. ([Sigstore Blog][1]) +2. **Ship artefact**: Sbomer writes bundle; Authority adds proofs; Scheduler stamps tooling metadata. +3. **Verify offline**: Vexer checks DSSE, Fulcio chain, TSA time, and inclusion proof against pinned data. +4. **Mirror**: Feedser syncs private Rekor mirror; consistency proofs keep it honest. ([OpenSSF][9]) + +--- + +# Minimal JSON schema (starter) + +```json +{ + "tlog": { + "url":"https://rekor.sigstore.dev", + "publicKey":{"type":"pem","fingerprint":"…"}, + "checkpoint":{"treeSize":123456,"signedNote":"base64","timestamp":"2025-10-10T12:34:56Z"}, + "entry":{"uuid":"…","logIndex":987654}, + "inclusionProof":{"hashAlgo":"SHA256","auditPath":["…","…"]} + }, + "bundle":{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=…","blob":"base64","digest":"sha256:…"}, + "subject":[{"name":"registry/org/app@sha256:…","digest":"sha256:…"}], + "fulcio":{"chainPem":["-----BEGIN CERTIFICATE-----…"]}, + "tsa":{"rootPem":"-----BEGIN CERTIFICATE-----…","tokenDigest":"sha256:…"}, + "policy":{"id":"vexer-policy-v1","sha256":"…"}, + "tooling":{"client":"cosign","version":"v2.6.0","flags":["--bundle","--rekor-url=…"]}, + "monitor":{"fromTreeSize":120000,"toTreeSize":123456,"consistencyProof":"sha256:…"}, + "provenance":{"tufSnapshot":"…","mirror":{"url":"https://rekor.mirror.local","treeSize":123456,"publicKey":"…"}} +} +``` + +--- + +If you want, I’ll turn this into `docs/security/receipts.md` plus a C# POCO/EF Core entity so Authority/Sbomer/Vexer can plumb it straight into the data model. + +[1]: https://blog.sigstore.dev/rekor-v2-ga/?utm_source=chatgpt.com "Rekor v2 GA - Cheaper to run, simpler to maintain" +[2]: https://blog.sigstore.dev/using-rekor-monitor/?utm_source=chatgpt.com "Using rekor-monitor to Scan Your Transparency Logs" +[3]: https://edu.chainguard.dev/open-source/sigstore/rekor/an-introduction-to-rekor/?utm_source=chatgpt.com "An Introduction to Rekor" +[4]: https://docs.sigstore.dev/logging/overview/?utm_source=chatgpt.com "Rekor" +[5]: https://docs.sigstore.dev/about/bundle/?utm_source=chatgpt.com "Sigstore Bundle Format" +[6]: https://docs.sigstore.dev/about/overview/?utm_source=chatgpt.com "Sigstore: Overview" +[7]: https://openssf.org/blog/2024/05/24/introducing-artifact-attestations-now-in-public-beta/?utm_source=chatgpt.com "Introducing Artifact Attestations—Now in Public Beta" +[8]: https://docs.okd.io/latest/nodes/nodes-sigstore-using.html?utm_source=chatgpt.com "Manage secure signatures with sigstore | Nodes | OKD 4" +[9]: https://openssf.org/blog/2025/10/15/announcing-the-sigstore-transparency-log-research-dataset/?utm_source=chatgpt.com "Announcing the Sigstore Transparency Log Research ..." +[10]: https://github.com/sigstore/cosign/issues/3599?utm_source=chatgpt.com "Attestations require uploading entire payload to rekor #3599" + + +Here’s a practical set of guidelines you can use as a north star when **implementing StellaOps**—both for *platform owners/operators* and for *teams building or extending StellaOps modules*. + +I’ll structure this as: + +1. Cross‑cutting principles (what must never be broken) +2. A phased implementation roadmap +3. Guidelines by concern (auth, data, CI, observability, offline, security) +4. Short checklists you can literally paste into your runbooks + +--- + +## 1. Cross‑cutting principles + +These are baked into the official StellaOps docs and should guide every implementation decision.([Gitea: Git with a cup of tea][1]) + +**1.1 Determinism & replay first** + +* All “facts” (SBOMs, advisories, VEX, reachability, logs) must be **replayable**: + + * Stable JSON key ordering, UTC ISO‑8601 timestamps, content‑addressed bundles, deterministic hashing. + * Avoid wall‑clock or “now()” in evaluation paths; Policy Engine and analyzers should be deterministic for the same inputs.([Gitea: Git with a cup of tea][1]) +* When in doubt: add a hash or bundle and make it verifiable rather than adding a mutable flag. + +**1.2 Aggregation‑Only ingestion** + +* **Concelier** and **Excititor** are *aggregation‑only*: + + * They ingest, normalise, enrich with provenance and linksets, and write immutable `advisory_raw` / `vex_raw` docs.([Gitea: Git with a cup of tea][2]) + * They **must not** compute severity, consensus, suppressions, risk scores, or merges—that belongs to Policy Engine and downstream overlays.([Gitea: Git with a cup of tea][2]) + +**1.3 Thin gateway, smart services** + +* Gateway (or API edge) is **proxy only**: + + * AuthN/AuthZ, DPoP verification, scope enforcement, routing. + * No policy overlays, no business logic, no joining evidence there—those live in Policy Engine & domain services.([Gitea: Git with a cup of tea][3]) + +**1.4 Offline‑first & sovereignty** + +* Assume **air‑gapped or intermittently connected** operation: + + * Design for offline bundles, mirrors, and “kit” updates instead of live internet calls. + * BYO trust roots, regional crypto (FIPS/eIDAS/SM/etc.), and Rekor v2 mirroring are first‑class, not afterthoughts.([Gitea: Git with a cup of tea][1]) + +**1.5 Modular responsibility boundaries** + +From the module cheat sheet: each service has a crisp responsibility; implementations should respect that split.([Gitea: Git with a cup of tea][1]) + +* **Authority** – identity, tokens (OpToks), DPoP/mTLS, audit. +* **Scanner (Web + Worker)** – SBOM generation, analysis, replay bundles. +* **Signer & Attestor** – DSSE signing, PoE enforcement, Rekor v2 anchoring. +* **Concelier & Excititor** – raw advisory & VEX ingest (Aggregation‑Only Contract). +* **Policy Engine & Scheduler** – joins, overlays, scheduling remediation work. +* **Notify, Export Center, UI, CLI, Zastava, Registry Token Service, Graph** – UX, notifications, exports, runtime admission, tokens, graph views. + +When you’re “implementing StellaOps,” you’re plugging into this map—don’t blur the boundaries. + +--- + +## 2. Phased implementation roadmap + +Use this as a sequence for a *new* installation. It’s compatible with the official high‑level architecture, quickstart, and platform overview docs.([Gitea: Git with a cup of tea][4]) + +### Phase 0 – Plan environments & infra + +* Decide on **envs**: at minimum `dev`, `staging`, `prod`. For regulated/orgs, add `airgap‑preprod`. +* Stand up shared data plane per cluster: + + * MongoDB (canonical store, jobs, overlays).([Gitea: Git with a cup of tea][1]) + * MinIO or RustFS/object storage for CAS and replay bundles.([Gitea: Git with a cup of tea][4]) + * Queue backend (Redis Streams / NATS / RabbitMQ).([Gitea: Git with a cup of tea][4]) + * Telemetry stack (logs, metrics, traces) per `docs/modules/telemetry/architecture.md`.([Gitea: Git with a cup of tea][2]) +* For `dev`, you can lean on the **docker‑compose skeleton** in the high‑level architecture doc; for `staging/prod`, translate that to Kubernetes with the same service graph.([Gitea: Git with a cup of tea][4]) + +### Phase 1 – Core control plane: Authority, Gateway, Licensing + +* Deploy **StellaOps.Authority** with its plugin configuration under `etc/authority.plugins/*.yaml`.([Gitea: Git with a cup of tea][5]) +* Wire Authority to your IdP or use the Standard plugin for local users, observing: + + * Strong password policies + Argon2id hashing (see cryptography updates).([Gitea: Git with a cup of tea][6]) + * Short‑lived OpToks, DPoP keypairs in OS keychains, no long‑lived bearer tokens.([Gitea: Git with a cup of tea][7]) +* Introduce an **API Gateway** that: + + * Verifies JWT + DPoP, enforces scopes, and forwards to backend services.([Gitea: Git with a cup of tea][3]) +* Connect to the **Licensing Service** (`www.stella-ops.org`) to obtain Proof‑of‑Entitlement (PoE) for attesting installations, or deliberately run in throttled community mode.([Gitea: Git with a cup of tea][4]) + +### Phase 2 – Scanning & attestation path + +* Deploy: + + * `StellaOps.Scanner.WebService` and `StellaOps.Scanner.Worker`. + * `StellaOps.Signer` and `StellaOps.Attestor` plus Fulcio/Rekor v2 (or mirror).([Gitea: Git with a cup of tea][4]) +* Install **stella CLI** for operators/CI: + + * Native AOT binaries, config via `config.yaml` & env vars like `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, etc.([Gitea: Git with a cup of tea][7]) + * Enable Buildx SBOM generation via `StellaOps.Scanner.Sbomer.BuildXPlugin`.([Gitea: Git with a cup of tea][8]) +* Validate with the official quickstart: + + * `stella auth login` (device‑code) → `stella scan image ... --sbom-type cyclonedx-json`.([Gitea: Git with a cup of tea][9]) + +### Phase 3 – Evidence graph & policy + +* Add **Concelier** and **Excititor**: + + * Configure connectors for upstream feeds (NVD, distro advisories, GHSA, OSV, vendor VEX, etc.).([Gitea: Git with a cup of tea][1]) + * Ensure AOC guards (`StellaOps.Aoc`) and Mongo validators are enabled so `advisory_raw` / `vex_raw` are immutable and compliant.([Gitea: Git with a cup of tea][2]) +* Deploy **Policy Engine** and **Scheduler**: + + * Policy Engine joins SBOM inventory with raw advisories/VEX, produces `effective_finding_*` collections, and exposes simulation APIs.([Gitea: Git with a cup of tea][1]) + * Scheduler watches change streams and impact windows, driving re‑scans and policy re‑evaluation.([Gitea: Git with a cup of tea][1]) +* Bring up **Notify** for email/chat hooks based on policy events.([Gitea: Git with a cup of tea][1]) + +### Phase 4 – UX, exports, runtime + +* Deploy **UI** (Angular SPA) and its backend, **Export Center**, and **Registry Token Service**, then **Zastava** (runtime admission / posture).([Gitea: Git with a cup of tea][1]) +* Add **Offline Kit / Export** profiles so you can produce offline bundles for air‑gapped clusters.([Gitea: Git with a cup of tea][2]) + +You don’t have to do all phases at once; but don’t violate the dependencies in the high‑level diagram (e.g., don’t run Attestor without Authority & Rekor, don’t run Policy without AOC‑compliant ingest).([Gitea: Git with a cup of tea][4]) + +--- + +## 3. Guidelines by concern + +### 3.1 Identity, auth, and RBAC + +**Use Authority for everything** + +* All services and the CLI should authenticate via **StellaOps.Authority** using OpToks (short‑lived JWTs) bound to DPoP or mTLS.([Gitea: Git with a cup of tea][1]) +* Scopes are your main guardrail: + + * Ingestion: `advisory:ingest`, `vex:ingest`. + * Read: `advisory:read`, `vex:read`, `findings:read`. + * Overlay writes: `effective:write` – **Policy Engine only**.([Gitea: Git with a cup of tea][2]) + +**Separate human vs machine identities** + +* Humans: + + * Use `auth login --device-code`, with scopes limited to what UI/CLI needs (`scanner.read`, `policy.read`, etc.).([Gitea: Git with a cup of tea][7]) +* CI/automation: + + * Use client‑credentials, service principals, and tightly scoped audiences (e.g. `scanner`, `attestor`, `export-center`).([Gitea: Git with a cup of tea][7]) + +**Authority plugin hygiene** + +* Implement or configure IdP plugins via `IAuthorityPluginRegistrar`; validate config rigorously in `PostConfigure`.([Gitea: Git with a cup of tea][5]) +* Never store secrets in git; rely on `.local.yaml`, env vars, or mounted secret files and document which keys are mandatory.([Gitea: Git with a cup of tea][5]) + +### 3.2 Data, schemas, and AOC + +**Raw vs derived is sacred** + +* Raw stores: + + * `advisory_raw` and `vex_raw` are append‑only and AOC‑guarded; only Concelier/Excititor can write there through guarded paths.([Gitea: Git with a cup of tea][2]) +* Derived: + + * `effective_finding_*` collections are produced by Policy Engine only, using `effective:write` scope. No other service should mutate them.([Gitea: Git with a cup of tea][2]) + +**Enforce via tooling** + +* Enable Mongo schema validators for raw collections and overlays as per platform docs.([Gitea: Git with a cup of tea][2]) +* Use `stella aoc verify` in CI whenever ingestion schema/guards change, ensuring no violations creep in.([Gitea: Git with a cup of tea][2]) + +**No side‑routes to Mongo** + +* Application teams must **never** talk to Mongo directly; use module APIs (Scanner, Concelier, Excititor, Policy, Graph) as the abstraction. This keeps AOC/rules enforceable and replayable.([Gitea: Git with a cup of tea][2]) + +### 3.3 CI/CD and developer workflows + +**Configure the CLI predictably** + +* Honor the CLI precedence rules: flags → env vars → config file → defaults.([Gitea: Git with a cup of tea][8]) +* Standardise envs in pipelines: + + * `STELLAOPS_AUTHORITY`, `STELLAOPS_SCANNER_URL`, `STELLAOPS_CONCELIER_URL`, `STELLAOPS_EXCITITOR_URL`, etc.([Gitea: Git with a cup of tea][8]) + +**SBOMs at build time** + +* Prefer the **Buildx plugin** (Sbomer) for SBOM generation: + + * `buildx install` → `buildx build` wrapper injects `--attest=type=sbom,generator=stellaops/sbom-indexer`.([Gitea: Git with a cup of tea][8]) +* If Buildx isn’t available, fall back to post‑build `stella scan image` with a visible warning in CI.([Gitea: Git with a cup of tea][8]) + +**Use exit codes & JSON for gating** + +* In CI, run in `--json` mode and gate on CLI exit codes: + + * `0` = success, `2` = policy fail, `3` = verification fail, `4` = auth error, etc.([Gitea: Git with a cup of tea][8]) +* Use Policy preview APIs (`/policy/preview`) to test the effect of new policies before promoting them to production.([Gitea: Git with a cup of tea][10]) + +### 3.4 Observability & SRE + +**Standard metrics & logs** + +* Emit and dashboard at least: + + * `ingestion_write_total`, `aoc_violation_total{code}`, `ingestion_latency_seconds`.([Gitea: Git with a cup of tea][2]) + * Scan & policy latencies, queue depths, worker failures, OpTok issuance/error counts.([Gitea: Git with a cup of tea][4]) +* Use structured logs with `traceId`, `tenant`, `source.vendor`, `content_hash` as correlation dimensions.([Gitea: Git with a cup of tea][2]) + +**Adopt the platform SLO hints** + +* Take the latency and throughput targets from the high‑level architecture as initial SLOs: + + * P95 build‑time ≤ 3–5 s on warmed images, policy+VEX ≤ 500 ms for 5k components, etc.([Gitea: Git with a cup of tea][4]) + +### 3.5 Offline & air‑gapped operation + +**Treat offline sites as first‑class** + +* Use **Offline Kits**: + + * Bundle raw Mongo snapshots for `advisory_raw`/`vex_raw`, guard configs, verifier binaries, and replay bundles.([Gitea: Git with a cup of tea][2]) +* For air‑gapped clusters: + + * Deploy the same service graph (Authority, Scanner, Concelier, Excititor, Policy, etc.), seeded via Offline Kits and Export Center exports.([Gitea: Git with a cup of tea][2]) +* Practice DR: + + * Test restore-from‑snapshot → replay change streams → re‑validate AOC compliance per the platform overview.([Gitea: Git with a cup of tea][2]) + +### 3.6 Security & crypto + +**Centralise signing logic** + +* Let **Signer** own all DSSE signing and **Attestor** own Rekor v2 anchoring: + + * Clients/CI must never sign directly; they request signed bundles from Signer using PoE‑validated entitlements.([Gitea: Git with a cup of tea][1]) +* Use KMS/HSM backed key material where possible; use the CLI KMS verbs (`kms export/import`) for local key workflows in line with the CLI architecture.([Gitea: Git with a cup of tea][7]) + +**Defensive defaults** + +* Enforce rate limiting, sender constraints, and safe crypto providers as described in Authority & crypto docs.([Gitea: Git with a cup of tea][6]) +* Never bypass `IAuthorityTokenStore` when revoking or mutating identities; supply machine‑friendly `revokedReason` codes so revocation bundles are deterministic.([Gitea: Git with a cup of tea][5]) + +### 3.7 Governance & change management + +* Follow the **DevOps governance rules**: + + * Gateway stays thin, ingestion is AOC‑only, Graph replaces older platforms, and rules are versioned in `docs/devops/contracts-and-rules.md`.([Gitea: Git with a cup of tea][3]) +* Treat architecture docs (`07_HIGH_LEVEL_ARCHITECTURE.md`, `platform/architecture-overview.md`, module dossiers) as **source of truth**; when behavior changes, update docs + sprint files, not just code.([Gitea: Git with a cup of tea][4]) + +--- + +## 4. Short checklists + +You can paste these into your internal docs or tickets. + +### 4.1 “Ready for first prod scan?” checklist + +* [ ] Authority running with hardened defaults and backed by persistent Mongo.([Gitea: Git with a cup of tea][1]) +* [ ] Gateway verifies JWT + DPoP and enforces basic scopes.([Gitea: Git with a cup of tea][3]) +* [ ] Mongo, MinIO/RustFS, and queue backend deployed and monitored.([Gitea: Git with a cup of tea][4]) +* [ ] Scanner Web + Worker healthy; basic `/health` and `/ready` endpoints pass.([Gitea: Git with a cup of tea][1]) +* [ ] Signer, Attestor, Fulcio, Rekor v2 reachable; test DSSE signing + inclusion proof.([Gitea: Git with a cup of tea][4]) +* [ ] CLI installed; `stella auth login` and `stella scan image` succeed against a test image in `dev`.([Gitea: Git with a cup of tea][9]) + +### 4.2 “AOC compliant ingestion?” checklist + +* [ ] `StellaOps.Aoc` guard wired into all Concelier/Excititor write paths.([Gitea: Git with a cup of tea][2]) +* [ ] Mongo validators enabled for `advisory_raw` and `vex_raw`.([Gitea: Git with a cup of tea][2]) +* [ ] Policy Engine is the only holder of `effective:write` scope.([Gitea: Git with a cup of tea][2]) +* [ ] `stella aoc verify` job added to CI for ingestion schema changes.([Gitea: Git with a cup of tea][2]) + +--- + +If you tell me your role (platform owner vs module implementer vs CI owner) and environment shape (Kubernetes/Compose, air‑gapped or not), I can turn these into a much more concrete “day‑1/day‑2” runbook or a set of internal ADRs. + +[1]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/raw/commit/56c687253fbacabc25d8e1944f4fd43cb8dcef9c/AGENTS.md "git.stella-ops.org" +[2]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/61f963fd52cd4d6bb2f86afc5a82eac04c04b00e/docs/modules/platform/architecture-overview.md?utm_source=chatgpt.com "StellaOps Architecture Overview (Sprint 19)" +[3]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/raw/commit/13e4b53dda1575ba46c6188c794fd465ec6fdeec/docs/devops/contracts-and-rules.md?utm_source=chatgpt.com "Raw - Stella Ops" +[4]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/efc4f5f761d1b77c370c7d34d3dd58f786fad617/docs/07_HIGH_LEVEL_ARCHITECTURE.md?utm_source=chatgpt.com "git.stella-ops.org/07_HIGH_LEVEL_ARCHITECTURE.md at ..." +[5]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/8da4e12a90fd82f6b2ec870bb73e0499b4ff6343/docs/dev/31_AUTHORITY_PLUGIN_DEVELOPER_GUIDE.md?utm_source=chatgpt.com "Authority Plug-in Developer Guide - Stella Ops" +[6]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/commits/commit/2e89a92d92387b49a0ce17333fcbaf17f00389e3/docs/07_HIGH_LEVEL_ARCHITECTURE.md?utm_source=chatgpt.com "8 Commits - Stella Ops" +[7]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/240e8ff25ddf0f3385b817c30c26a24bff2e5730/docs/modules/cli/architecture.md?utm_source=chatgpt.com "git.stella-ops.org/architecture.md at ..." +[8]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/commit/e2672ba968a7bd2c935cbc4cb572b383b15c8b07?files=docs%2FARCHITECTURE_CLI.md&utm_source=chatgpt.com "Rewrite architecture docs and add Vexer connector template ..." +[9]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org/src/commit/48702191bed7d66b8e29929a8fad4ecdb40b9490/docs/quickstart.md?utm_source=chatgpt.com "Quickstart – First Scan in Five Minutes - Stella Ops" +[10]: https://git.stella-ops.org/stella-ops.org/git.stella-ops.org?utm_source=chatgpt.com "stella-ops.org/git.stella-ops.org - git.stella-ops.org - Stella Ops" diff --git a/docs/product-advisories/30-Nov-2025 - Standup Sprint Kickstarters.md b/docs/product-advisories/30-Nov-2025 - Standup Sprint Kickstarters.md new file mode 100644 index 000000000..49a90a633 --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - Standup Sprint Kickstarters.md @@ -0,0 +1,861 @@ +Here’s a crisp, no-drama standup plan with three small wins that unblock bigger work. Background first, then exact tasks you can ship in a day. + +--- + +# 1) Scanner post-mortem → 2 reproducible regressions + +**Why:** Post-mortems sprawl. Two bullet-proof repros turn theories into fixable tickets. + +**Task today** + +* Pick the two highest-impact failure modes (e.g., wrong reachability verdict; missed OSV/CVE due to parser). +* For each, produce a 1-command repro script (Docker image + SBOM fixture) and an **expected vs actual** JSON artifact. +* Store under `tests/scanner/regressions//` with `README.md` and `make test` target. + +**Definition of done** + +* CI job `Scanner-Regression` runs both and fails deterministically if behavior regresses. + +--- + +# 2) Mongo→Postgres slice prototype (pick one) + +**Why:** A focused end-to-end slice beats a giant migration plan. + +**Candidates** + +* `Authority.Tokens` (licensing/entitlements) +* `Scheduler.Jobs` (enqueued/executed metadata) +* `VEX.Verdicts` (append-only history) + +**Task today** + +* Draft minimal DDL in `db/pg/migrations/0001_.sql` and EF Core model. +* Add writer + reader paths behind `IStore`; toggle backend using `STORE_BACKEND=Mongo|Postgres`. +* Compose a data-parity test: write N records → read from both stores → byte-compare normalized JSON. + +**Definition of done** + +* Service boots with `STORE_BACKEND=Postgres`, parity test passes locally, migration cleanly creates/drops the slice. + +--- + +# 3) DSSE + Rekor attestation batch sweep + +**Plain English:** sign DSSE envelopes, submit to Rekor, and size batches until failures appear. + +**Task today** + +* Choose an artifact type (e.g., `scanner-report.json`). +* Implement a tiny CLI/script that signs K reports into DSSE, submits to Rekor/mirror, and logs latency + bytes + success. +* Sweep K in {1,5,10,25,50}, output CSV under `experiments/dsse_rekor/`. + +**Definition of done** + +* Deliver plot/table with recommended batch size and failure threshold (e.g., “use 10; >25 implies 8% retries”). + +--- + +## Suggested assignments (1-day scope) + +* **Alex** → #1 repros +* **Mira** → #2 PG slice (Authority.Tokens) +* **Petar** → #3 DSSE/Rekor sweep + +## Ready-to-copy tickets + +* **SCN-201:** “Create 2 deterministic regression tests for Scanner failures” (include folder layout + CI target). +* **DB-301:** “Prototype Postgres backend for Authority.Tokens with parity test and feature flag.” +* **SEC-411:** “Run DSSE+Rekor batch sweep for scanner reports; publish CSV and default threshold.” + +If you want, I can generate the folders, Makefiles, EF Core stubs, and DSSE/Rekor sweep script next. +Good, this helps narrow the scope. I’ll go point by point and then expand #3. + +--- + +## 1) How these fit into Stella Ops, concretely + +Think in terms of your core promises / moats: + +* Deterministic, replayable scans +* Verifiable SBOM → VEX chain with proofs +* Future-proof data layout (Mongo + “recognized” SQL) +* Proof-of-integrity / Rekor-style transparency + +The three tasks line up like this: + +### Task 1: Scanner post-mortem → 2 reproducible test cases + +**Where it lives:** `scanner.webservice` + your test suite. + +**Why it matters for Stella Ops:** + +* Your brand is: “we don’t silently miss things; if we ever do, it becomes a test forever”. +* Each “failure mode” becomes a **fixture** that: + + * feeds into reachability heuristics, + * is later used by Vexer/Excititor to show “we’ve seen this pattern in the wild and we handle it”. + +It’s not random QA; it’s the foundation for your **“golden reachability dataset”** idea from the other branch. Every time you study a competitor miss or your own miss, it turns into: + +* “Golden fixture #NN – known tricky case, guarded forever by CI”. + +So this is directly supporting: + +* Deterministic scanner behavior +* Trust Algebra Studio later being able to say: “policy X passes all N golden fixtures”. + +Very practical outcome: it gives your devs a **concrete target** instead of a vague “scanner is sometimes wrong”. + +--- + +### Task 2: Postgres without migration: why and how + +You’re right: there is no migration today, only **shape-finding**. + +You said earlier: “conversion, not migration” and “we use PostgreSQL mainly because of recognition”. That can be turned into something useful now, without over-engineering: + +**Goal in this phase** + +* Define **one** slice of data model in Postgres that: + + * is understandable to auditors / integrators, + * is stable enough that, if you later decide to “convert” Mongo → PG, you already know how it should look, + * forces you to create a clean abstraction (`IWhateverStore`) rather than hard-wiring Mongo into every service. + +So instead of “migration plan”, think: + +> “We are prototyping a **Postgres-friendly façade** for one core concept, behind an interface.” + +Example: `Authority.Tokens` or `Scheduler.Jobs`. + +* You keep Mongo as the actual source of truth for now. +* You add a minimal Postgres model in EF Core. +* You add a parity test (write/read in both backends, compare). +* You wire a feature flag like `STORE_BACKEND=Mongo|Postgres` so you can switch environments on/off. + +This gives you: + +* Early signal about “does this data model work in SQL?” +* A future-proof seam where “conversion” can happen when the product stabilizes. +* Something that looks familiar to enterprise customers (“yes, we have Postgres, here is the schema”). + +No migration script, no DMS, just **learning and shaping**. + +If you prefer, you can drop the CI parity test and only keep: + +* Interface +* Two implementations +* A simple console test or integration test + +to keep ceremony minimal while still forcing a clean boundary. + +--- + +## 3) DSSE + Rekor attestation experiment: deeper elaboration + +I’ll treat this as: “Explain what exactly my team should build and why it matters to Stella Ops.” + +### 3.1. Why you care at all + +This task supports at least three of your moats: + +* **Deterministic replayable scans** + The DSSE envelope + Rekor entry is a cryptographic “receipt” for a given scan + SBOM + VEX result. +* **Proof-of-Integrity Graph / Proof-Market Ledger** + If you later build your own Rekor mirror or “Proof-Market Ledger”, you need to know real batch sizes and behavior now. +* **Crypto-sovereign readiness** + Eventually you want GOST / SM / PQC signatures; this small experiment tells you how your stack behaves with any signature scheme you plug in later. + +So we’re doing **one focused measurement**: + +> For *one* type of attestation, find the smallest batch size that: +> +> * keeps latency acceptable, +> * doesn’t cause excessive timeouts or retries, +> * doesn’t make envelopes so large they become awkward. + +This becomes your **default configuration** for Scanner → Attestation → Rekor in all future designs. + +--- + +### 3.2. What exactly to build + +Propose a tiny .NET 10 console tool, e.g.: + +`src/Experiments/StellaOps.Attest.Bench/StellaOps.Attest.Bench.csproj` + +Binary: `stella-attest-bench` + +**Inputs** + +* A directory with scanner reports, e.g.: `artifacts/scanner-reports/*.json` +* Rekor endpoint and credentials (or test/mirror instance) +* Batch sizes to sweep: e.g. `1,5,10,25,50` + +**CLI sketch** + +```bash +stella-attest-bench \ + --reports-dir ./artifacts/scanner-reports \ + --rekor-url https://rekor.stella.local \ + --batch-sizes 1,5,10,25,50 \ + --out ./experiments/dsse_rekor/results.csv +``` + +**What each run does** + +For each batch size `K`: + +1. Take `K` reports from the directory. +2. For each report: + + * Wrap into a DSSE envelope: + + ```json + { + "payloadType": "application/vnd.stellaops.scanner-report+json", + "payload": "", + "signatures": [ + { + "keyid": "authority-key-1", + "sig": "" + } + ] + } + ``` + * Measure size of the envelope in bytes. +3. Submit the `K` envelopes to Rekor: + + * Either one by one, or if your client API supports it, in a single batch call. + * Record: + + * start timestamp + * end timestamp + * status (success / failure / retry count) +4. Append a row to `results.csv`: + + ```csv + timestamp,batch_size,envelopes_count,total_bytes,avg_bytes,latency_ms,successes,failures,retries + 2025-11-30T14:02:00Z,10,10,123456,12345.6,820,10,0,0 + ``` + +You can enrich it later with HTTP codes, Rekor log index, etc., but this is enough to choose a default. + +--- + +### 3.3. Minimal internal structure + +Rough C# layout (no full code, just architecture so devs don’t wander): + +```csharp +// Program.cs +// - Parse args +// - Build IServiceProvider +// - Resolve and run BenchRunner + +public sealed class BenchConfig +{ + public string ReportsDirectory { get; init; } = default!; + public Uri RekorUrl { get; init; } = default!; + public int[] BatchSizes { get; init; } = Array.Empty(); + public string OutputCsvPath { get; init; } = default!; +} + +public sealed class BenchRunner +{ + private readonly IDsseSigner _signer; + private readonly IRekorClient _rekor; + private readonly IResultsSink _sink; + + public async Task RunAsync(BenchConfig config, CancellationToken ct); +} + +// IDsseSigner: wraps "payload" (report json bytes) -> DSSE envelope bytes +// IRekorClient: envelope bytes -> result (success/fail, latency, retries) +// IResultsSink: append rows to CSV +``` + +Later, `IDsseSigner` and `IRekorClient` can be reused inside a proper `StellaOps.Attest` library that the Scanner or Scheduler use. + +--- + +### 3.4. What “done” looks like in a way that matters to Stella Ops + +Not just “we have a toy”. You want a **decision** out of this: + +* `DEFAULT_DSSE_BATCH_SIZE = 10` +* “For this Rekor setup, we observe: + + * ≤10: 100 % success, latency ~X ms + * 25: occasional retries + * 50: frequent 5xx / timeouts; not recommended” + +You then: + +* Write this into a short markdown note, e.g. + `docs/attestation/dsse-rekor-batch-sizing.md` +* Use it when designing: + + * Scanner → “Attestation job” messages (avoid pushing >10 envelopes at once), + * The future “Proof-Market Ledger” so it doesn’t assume unbounded batches, + * Air-gap mirror behavior (“we queue up batches of 10 before syncing”). + +So the outcome is a **concrete configuration default and a constraint** that all future modules respect. + +--- + +### If you want to simplify further + +If this still feels too heavy for current prototyping speed, the absolute minimum to keep the value: + +* Hardcode 2 batch sizes: `1` and `10`. +* Only measure: + + * latency + * success/fail +* Print results to stdout instead of CSV. + +The key is: **one small executable experiment** that turns “DSSE + Rekor is cool” into “our default batch size is X for environment Y”. + +--- + +If you tell me which module names you want to standardize on for this (e.g. `StellaOps.Attest`, `StellaOps.Experiments`, etc.), I can sketch exact folder names, namespaces, and even a first `.csproj` + `Program.cs` your dev can paste in. +I’ll rewrite all three as tight internal guidelines you can drop into `docs/dev/`. Think of each as something you can hand to a new engineer and say: “Follow this exactly.” + +--- + +## G1 · Scanner regression fixtures + +**File:** `docs/dev/scanner-regressions.md` +**Applies to:** `scanner.webservice` and any libraries it uses. + +### 1. Purpose + +Any serious scanner failure must become a deterministic regression fixture. Over time this becomes Stella Ops’ “golden dataset” for reachability and correctness. + +Outcomes: + +* Bugs don’t repeat silently. +* Heuristics and future policies are validated against the same corpus. +* Post‑mortems always end with a guardrail in CI. + +### 2. When to create a regression fixture + +Create a fixture when all three hold: + +1. The bug affects at least one of: + + * Severity ≥ Medium + * High‑volume ecosystems (OS packages, Java, Python, Node, container base images) + * Core behaviors (reachability, deduplication, suppression, parser correctness) +2. The behavior is reproducible from static inputs (image, SBOM, or manifests). +3. The expected correct behavior is agreed by at least one more engineer on the scanner team. + +If in doubt: add the fixture. It is cheap, and it strengthens the golden corpus. + +### 3. Directory structure & naming + +Test project: + +```text +tests/ + StellaOps.Scanner.RegressionTests/ + Regression/ + SCN-0001-missed-cve-in-layer/ + SCN-0002-wrong-reachability/ + ... +``` + +Fixture layout (example): + +```text +SCN-0001-missed-cve-in-layer/ + input/ + image.sbom.json # or image.tar, etc. + config.json # scanner flags, if needed + expected/ + findings.json # canonical expected findings + case.metadata.json # machine-readable description + case.md # short human narrative +``` + +`case.metadata.json` schema: + +```json +{ + "id": "SCN-0001", + "title": "Missed CVE-2025-12345 in lower layer", + "kind": "vulnerability-missed", + "source": "internal-postmortem", + "severity": "high", + "tags": [ + "reachability", + "language:java", + "package:log4j" + ] +} +``` + +`case.md` should answer: + +* What failed? +* Why this case is representative / important? +* What is the correct expected behavior? + +### 4. Test harness rules + +Global rules for all regression tests: + +* No network access (fixtures must be fully self‑contained). +* No time‑dependent logic (use fixed timestamps if necessary). +* No nondeterministic behavior (seed any randomness). + +Comparison rules: + +* Normalize scanner output before comparison: + + * Sort arrays (e.g. findings). + * Remove volatile fields (generated IDs, timestamps, internal debug metadata). +* Compare canonical JSON (e.g. string equality on normalized JSON). + +Implementation sketch (xUnit): + +```csharp +public class GoldenRegressionTests +{ + [Theory] + [MemberData(nameof(RegressionSuite.LoadCases), MemberType = typeof(RegressionSuite))] + public async Task Scanner_matches_expected_findings(RegressionCase @case) + { + var actual = await ScannerTestHost.RunAsync(@case.InputDirectory); + + var expectedJson = File.ReadAllText(@case.ExpectedFindingsPath); + + var normalizedActual = FindingsNormalizer.Normalize(actual); + var normalizedExpected = FindingsNormalizer.Normalize(expectedJson); + + Assert.Equal(normalizedExpected, normalizedActual); + } +} +``` + +### 5. How to add a new regression case + +Checklist for developers: + +1. **Reproduce the bug** using local dev tools. +2. **Minimize the input**: + + * Prefer a trimmed SBOM or minimal image over full customer artifacts. + * Remove sensitive data; use synthetic equivalents if needed. +3. **Create folder** under `Regression/SCN-XXXX-short-slug/`. +4. Populate: + + * `input/` with all needed inputs (SBOMs, manifests, config). + * `expected/findings.json` with the correct canonical output (not the buggy one). + * `case.metadata.json` and `case.md`. +5. **Run tests** locally: + `dotnet test tests/StellaOps.Scanner.RegressionTests` +6. Fix the scanner behavior (if not already fixed). +7. Ensure tests fail without the fix and pass with it. +8. Open PR with: + + * Fixture directory. + * Scanner fix. + * Any harness adjustments. + +### 6. CI integration & “done” definition + +* CI job `Scanner-Regression` runs in PR validation and main. +* A regression case is “live” when: + + * It’s present under `Regression/`. + * It is picked up by the harness. + * CI fails if the behavior regresses. + +--- + +## G2 · Postgres slice prototype (shape‑finding, no migration) + +**File:** `docs/dev/authority-store-backends.md` +**Applies to:** Authority (or similar) services that currently use Mongo. + +### 1. Purpose + +This is not data migration. It is about: + +* Designing a clean storage interface. +* Prototyping a Postgres‑friendly schema for one bounded slice (e.g. `Authority.Tokens`). +* Being able to run the service with either Mongo or Postgres behind the same interface. + +This supports future “conversion” and enterprise expectations (“we speak Postgres”) without blocking current prototyping speed. + +### 2. Scope & constraints + +* Scope: one slice only (e.g. tokens, jobs, or VEX verdicts). +* Mongo remains the operational source of truth for now. +* Postgres path is opt‑in via configuration. +* No backward migration or synchronization logic. + +### 3. Repository layout + +Example for `Authority.Tokens`: + +```text +src/ + StellaOps.Authority/ + Domain/ + Token.cs + TokenId.cs + ... + Stores/ + ITokenStore.cs + MongoTokenStore.cs + PostgresTokenStore.cs + Persistence/ + AuthorityPgDbContext.cs + Migrations/ + 0001_AuthTokens_Init.sql +docs/ + dev/ + authority-store-backends.md +``` + +### 4. Store interface design + +Guidelines: + +* Keep the interface narrow and domain‑centric. +* Do not leak Mongo or SQL constructs. + +Example: + +```csharp +public interface ITokenStore +{ + Task GetByIdAsync(TokenId id, CancellationToken ct = default); + Task> GetByOwnerAsync(PrincipalId ownerId, CancellationToken ct = default); + Task SaveAsync(Token token, CancellationToken ct = default); + Task RevokeAsync(TokenId id, RevocationReason reason, CancellationToken ct = default); +} +``` + +Both `MongoTokenStore` and `PostgresTokenStore` must implement this contract. + +### 5. Postgres schema guidelines + +Principles: + +* Use schemas per bounded context, e.g. `authority`. +* Choose stable primary keys (`uuid` or `bigint`), not composite keys unless necessary. +* Index only for known access patterns. + +Example DDL: + +```sql +CREATE SCHEMA IF NOT EXISTS authority; + +CREATE TABLE IF NOT EXISTS authority.tokens ( + id uuid PRIMARY KEY, + owner_id uuid NOT NULL, + kind text NOT NULL, + issued_at_utc timestamptz NOT NULL, + expires_at_utc timestamptz NULL, + is_revoked boolean NOT NULL DEFAULT false, + revoked_at_utc timestamptz NULL, + revoked_reason text NULL, + payload_json jsonb NOT NULL, + created_at_utc timestamptz NOT NULL DEFAULT now(), + updated_at_utc timestamptz NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS ix_tokens_owner_id + ON authority.tokens (owner_id); + +CREATE INDEX IF NOT EXISTS ix_tokens_kind + ON authority.tokens (kind); +``` + +EF Core: + +* Map with `ToTable("tokens", "authority")`. +* Use owned types or value converters for `payload_json`. + +### 6. Configuration & feature flag + +Configuration: + +```jsonc +// appsettings.json +{ + "Authority": { + "StoreBackend": "Mongo", // or "Postgres" + "Postgres": { + "ConnectionString": "Host=...;Database=stella;..." + }, + "Mongo": { + "ConnectionString": "...", + "Database": "stella_authority" + } + } +} +``` + +Environment override: + +* `AUTHORITY_STORE_BACKEND=Postgres` + +DI wiring: + +```csharp +services.AddSingleton(sp => +{ + var cfg = sp.GetRequiredService>().Value; + return cfg.StoreBackend switch + { + "Postgres" => new PostgresTokenStore( + sp.GetRequiredService()), + "Mongo" => new MongoTokenStore( + sp.GetRequiredService()), + _ => throw new InvalidOperationException("Unknown store backend") + }; +}); +``` + +### 7. Minimal parity / sanity checks + +Given you are still prototyping, keep this light. + +Recommended: + +* A single test or console harness that: + + * Creates a small set of `Token` objects. + * Writes via Mongo and via Postgres. + * Reads back from each and compares a JSON representation of the domain object (ignoring DB‑specific metadata). + +This is not full data migration testing; it is a smoke check that both backends honor the same domain contract. + +### 8. “Done” definition for the first slice + +For the chosen slice (e.g. `Authority.Tokens`): + +* `ITokenStore` exists, with Mongo and Postgres implementations. +* A Postgres DbContext and first migration are present and runnable. +* The service starts cleanly with `StoreBackend=Postgres` against an empty DB. +* There is at least one automated or scripted sanity check that both backends behave equivalently for typical operations. +* This is documented in `docs/dev/authority-store-backends.md`: + + * How to switch backends. + * Current state: “Postgres is experimental; Mongo is default.” + +--- + +## G3 · DSSE + Rekor batch‑size experiment + +**File:** `docs/attestation/dsse-rekor-batch-sizing.md` +**Applies to:** Attestation / integrity pipeline for scanner reports. + +### 1. Purpose + +Determine a concrete default batch size for DSSE envelopes submitted to Rekor, balancing: + +* Reliability (few or no failures / retries). +* Latency (per batch). +* Envelope size (practical for transport and logging). + +Outcome: a hard configuration value, e.g. `DefaultDsseRekorBatchSize = 10`, backed by measurement. + +### 2. Scope + +* Artifact type: scanner report (e.g. `scanner-report.json`). +* Environment: your current Rekor endpoint (or mirror), not production critical path. +* One small experiment tool, later reusable by attestation services. + +### 3. Project structure + +```text +src/ + Experiments/ + StellaOps.Attest.Bench/ + Program.cs + BenchConfig.cs + BenchRunner.cs + DsseSigner.cs + RekorClient.cs + ResultsCsvSink.cs +experiments/ + dsse_rekor/ + results.csv +docs/ + attestation/ + dsse-rekor-batch-sizing.md +``` + +### 4. CLI contract + +Binary: `stella-attest-bench` + +Example usage: + +```bash +stella-attest-bench \ + --reports-dir ./artifacts/scanner-reports \ + --rekor-url https://rekor.lab.stella \ + --batch-sizes 1,5,10,25,50 \ + --out ./experiments/dsse_rekor/results.csv \ + --max-retries 3 \ + --timeout-ms 10000 +``` + +Required flags: + +* `--reports-dir` +* `--rekor-url` +* `--batch-sizes` +* `--out` + +Optional: + +* `--max-retries` (default 3) +* `--timeout-ms` (default 10000 ms) + +### 5. Implementation guidelines + +Core config: + +```csharp +public sealed class BenchConfig +{ + public string ReportsDirectory { get; init; } = default!; + public Uri RekorUrl { get; init; } = default!; + public int[] BatchSizes { get; init; } = Array.Empty(); + public string OutputCsvPath { get; init; } = default!; + public int MaxRetries { get; init; } = 3; + public int TimeoutMs { get; init; } = 10_000; +} +``` + +Interfaces: + +```csharp +public interface IDsseSigner +{ + byte[] WrapAndSign(byte[] payload, string payloadType); +} + +public interface IRekorClient +{ + Task SubmitAsync( + IReadOnlyList envelopes, + CancellationToken ct); +} + +public interface IResultsSink +{ + Task AppendAsync(BatchMeasurement measurement, CancellationToken ct); +} +``` + +Measurement model: + +```csharp +public sealed class BatchMeasurement +{ + public DateTime TimestampUtc { get; init; } + public int BatchSize { get; init; } + public int EnvelopeCount { get; init; } + public long TotalBytes { get; init; } + public double AverageBytes { get; init; } + public long LatencyMs { get; init; } + public int SuccessCount { get; init; } + public int FailureCount { get; init; } + public int RetryCount { get; init; } +} +``` + +Runner outline: + +1. Enumerate report files in `ReportsDirectory` (e.g. `*.json`). +2. For each `batchSize`: + + * Select up to `batchSize` reports. + * For each report: + + * Read bytes. + * Call `IDsseSigner.WrapAndSign` with payload type + `application/vnd.stellaops.scanner-report+json`. + * Track envelope sizes. + * Start stopwatch. + * Submit via `IRekorClient.SubmitAsync` with retry logic honoring `MaxRetries` and `TimeoutMs`. + * Record latency, successes, failures, retries. + * Write one `BatchMeasurement` row to `results.csv`. + +CSV headers: + +```csv +timestamp_utc,batch_size,envelopes_count,total_bytes,avg_bytes,latency_ms,successes,failures,retries +``` + +Signer: + +* For the experiment, use a local dev key (e.g. Ed25519). +* Make signing deterministic (no random salts that affect envelope size unexpectedly). + +Rekor client: + +* For this experiment, you only need “accepted or not” plus HTTP status codes; inclusion proof verification is out of scope. + +### 6. How to run and interpret + +Execution steps: + +1. Prepare a folder with representative scanner reports: + + * At least 20–30 reports from different images, to approximate typical size variance. +2. Ensure the Rekor environment is reachable and not rate‑limited. +3. Run the tool with a sweep like `1,5,10,25,50`. +4. Inspect `results.csv`: + + * For each batch size, look at: + + * Average latency. + * Any non‑zero failures or elevated retry counts. + * Total and average bytes per envelope. + +Decision rules: + +* Pick the largest batch size that: + + * Shows 0 failures and acceptable retry counts across multiple runs. + * Keeps latency in a reasonable budget for your pipeline (e.g. under a few seconds per batch). +* Define: + + * `DefaultBatchSize` = chosen safe value. + * `HardMaxBatchSize` = first size where failures or unacceptable latency appear. + +Document in `docs/attestation/dsse-rekor-batch-sizing.md`: + +* Environment details (Rekor version, hardware, network). +* Brief description of report corpus. +* A summarized table from `results.csv`. +* The chosen `DefaultBatchSize` and `HardMaxBatchSize` with a clear statement: + + * “All production attestation jobs must respect this default and must not exceed the hard max without a new measurement.” + +### 7. “Done” definition + +The experiment is “done” when: + +* `StellaOps.Attest.Bench` builds and runs with the specified CLI. +* It produces a `results.csv` with one row per `(run, batch_size)` combination. +* There is a written default: + + * `DefaultDsseRekorBatchSize = X` + * plus rationale in `dsse-rekor-batch-sizing.md`. +* Future attestation designs (e.g. scanner → ledger pipeline) are required to use that default unless they explicitly update the experiment and the document. + +--- + +If you want, next step can be: convert each of these into actual files (including a minimal `csproj` and `Program.cs` for the bench tool) so your team can copy‑paste and start implementing. diff --git a/docs/product-advisories/30-Nov-2025 - UI Micro-Interactions for StellaOps.md b/docs/product-advisories/30-Nov-2025 - UI Micro-Interactions for StellaOps.md new file mode 100644 index 000000000..6ee753839 --- /dev/null +++ b/docs/product-advisories/30-Nov-2025 - UI Micro-Interactions for StellaOps.md @@ -0,0 +1,497 @@ +Here are **three tightly scoped UI micro-interactions** you can drop into the next front-end sprint. Each maps to a single Angular task and reinforces StellaOps’ auditability, low-noise VEX gating, and evidence provenance for both air-gapped and online users. + +--- + +# 1) Audit Trail “Why am I seeing this?” (per-row expandable reason) + +**Goal:** Make every verdict/action explainable at a glance. + +* **User flow:** In Tables (Findings, Components, Policies), each row shows a tiny “ⓘ Why?” chip. Clicking expands an inline panel with the explanation capsule (policy that fired, rule hash, data inputs, and the deterministic graph revision ID). +* **Angular task:** + * Add `ReasonCapsuleComponent` (standalone) with inputs: `policyName`, `ruleId`, `graphRevisionId`, `inputsDigest`, `timestamp`, `actor`. + * Drop it into tables via `*ngTemplateOutlet` so no table refactor. + * Keyboard accessible (Enter/Space toggles). +* **Backend contract (read-only):** `GET /api/audit/reasons/:verdictId` → + ```json + { + "policy":"Default VEX Merge", + "ruleId":"vex.merge.R12", + "graphRevisionId":"grv_2025-11-18T12:03Z_b3e1", + "inputsDigest":"sha256:…", + "actor":"Vexer", + "ts":"2025-11-18T12:03:22Z" + } + ``` +* **Acceptance criteria:** + * Toggle persists open/closed per row in URL state (`?open=rid1,rid3`). + * Copy-to-clipboard for `graphRevisionId`. + * Works in offline bundle (served from local API). + +--- + +# 2) Low-noise VEX Gate (inline, gated action with 3 evidence tiers) + +**Goal:** Reduce false prompts and only block when evidence is meaningful. + +* **User flow:** On risky actions (promote image, approve import), the primary button morphs into a **VEX Gate**: a small dropdown right-aligned to the button label showing **Green/Amber/Red** status with the top blocking reason. Clicking opens a concise sheet (not a modal) with the minimal evidence set, and a single “Proceed anyway” path if policy allows. +* **Angular task:** + * Create `VexGateButtonDirective` that decorates existing `