Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Introduces new marketing bridge documents for Decision Capsules, Hybrid Reachability, and Evidence-Linked VEX. Updates product vision, README, key features, moat, reachability, and VEX consensus docs to reflect four differentiating capabilities: signed reachability (hybrid static/runtime), deterministic replay, explainable policy with evidence-linked VEX, and sovereign/offline operation. All scan decisions are now described as sealed, reproducible, and audit-grade, with explicit handling of 'Unknown' states and hybrid reachability evidence.
216 lines
9.1 KiB
Markdown
216 lines
9.1 KiB
Markdown
# 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
|
||
|
||
<!-- TODO: Review for separate approval - updated lattice 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> Evidence,
|
||
IReadOnlyList<Mitigation> Mitigations,
|
||
IReadOnlyDictionary<string,string> 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
|
||
|
||
<!-- TODO: Review for separate approval - added hybrid reachability section -->
|
||
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.
|