# Reachability Lattice & Scoring Model > **Status:** Draft – mirrors the December 2025 advisory on confidence-based reachability. > **Owners:** Scanner Guild · Policy Guild · Signals Guild. > Stella Ops isn't just another scanner—it's a different product category: **deterministic, evidence-linked vulnerability decisions** that survive auditors, regulators, and supply-chain propagation. This document defines the confidence lattice, evidence types, mitigation scoring, and policy gates used to turn static/runtime signals into reproducible reachability decisions and VEX statuses. --- ## 1. Overview **Key differentiator:** Unlike simplistic yes/no reachability approaches, the Stella Ops lattice model explicitly handles an **"Unknown"** (under_investigation) state, ensuring incomplete data doesn't lead to false safety. Every VEX decision is evidence-linked with proof pointers to the underlying reachability evidence. Classic "reachable: true/false" answers are too brittle. Stella Ops models reachability as an **ordered lattice** with explicit states and scores. Each analyzer/runtime probe emits `Evidence` documents; mitigations add `Mitigation` entries. The lattice engine joins both inputs into a `ReachDecision`: ``` UNOBSERVED (0–9) < POSSIBLE (10–29) < STATIC_PATH (30–59) < DYNAMIC_SEEN (60–79) < DYNAMIC_USER_TAINTED (80–99) < EXPLOIT_CONSTRAINTS_REMOVED (100) ``` Each state corresponds to increasing confidence that a vulnerability can execute. Mitigations reduce scores; policy gates map scores to VEX statuses (`not_affected`, `under_investigation`, `affected`). --- ## 2. Core types ```csharp public enum ReachState { Unobserved, Possible, StaticPath, DynamicSeen, DynamicUserTainted, ExploitConstraintsRemoved } public enum EvidenceKind { StaticCallEdge, StaticEntryPointProximity, StaticPackageDeclaredOnly, RuntimeMethodHit, RuntimeStackSample, RuntimeHttpRouteHit, UserInputSource, DataTaintFlow, ConfigFlagOn, ConfigFlagOff, ContainerNetworkRestricted, ContainerNetworkOpen, WafRulePresent, PatchLevel, VendorVexNotAffected, VendorVexAffected, ManualOverride } public sealed record Evidence( string Id, EvidenceKind Kind, double Weight, string Source, DateTimeOffset Timestamp, string? ArtifactRef, string? Details); public enum MitigationKind { WafRule, FeatureFlagDisabled, AuthZEnforced, InputValidation, PatchedVersion, ContainerNetworkIsolation, RuntimeGuard, KillSwitch, Other } public sealed record Mitigation( string Id, MitigationKind Kind, double Strength, string Source, DateTimeOffset Timestamp, string? ConfigHash, string? Details); public sealed record ReachDecision( string VulnerabilityId, string ComponentPurl, ReachState State, int Score, string PolicyVersion, IReadOnlyList Evidence, IReadOnlyList Mitigations, IReadOnlyDictionary Metadata); ``` --- ## 3. Scoring policy (default) | Evidence class | Base score contribution | |--------------------------|-------------------------| | Static path (call graph) | ≥ 30 | | Runtime hit | ≥ 60 | | User-tainted flow | ≥ 80 | | "Constraints removed" | = 100 | | Lockfile-only evidence | 10 (if no other signals)| Mitigations subtract up to 40 points (configurable): ``` effectiveScore = baseScore - min(sum(m.Strength), 1.0) * MaxMitigationDelta ``` Clamp final scores to 0–100. --- ## 4. State & VEX gates Default thresholds (edit in `reachability.policy.yml`): | State | Score range | |----------------------------|-------------| | UNOBSERVED | 0–9 | | POSSIBLE | 10–29 | | STATIC_PATH | 30–59 | | DYNAMIC_SEEN | 60–79 | | DYNAMIC_USER_TAINTED | 80–99 | | EXPLOIT_CONSTRAINTS_REMOVED| 100 | VEX mapping: * **not_affected**: score ≤ 25 or mitigations dominate (score reduced below threshold). * **affected**: score ≥ 60 (dynamic evidence without sufficient mitigation). * **under_investigation**: everything between. **This explicit "Unknown" state is a key differentiator**—incomplete data never leads to false safety. Each decision records `reachability.policy.version`, analyzer versions, policy hash, and config snapshot so downstream verifiers can replay the exact logic. All decisions are sealed in Decision Capsules for audit-grade reproducibility. --- ## 5. Evidence sources | Signal group | Producers | EvidenceKind | |--------------|-----------|--------------| | Static call graph | Roslyn/IL walkers, ASP.NET routing models, JVM/JIT analyzers | `StaticCallEdge`, `StaticEntryPointProximity`, `StaticFrameworkRouteEdge` | | Runtime sampling | .NET EventPipe, JFR, Node inspector, Go/Rust probes | `RuntimeMethodHit`, `RuntimeStackSample`, `RuntimeHttpRouteHit` | | Data/taint | Taint analyzers, user-input detectors | `UserInputSource`, `DataTaintFlow` | | Environment | Config snapshot, container args, network policy | `ConfigFlagOn/Off`, `ContainerNetworkRestricted/Open` | | Mitigations | WAF connectors, patch diff, kill switches | `MitigationKind.*` via `Mitigation` records | | Trust | Vendor VEX statements, manual overrides | `VendorVexNotAffected/Affected`, `ManualOverride` | Each evidence object **must** log `Source`, timestamps, and references (function IDs, config hashes) so auditors can trace it in the event graph. This enables **evidence-linked VEX decisions** where every assertion includes pointers to the underlying proof. --- ## 6. Event graph schema Persist function-level edges and evidence in Mongo (or your event store) under: * `reach_functions` – documents keyed by `FunctionId`. * `reach_call_sites` – `CallSite` edges (`caller`, `callee`, `frameworkEdge`). * `reach_evidence` – array of `Evidence` per `(scanId, vulnId, component)`. * `reach_mitigations` – array of `Mitigation` entries with config hashes. * `reach_decisions` – final `ReachDecision` document; references above IDs. All collections are tenant-scoped and include analyzer/policy version metadata. --- ## 7. Policy gates → VEX decisions VEXer consumes `ReachDecision` and `reachability.policy.yml` to emit: ```json { "vulnerability": "CVE-2025-1234", "products": ["pkg:nuget/Example@1.2.3"], "status": "not_affected|under_investigation|affected", "status_notes": "Reachability score 22 (Possible) with WAF rule mitigation.", "justification": "component_not_present|vulnerable_code_not_present|... or custom reason", "action_statement": "Monitor config ABC", "impact_statement": "Runtime probes observed 0 hits; static call graph absent.", "timestamp": "...", "custom": { "reachability": { "state": "POSSIBLE", "score": 22, "policyVersion": "reach-1", "evidenceRefs": ["evidence:123", "mitigation:456"] } } } ``` Justifications cite specific evidence/mitigation IDs so replay bundles (`docs/replay/DETERMINISTIC_REPLAY.md`) can prove the decision. --- ## 8. Runtime probes (overview) * .NET: EventPipe session watching `Microsoft-Windows-DotNETRuntime/Loader,JIT` → `RuntimeMethodHit`. * JVM: JFR recording with `MethodProfilingSample` events. * Node/TS: Inspector or `--trace-event-categories node.async_hooks,node.perf` sample. * Go/Rust: `pprof`/probe instrumentation. All runtime probes write evidence via `IRuntimeEvidenceSink`, which deduplicates hits, enriches them with `FunctionId`, and stores them in `reach_evidence`. See `src/Scanner/StellaOps.Scanner.WebService/Reachability/Runtime/DotNetRuntimeProbe.cs` (once implemented) for reference. --- ## 9. Hybrid Reachability Stella Ops combines **static call-graph analysis** with **runtime process tracing** for true hybrid reachability: - **Static analysis** provides call-graph edges from IL/bytecode analysis, framework routing models, and entry-point proximity calculations. - **Runtime analysis** provides observed method hits, stack samples, and HTTP route hits from live or shadow traffic. - **Hybrid reconciliation** merges both signal types, with each edge type attestable via DSSE. See `docs/reachability/hybrid-attestation.md` for the attestation model. This hybrid approach ensures that both build-time and run-time context contribute to the same verdict, avoiding the blind spots of purely static or purely runtime analysis. --- ## 10. Roadmap | Task | Description | |------|-------------| | `REACH-LATTICE-401-023` | Initial lattice types + scoring engine + event graph schema. | | `REACH-RUNTIME-402-024` | Productionize runtime probes (EventPipe/JFR) with opt-in config and telemetry. | | `REACH-VEX-402-025` | Wire `ReachDecision` into VEX generator; ensure OpenVEX/CSAF cite reachability evidence. | | `REACH-POLICY-402-026` | Expose reachability gates in Policy DSL & CLI (edit/lint/test). | Keep this doc updated as the lattice evolves or new signals/mitigations are added.