# Triage and Unknowns Technical Reference **Source Advisories**: - 30-Nov-2025 - Unknowns Decay & Triage Heuristics - 14-Dec-2025 - Dissect triage and evidence workflows - 04-Dec-2025 - Ranking Unknowns in Reachability Graphs **Last Updated**: 2025-12-14 --- ## 1. EVIDENCE-FIRST PRINCIPLES 1. **Evidence before detail**: Opening alert shows best available evidence immediately 2. **Fast first signal**: UI renders credible "first signal" quickly 3. **Determinism reduces hesitation**: Sorting, graphs, diffs stable across refreshes 4. **Offline by design**: If evidence exists locally, render without network 5. **Audit-ready by default**: Every decision reproducible, attributable, exportable ## 2. MINIMAL EVIDENCE BUNDLE (PER FINDING) 1. **Reachability proof**: Function-level path or package-level import chain 2. **Call-stack snippet**: 5–10 frames around sink/source with file:line anchors 3. **Provenance**: Attestation/DSSE + build ancestry (image → layer → artifact → commit) 4. **VEX/CSAF status**: affected/not-affected/under-investigation + reason 5. **Diff**: SBOM or VEX delta since last scan (smart-diff) 6. **Graph revision + receipt**: `graphRevisionId` plus the signed verdict receipt linking to upstream evidence (DSSE/Rekor when available) ## 3. KPIS ### 3.1 TTFS (Time-to-First-Signal) **Definition**: p50/p95 from alert creation to first rendered evidence **Target**: p95 < 1.5s (with 100ms RTT, 1% loss) ### 3.2 Clicks-to-Closure **Definition**: Median interactions per decision type **Target**: median < 6 clicks ### 3.3 Evidence Completeness Score **Definition**: 0–4 (reachability, call-stack, provenance, VEX present) **Target**: ≥90% of decisions include all evidence + reason + replay token ### 3.4 Offline Friendliness **Definition**: % evidence resolvable with no network **Target**: ≥95% with local bundle ### 3.5 Audit Log Completeness **Requirement**: Every decision has evidence hash set, actor, policy context, replay token ## 4. KEYBOARD SHORTCUTS - `J`: Jump to first incomplete evidence pane - `Y`: Copy DSSE (attestation block or Rekor entry ref) - `R`: Toggle reachability view (path list ↔ compact graph ↔ textual proof) - `/`: Search within graph (node/func/package) - `S`: Deterministic sort (reachability→severity→age→component) - `A`, `N`, `U`: Quick VEX set (Affected / Not-affected / Under-investigation) - `?`: Keyboard help overlay ## 5. UX FLOW ### 5.1 Alert Row - TTFS timer - Reachability badge - Decision state - Diff-dot ### 5.2 Open Alert → Evidence Tab (Not Details) **Top strip**: 3 proof pills (Reachability ✓ / Call-stack ✓ / Provenance ✓) Click to expand inline ### 5.3 Decision Drawer (Pinned Right) - VEX/CSAF radio (A/N/U) - Reason presets → "Record decision" - Audit-ready summary (hashes, timestamps, policy) ### 5.4 Diff Tab SBOM/VEX delta, grouped by "meaningful risk shift" ### 5.5 Activity Tab Immutable audit log; export as signed bundle ## 6. GRAPH PERFORMANCE (LARGE CALL GRAPHS) ### 6.1 Minimal-Latency Snapshots - Pre-render static PNG/SVG thumbnails server-side ### 6.2 Progressive Neighborhood Expansion - Load 1-hop first, expand on demand - First TTFS < 500ms ### 6.3 Stable Node Ordering - Deterministic layout with consistent anchors ### 6.4 Chunked Graph Edges - Capped fan-out - Collapse identical library paths into reachability macro-edge **Targets**: - Preview < 300ms - Interactive hydration < 2.0s for large graphs ## 7. OFFLINE DESIGN ### 7.1 Local Evidence Cache Store (SBOM slices, path proofs, DSSE attestations, compiled call-stacks) in signed bundle beside SARIF/VEX ### 7.2 Deferred Enrichment Mark fields needing internet; queue background "enricher" when network returns ### 7.3 Predictable Fallbacks Show embedded DSSE + "verification pending" if provenance server missing ## 8. AUDIT & REPLAY ### 8.1 Deterministic Replay Token ``` replay_token = hash(feed_manifests + rules + lattice_policy + inputs) ``` ### 8.2 One-Click "Reproduce" CLI snippet pinned to exact versions and policies ### 8.3 Evidence Hash-Set Content-address each proof artifact; audit entry stores hashes + signer ## 9. TELEMETRY IMPLEMENTATION ```typescript ttfs.start → (alert creation) ttfs.signal → (first evidence card paint) close.clicks → (decision recorded) ``` Log evidence bitset (reach, stack, prov, vex) at decision time ## 10. API REQUIREMENTS ### 10.1 Endpoints ``` GET /alerts?filters… → list view GET /alerts/{id}/evidence → evidence payload (reachability, call stack, provenance, hashes) POST /alerts/{id}/decisions → record decision event (append-only) GET /alerts/{id}/audit → audit timeline GET /alerts/{id}/diff?baseline=… → SBOM/VEX diff GET /bundles/{id}, POST /bundles/verify → offline bundle download/verify ``` ### 10.2 Evidence Payload Schema ```json { "alert_id": "a123", "reachability": { "status": "available|loading|unavailable|error", "hash": "sha256:…", "proof": {...} }, "callstack": { "status": "...", "hash": "...", "frames": [...] }, "provenance": { "status": "...", "hash": "...", "dsse": {...} }, "vex": { "status": "...", "current": {...}, "history": [...] }, "hashes": ["sha256:…", ...] } ``` **Guidelines**: - Deterministic ordering for arrays and nodes - Explicit `status` per evidence section - Include `hash` per artifact ## 11. DECISION EVENT SCHEMA Store per decision: - `alert_id`, `artifact_id` (image digest/commit hash) - `actor_id`, `timestamp` - `decision_status` (Affected/Not affected/Under investigation) - `reason_code` (preset) + `reason_text` - `evidence_hashes[]` (content-addressed) - `policy_context` (ruleset version, policy id) - `replay_token` (hash of inputs) ## 12. OFFLINE BUNDLE FORMAT Single file (`.stella.bundle.tgz`) containing: - Alert metadata snapshot - Evidence artifacts (reachability proofs, call stacks, provenance attestations) - SBOM slice(s) for diffs - VEX decision history - Manifest with content hashes - **Must be signed and verifiable** ## 13. PERFORMANCE BUDGETS - **TTFS**: <200ms skeleton, <500ms first evidence pill, <1.5s p95 full evidence - **Graph**: Preview <300ms, interactive <2.0s - **Interaction response**: ≤100ms - **Animation frame budget**: 16ms avg / 50ms p95 - **Keyboard coverage**: ≥90% of triage actions - **Offline replay**: 100% of decisions re-render from bundle ## 14. ERROR HANDLING Never show empty states without explanation. Distinguish: - "not computed yet" - "not possible due to missing inputs" - "blocked by permissions" - "offline—enrichment pending" - "verification failed" ## 15. RBAC Gate: - Viewing provenance attestations - Recording decisions - Exporting audit bundles All decision events immutable; corrections are new events (append-only) ## 16. UNKNOWNS DECAY & TRIAGE HEURISTICS ### 16.1 Problem Stale "unknown" findings create noise; need deterministic decay and triage rules ### 16.2 Requirements - Confidence decay card - Triage queue UI - Export artifacts for planning ### 16.3 Determinism - Decay windows and thresholds must be deterministic - Exports reproducible without live dependencies ### 16.4 Decay Logic **Decay Windows**: Define time-based decay windows **Thresholds**: Set confidence thresholds for promotion/demotion **UI/Export Snapshot Expectations**: Deterministic decay logic description ## 17. UNKNOWNS RANKING ALGORITHM ### 17.1 Score Formula ``` Score = clamp01( wP·P + # Popularity impact wE·E + # Exploit consequence potential wU·U + # Uncertainty density wC·C + # Graph centrality wS·S # Evidence staleness ) ``` ### 17.2 Default Weights ``` wP = 0.25 (deployment impact) wE = 0.25 (potential consequence) wU = 0.25 (uncertainty density) wC = 0.15 (graph centrality) wS = 0.10 (evidence staleness) ``` ### 17.3 Heuristics ``` P = min(1, log10(1 + deployments)/log10(1 + 100)) U = sum of flags, capped at 1.0: +0.30 if no provenance anchor +0.25 if version_range +0.20 if conflicting_feeds +0.15 if missing_vector +0.10 if unreachable source advisory S = min(1, age_days / 14) ``` ### 17.4 Band Assignment ``` Score ≥ 0.70 → HOT (immediate rescan + VEX escalation) 0.40 ≤ Score < 0.70 → WARM (scheduled rescan 12-72h) Score < 0.40 → COLD (weekly batch) ``` ### 17.5 Alternative: Blast Radius + Containment Model > **Added**: 2025-12-17 from "Building a Deeper Moat Beyond Reachability" advisory An alternative ranking model that incorporates blast radius and runtime containment signals: **Unknown reasons tracked**: - missing VEX for a CVE/component - version provenance uncertain - ambiguous indirect call edge for reachability - packed/stripped binary blocking symbolization **Rank factors (weighted)**: - **Blast radius**: transitive dependents, runtime privilege, exposure surface (net-facing? in container PID 1?) - **Evidence scarcity**: how many critical facts are missing? - **Exploit pressure**: EPSS percentile (if available), KEV presence - **Containment signals**: sandboxing, seccomp, read-only FS, eBPF/LSM denies observed **Data Model**: ```csharp public sealed record UnknownItem( string Id, string ArtifactDigest, string ArtifactPurl, string[] Reasons, BlastRadius BlastRadius, double EvidenceScarcity, ExploitPressure ExploitPressure, ContainmentSignals Containment, double Score, // 0..1 string ProofRef // path inside proof bundle ); public sealed record BlastRadius(int Dependents, bool NetFacing, string Privilege); public sealed record ExploitPressure(double? Epss, bool Kev); public sealed record ContainmentSignals(string Seccomp, string Fs); ``` **Ranking Function**: ```csharp public static class UnknownRanker { public static double Rank(BlastRadius b, double scarcity, ExploitPressure ep, ContainmentSignals c) { var dependents01 = Math.Clamp(b.Dependents / 50.0, 0, 1); var net = b.NetFacing ? 0.5 : 0.0; var priv = string.Equals(b.Privilege, "root", StringComparison.OrdinalIgnoreCase) ? 0.5 : 0.0; var blast = Math.Clamp((dependents01 + net + priv) / 2.0, 0, 1); var epss01 = ep.Epss is null ? 0.35 : Math.Clamp(ep.Epss.Value, 0, 1); var kev = ep.Kev ? 0.30 : 0.0; var pressure = Math.Clamp(epss01 + kev, 0, 1); var containment = 0.0; if (string.Equals(c.Seccomp, "enforced", StringComparison.OrdinalIgnoreCase)) containment -= 0.10; if (string.Equals(c.Fs, "ro", StringComparison.OrdinalIgnoreCase)) containment -= 0.10; return Math.Clamp(0.60 * blast + 0.30 * scarcity + 0.30 * pressure + containment, 0, 1); } } ``` **JSON Schema**: ```json { "id": "unk_...", "artifactPurl": "pkg:...", "reasons": ["missing_vex", "ambiguous_indirect_call"], "blastRadius": { "dependents": 42, "privilege": "root", "netFacing": true }, "evidenceScarcity": 0.7, "exploitPressure": { "epss": 0.83, "kev": false }, "containment": { "seccomp": "enforced", "fs": "ro" }, "score": 0.66, "proofRef": "proofs/unk_.../tree.cbor" } ## 18. UNKNOWNS DATABASE SCHEMA ```sql CREATE TABLE unknowns ( unknown_id uuid PRIMARY KEY, pkg_id text, pkg_version text, digest_anchor bytea, unknown_flags jsonb, popularity_p float, potential_e float, uncertainty_u float, centrality_c float, staleness_s float, score float, band text CHECK(band IN ('HOT','WARM','COLD')), graph_slice_hash bytea, evidence_set_hash bytea, normalization_trace jsonb, callgraph_attempt_hash bytea, created_at timestamptz, updated_at timestamptz ); CREATE TABLE deploy_refs ( pkg_id text, image_id text, env text, first_seen timestamptz, last_seen timestamptz ); CREATE TABLE graph_metrics ( pkg_id text PRIMARY KEY, degree_c float, betweenness_c float, last_calc_at timestamptz ); ``` ## 19. TRIAGE QUEUE UI ### 19.1 Queue Views - **HOT**: Red badge, sort by score desc - **WARM**: Yellow badge, sort by score desc - **COLD**: Gray badge, sort by age asc ### 19.2 Bulk Actions - Mark as reviewed - Escalate to HOT - Suppress (with reason) - Export selected ### 19.3 Filters - By band - By package - By environment - By date range ## 20. DECISION WORKFLOW CHECKLIST For any triage decision: - [ ] Evidence reviewed (reachability, call-stack, provenance, VEX) - [ ] Decision status selected (A/N/U) - [ ] Reason provided (preset or custom) - [ ] Replay token generated - [ ] Evidence hashes captured - [ ] Audit event recorded - [ ] Decision immutable (append-only) --- **Document Version**: 1.0 **Target Platform**: .NET 10, PostgreSQL ≥16, Angular v17