- Add RateLimitConfig for configuration management with YAML binding support. - Introduce RateLimitDecision to encapsulate the result of rate limit checks. - Implement RateLimitMetrics for OpenTelemetry metrics tracking. - Create RateLimitMiddleware for enforcing rate limits on incoming requests. - Develop RateLimitService to orchestrate instance and environment rate limit checks. - Add RateLimitServiceCollectionExtensions for dependency injection registration.
1029 lines
30 KiB
Plaintext
1029 lines
30 KiB
Plaintext
Here’s a compact, practical blueprint to fuse **Smart‑Diff** with a **Call‑Stack Reachability** engine and emit **DSSE‑signed diff attestations** that carry a **weighted impact index**—so Stella Ops can prove not just “what changed,” but “how risky the change is in runtime‑reachable code.”
|
||
|
||
---
|
||
|
||
# What this does (in plain words)
|
||
|
||
* **Smart‑Diff**: computes semantic diffs between two artifact states (SBOMs, lockfiles, symbols, call maps).
|
||
* **Reachability**: measures whether a changed function/package is actually on a path that executes in your services (based on call graph + entrypoints).
|
||
* **Weighted Impact Index (WII)**: one number (0–100) that rises when the change lands on short, highly‑used, externally‑facing, or privileged call paths—and when known vulns become more “deterministic” (exploitably reachable).
|
||
* **DSSE Attestation**: a signed, portable JSON (in‑toto/DSSE) that binds the diff + WII to build/run evidence.
|
||
|
||
---
|
||
|
||
# Signals that feed the Weighted Impact Index
|
||
|
||
**Per‑delta features** (each contributes a weight; defaults in brackets):
|
||
|
||
* `Δreach_len` – change in **shortest reachable path length** to an entrypoint (−∞..+∞) [w=0.25]
|
||
* `Δlib_depth` – change in **library call depth** (indirection layers) [w=0.1]
|
||
* `exposure` – whether the touched symbol is **public/external‑facing** (API, RPC, HTTP route) [w=0.15]
|
||
* `privilege` – whether path crosses **privileged sinks** (deserialization, shell, fs/net, crypto) [w=0.15]
|
||
* `hot_path` – historical runtime evidence (pprof, APM, eBPF) showing **frequent execution** [w=0.15]
|
||
* `cvss_v4` – normalized CVSS v4.0 severity for affected CVEs (0–10 → 0–1) [w=0.1]
|
||
* `epss_v4` – exploit probability (0–1) [w=0.1]
|
||
* `guard_coverage` – presence of sanitizers/validations on the path (reduces score) [w=−0.1]
|
||
|
||
**Determinism nudge**
|
||
If `reachability == true` and `(cvss_v4 > 0.7 || epss_v4 > 0.5)`, add a +5 bonus to reflect “evidence‑linked determinism.”
|
||
|
||
**Final WII**
|
||
|
||
```
|
||
WII = clamp01( Σ (w_i * feature_i_normalized) ) * 100
|
||
```
|
||
|
||
---
|
||
|
||
# Minimal data you need in the engines
|
||
|
||
## 1) Smart‑Diff (inputs/outputs)
|
||
|
||
**Inputs:** SBOM(CycloneDX), symbol graph (per‑lang indexers), lockfiles, route maps.
|
||
**Outputs:** `DiffUnit[]` with:
|
||
|
||
```json
|
||
{
|
||
"unitId": "pkg:npm/lodash@4.17.21#function:merge",
|
||
"change": "modified|added|removed",
|
||
"before": {"hash":"...", "attrs": {...}},
|
||
"after": {"hash":"...", "attrs": {...}}
|
||
}
|
||
```
|
||
|
||
## 2) Reachability Engine
|
||
|
||
**Inputs:** call graph (nodes: symbols; edges: calls), entrypoints (HTTP routes, jobs, message handlers), runtime heat (optional).
|
||
**Queries:** `isReachable(symbol)`, `shortestPathLen(symbol)`, `libCallDepth(symbol)`, `hasPrivilegedSink(path)`, `hasGuards(path)`.
|
||
|
||
---
|
||
|
||
# Putting it together (pipeline)
|
||
|
||
1. **Collect**: For image/artifact A→B, build call graph, import SBOMs, CVE map, EPSS/CVSS data, routes, runtime heat.
|
||
2. **Diff**: Run Smart‑Diff → `DiffUnit[]`.
|
||
3. **Enrich per DiffUnit** using Reachability:
|
||
|
||
* `reachable = isReachable(unit.symbol)`
|
||
* `Δreach_len = shortestPathLen_B - shortestPathLen_A`
|
||
* `Δlib_depth = libCallDepth_B - libCallDepth_A`
|
||
* `exposure/privilege/hot_path/guard_coverage` booleans from path analysis
|
||
* `cvss_v4/epss_v4` from Feed (Concelier) + Excititor
|
||
4. **Score**: Compute `WII` per unit; also compute **artifact‑level WII** as:
|
||
|
||
* `max(WII_unit)` and `p95(WII_unit)` for “spike” vs “broad” impact.
|
||
5. **Attest**: Emit DSSE statement with diff + scores + evidence URIs (SBOM digest, call‑graph digest, logs).
|
||
6. **Publish/Store**: Rekor(v2) mirror (Proof‑Market Ledger), and PostgreSQL as system‑of‑record.
|
||
|
||
---
|
||
|
||
# DSSE statement (example)
|
||
|
||
```json
|
||
{
|
||
"_type": "https://in-toto.io/Statement/v1",
|
||
"subject": [{"name":"ghcr.io/acme/payments:1.9.3","digest":{"sha256":"..."} }],
|
||
"predicateType": "https://stella-ops.org/attestations/smart-diff-wii@v1",
|
||
"predicate": {
|
||
"artifactBefore": {"digest":{"sha256":"..."}},
|
||
"artifactAfter": {"digest":{"sha256":"..."}},
|
||
"evidence": {
|
||
"sbomBefore":{"mediaType":"application/vnd.cdx+json","digest":{"sha256":"..." }},
|
||
"sbomAfter": {"mediaType":"application/vnd.cdx+json","digest":{"sha256":"..." }},
|
||
"callGraph": {"mediaType":"application/vnd.stella.callgraph+json","digest":{"sha256":"..."}},
|
||
"runtimeHeat": {"mediaType":"application/json","optional":true,"digest":{"sha256":"..."}}
|
||
},
|
||
"units": [{
|
||
"unitId":"pkg:nuget/Newtonsoft.Json@13.0.3#type:JsonSerializer",
|
||
"change":"modified",
|
||
"features":{
|
||
"reachable":true,
|
||
"deltaReachLen":-2,
|
||
"deltaLibDepth":-1,
|
||
"exposure":true,
|
||
"privilege":true,
|
||
"hotPath":true,
|
||
"guardCoverage":false,
|
||
"cvssV4":0.84,
|
||
"epssV4":0.61
|
||
},
|
||
"wii": 78.4,
|
||
"paths":[
|
||
{"entry":"HTTP POST /api/import","shortestLen":3,"privSinks":["fs.write"] }
|
||
]
|
||
}],
|
||
"aggregate": {"maxWii": 78.4, "p95Wii": 42.1}
|
||
},
|
||
"dsse": {"alg":"ed25519","keyid":"stella-authority:kid:abc123","sig":"..."}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# .NET 10 integration (skeletal but end‑to‑end)
|
||
|
||
## Contracts
|
||
|
||
```csharp
|
||
public record DiffUnit(string UnitId, ChangeKind Change, Attr? Before, Attr? After);
|
||
|
||
public interface IReachabilityService {
|
||
bool IsReachable(SymbolId s);
|
||
int? ShortestPathLen(SymbolId s);
|
||
int LibCallDepth(SymbolId s);
|
||
bool PathHasPrivilegedSinks(SymbolId s);
|
||
bool PathHasGuards(SymbolId s);
|
||
bool IsHotPath(SymbolId s);
|
||
}
|
||
|
||
public sealed class WiiScorer {
|
||
public double Score(WiiFeatures f) {
|
||
double sum =
|
||
0.25 * NormalizeDelta(f.DeltaReachLen) +
|
||
0.10 * NormalizeDelta(f.DeltaLibDepth) +
|
||
0.15 * Bool(f.Exposure) +
|
||
0.15 * Bool(f.Privilege) +
|
||
0.15 * Bool(f.HotPath) +
|
||
0.10 * Clamp01(f.CvssV4) +
|
||
0.10 * Clamp01(f.EpssV4) -
|
||
0.10 * Bool(f.GuardCoverage);
|
||
if (f.Reachable && (f.CvssV4 > 0.7 || f.EpssV4 > 0.5)) sum += 0.05;
|
||
return Math.Round(Clamp01(sum) * 100, 1);
|
||
}
|
||
// helper normalizers (Δ capped to ±5 for stability)
|
||
static double NormalizeDelta(int? d) => Clamp01(((d ?? 0) + 5) / 10.0);
|
||
static double Bool(bool b) => b ? 1.0 : 0.0;
|
||
static double Clamp01(double x) => Math.Min(1, Math.Max(0, x));
|
||
}
|
||
```
|
||
|
||
## Orchestrator (Scanner.WebService or Scheduled.Worker)
|
||
|
||
```csharp
|
||
public async Task<SmartDiffAttestation> RunAsync(Artifact before, Artifact after) {
|
||
var diffs = await _smartDiff.ComputeAsync(before, after);
|
||
var scorer = new WiiScorer();
|
||
var units = new List<AttestedUnit>();
|
||
|
||
foreach (var d in diffs) {
|
||
var s = SymbolId.Parse(d.UnitId);
|
||
var feat = new WiiFeatures {
|
||
Reachable = _reach.IsReachable(s),
|
||
DeltaReachLen = SafeDelta(_reach.ShortestPathLen(s), _baseline.ShortestPathLen(s)),
|
||
DeltaLibDepth = _reach.LibCallDepth(s) - _baseline.LibCallDepth(s),
|
||
Exposure = _exposure.IsExternalFacing(s),
|
||
Privilege = _reach.PathHasPrivilegedSinks(s),
|
||
HotPath = _reach.IsHotPath(s),
|
||
GuardCoverage = _reach.PathHasGuards(s),
|
||
CvssV4 = _vuln.CvssV4For(s),
|
||
EpssV4 = _vuln.EpssV4For(s)
|
||
};
|
||
var wii = scorer.Score(feat);
|
||
units.Add(new AttestedUnit(d.UnitId, d.Change, feat, wii, _reach.PathPreview(s)));
|
||
}
|
||
|
||
var agg = new {
|
||
maxWii = units.Max(u => u.Wii),
|
||
p95Wii = Percentile(units.Select(u => u.Wii), 0.95)
|
||
};
|
||
|
||
var stmt = _attestor.BuildDsse(before, after, units, agg, _evidence.Hashes());
|
||
return await _attestor.SignAsync(stmt);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# Where it lives in Stella Ops
|
||
|
||
* **Concelier** (feeds): CVE → CVSS v4.0 and EPSS v4 hydration.
|
||
* **Excititor** (VEX): accepts WII + reachability to mark *Affected/Not Affected/Under Investigation* with evidence.
|
||
* **Scanner.WebService & Scanner.Workers**: build call graphs, compute diffs, ask Concelier/Excititor for scores, produce attestations.
|
||
* **Notify.WebService**: triggers when `aggregate.maxWii >= threshold` or when `reachable && epss_v4 > X`.
|
||
|
||
---
|
||
|
||
# Developer checklist (DoD)
|
||
|
||
* [ ] Per‑language call‑graph adapters: .NET, Java, Node, Python (symbol → entrypoint reach; shortest path).
|
||
* [ ] Smart‑Diff emits `unitId` at **function/method granularity** (fall back to package).
|
||
* [ ] Concelier endpoints to fetch **CVSS v4** and **EPSS v4** per `purl`/symbol.
|
||
* [ ] WiiScorer unit tests with frozen fixtures (golden files).
|
||
* [ ] DSSE attestation schema + JSON Schema validation.
|
||
* [ ] Rekor(v2) publish & local verification; offline bundle export.
|
||
* [ ] Policy gates: block deploy if `maxWii ≥ 70` **and** `reachable == true`.
|
||
* [ ] UI panel: show **top 5 WII units**, path preview, “why this score” explainer.
|
||
* [ ] CLI: `stella attest diff --before A --after B --emit dsse.json`.
|
||
|
||
---
|
||
|
||
# Why this strengthens the moat
|
||
|
||
* Turns **reachability** into a **determinism signal**, not just “found a CVE.”
|
||
* Binds **evidence** (graphs, SBOMs, metrics) into **signed, portable** proofs.
|
||
* Produces a **single, comparable index** that procurement/compliance and SREs can act on in seconds.
|
||
|
||
If you want, I can drop in a ready‑to‑run `.NET 10` sample project (Scanner plug‑in + WiiScorer tests) and a JSON‑Schema for the DSSE predicate.
|
||
Below is a **full, end‑to‑end implementation plan** to ship “Smart‑Diff + Call‑Stack Reachability + Weighted Impact Index + DSSE‑signed diff attestations” into production inside Stella Ops (Scanner / Concelier / Excititor / Notify), with clear sequencing, deliverables, and the concrete engineering work breakdown.
|
||
|
||
---
|
||
|
||
## 1) Target outcomes and success criteria
|
||
|
||
### Outcomes
|
||
|
||
1. **For any artifact upgrade (A → B)**, produce a **DSSE‑signed in‑toto Statement** that includes:
|
||
|
||
* What changed (Smart‑Diff)
|
||
* What is runtime reachable (Call‑Stack Reachability)
|
||
* A **per‑change Weighted Impact Index (WII)** and artifact aggregates (max, p95)
|
||
* Evidence digests (SBOMs, call graph, runtime heat, vuln feeds) bound to the attestation
|
||
|
||
2. **Policy gates** can block or require approval based on:
|
||
|
||
* `maxWII`, `reachable == true`, `epss/cvss thresholds`, “privileged path” flags, etc.
|
||
|
||
3. **Operators can explain the score**: top changes, entrypoints, shortest path, sinks, guards, heat.
|
||
|
||
### Success criteria (Definition of Done)
|
||
|
||
* ✅ Deterministic diff attestations (same inputs → same statement bytes before signing)
|
||
* ✅ Signature verification succeeds offline using published key(s)
|
||
* ✅ Correct reachability on representative services (route hits match predicted reachable set)
|
||
* ✅ Low noise: “unreachable changes” do not page
|
||
* ✅ Scoring is configurable and versioned (weights tracked)
|
||
* ✅ Works in CI and/or cluster scanning pipeline
|
||
* ✅ Stored in ledger + queryable DB + searchable UI
|
||
|
||
---
|
||
|
||
## 2) System architecture and data flow
|
||
|
||
### High-level data flow
|
||
|
||
```
|
||
Artifact A/B (OCI digest, build metadata)
|
||
├─ SBOM A/B (CycloneDX)
|
||
├─ Symbol Index A/B (function/type identifiers)
|
||
├─ Call Graph A/B (nodes=symbols, edges=calls, entrypoints)
|
||
├─ Runtime Heat (optional; APM/eBPF)
|
||
├─ Vuln Intelligence (CVSS v4, EPSS) from Concelier
|
||
│
|
||
└─ Smart-Diff (A vs B) -> DiffUnit[]
|
||
└─ Reachability enrich (A/B) -> features per unit
|
||
└─ WII scoring
|
||
└─ DSSE in-toto Statement (predicate)
|
||
└─ Sign (KMS/HSM/Key vault) -> DSSE envelope
|
||
├─ Publish to Ledger (Rekor-like)
|
||
├─ Store in Postgres + Object store
|
||
└─ Notify/Policy evaluation
|
||
```
|
||
|
||
### Services / modules to implement or extend
|
||
|
||
* **Scanner.Workers**: build evidence (SBOM, call graph), compute diff, compute reachability features, compute WII
|
||
* **Scanner.WebService**: APIs to request attestations, query results, verify
|
||
* **Concelier**: CVE → package/symbol mapping, CVSS v4 + EPSS hydration
|
||
* **Excititor**: produce/ingest VEX decisions using WII + reachability evidence
|
||
* **Notify**: alerting rules and policy gates for CI/CD and runtime
|
||
|
||
---
|
||
|
||
## 3) Core data contracts (must come first)
|
||
|
||
### 3.1 Stable identifiers
|
||
|
||
You need **stable IDs** for everything so diffs and reachability join correctly:
|
||
|
||
* **Artifact ID**: OCI digest (sha256) + image name + tag (tag is not trusted as primary)
|
||
* **Package ID**: PURL (package-url standard)
|
||
* **Symbol ID**: language-specific but normalized:
|
||
|
||
* `.NET`: `assembly::namespace.type::method(signature)` or `pdb mvid + token`
|
||
* Java: `class#method(desc)`
|
||
* Node: `module:path#exportName` (best-effort)
|
||
* Python: `module:function` (best-effort)
|
||
|
||
Rule: **Symbol IDs must remain stable across rebuilds** where possible (prefer token-based or fully-qualified signature).
|
||
|
||
### 3.2 Predicate schema v1
|
||
|
||
Lock the predicate shape early:
|
||
|
||
* `artifactBefore`, `artifactAfter` digests
|
||
* `evidence` digests/URIs (sbom, callGraph, runtimeHeat, vulnSnapshot)
|
||
* `units[]` (diff units with features, paths, wii)
|
||
* `aggregate` (max/p95 etc.)
|
||
* `scoring` (weights + version + normalizer caps)
|
||
* `generator` info (scanner version, build id)
|
||
|
||
Deliverables:
|
||
|
||
* JSON Schema for predicate `smart-diff-wii@v1`
|
||
* Protobuf (optional) for internal transport
|
||
* Golden-file fixtures for serialization determinism
|
||
|
||
---
|
||
|
||
## 4) Phase plan (sequenced deliverables, no guessy timelines)
|
||
|
||
### Milestone M0 — Foundations (must be completed before “real scoring”)
|
||
|
||
**Goal:** You can sign/verify attestations and store evidence.
|
||
|
||
**Work items**
|
||
|
||
1. **DSSE + in-toto Statement implementation**
|
||
|
||
* Choose signing primitive: Ed25519 or ECDSA P‑256
|
||
* Implement:
|
||
|
||
* Statement builder (canonical JSON)
|
||
* DSSE envelope wrapper
|
||
* Signature verify endpoint + CLI
|
||
* Add key rotation fields: `keyid`, `predicateType version`, `scanner build version`
|
||
|
||
2. **Evidence store**
|
||
|
||
* Object storage bucket layout:
|
||
|
||
* `/sbom/{artifactDigest}.cdx.json`
|
||
* `/callgraph/{artifactDigest}.cg.json`
|
||
* `/runtimeheat/{service}/{date}.json`
|
||
* `/vuln-snapshot/{date}.json`
|
||
* Every evidence object has digest recorded in DB
|
||
|
||
3. **Database schema**
|
||
|
||
* `artifacts(id, digest, name, createdAt, buildMetaJson)`
|
||
* `evidence(id, artifactDigest, kind, digest, uri, createdAt)`
|
||
* `attestations(id, subjectDigest, beforeDigest, afterDigest, predicateType, dsseDigest, createdAt, aggregateJson)`
|
||
* `attestation_units(attestationId, unitId, changeKind, reachable, wii, featuresJson, pathsJson)`
|
||
|
||
4. **Ledger integration**
|
||
|
||
* Minimal: append-only table + hash chaining (if you want quickly)
|
||
* Full: publish to Rekor-like transparency log if already present in your ecosystem
|
||
|
||
**DoD**
|
||
|
||
* `stella verify dsse.json` returns OK
|
||
* Stored attestations can be fetched by subject digest
|
||
* Evidence digests validate
|
||
|
||
---
|
||
|
||
### Milestone M1 — Smart‑Diff v1 (package + file level)
|
||
|
||
**Goal:** Produce a signed attestation that captures “what changed” even before reachability.
|
||
|
||
**Work items**
|
||
|
||
1. **SBOM ingestion & normalization**
|
||
|
||
* Parse CycloneDX SBOM
|
||
* Normalize component identity to PURL
|
||
* Extract versions, hashes, scopes, dependencies edges
|
||
|
||
2. **Diff engine (SBOM-level)**
|
||
|
||
* Added/removed/updated packages
|
||
* Transitive dependency changes
|
||
* License changes (optional)
|
||
* Output `DiffUnit[]` at package granularity first
|
||
|
||
3. **Attestation emitter**
|
||
|
||
* Populate predicate with:
|
||
|
||
* units (packages)
|
||
* aggregate metrics: number of changed packages; “risk placeholders” (no reachability yet)
|
||
|
||
**DoD**
|
||
|
||
* For a dependency bump PR, the system emits DSSE attestation with package diffs
|
||
|
||
---
|
||
|
||
### Milestone M2 — Call graph & reachability for .NET (first “real value”)
|
||
|
||
**Goal:** For .NET services, determine whether changed symbols/packages are reachable from entrypoints.
|
||
|
||
**Work items**
|
||
|
||
1. **.NET call graph builder**
|
||
|
||
* Implement Roslyn-based static analysis for:
|
||
|
||
* method invocations
|
||
* virtual calls (best-effort: conservative edges)
|
||
* async/await (capture call relations)
|
||
* Capture:
|
||
|
||
* nodes: methods/functions
|
||
* edges: caller → callee
|
||
* metadata: assembly, namespace, accessibility, source location (if available)
|
||
|
||
2. **Entrypoint extractor (.NET)**
|
||
|
||
* ASP.NET Core minimal APIs:
|
||
|
||
* `MapGet/MapPost/...` route mapping to delegate targets
|
||
* MVC/WebAPI:
|
||
|
||
* controller action methods
|
||
* gRPC endpoints
|
||
* Background workers:
|
||
|
||
* `IHostedService`, Hangfire jobs, Quartz jobs
|
||
* Message handlers:
|
||
|
||
* MassTransit / Kafka consumers (pattern match + config hooks)
|
||
|
||
3. **Reachability index**
|
||
|
||
* Store adjacency lists
|
||
* For each entrypoint, compute:
|
||
|
||
* reachable set
|
||
* shortest path length to each reachable node (BFS on unweighted graph)
|
||
* path witness (store 1–3 representative paths for explainability)
|
||
* Store:
|
||
|
||
* `distToNearestEntrypoint[node]`
|
||
* `nearestEntrypoint[node]`
|
||
* (optional) `countEntrypointsReaching[node]`
|
||
|
||
4. **Join Smart‑Diff with reachability**
|
||
|
||
* If you only have package diffs at this stage:
|
||
|
||
* Map package → symbol set using symbol index
|
||
* Mark unit reachable if any symbol reachable
|
||
* If you already have symbol diffs:
|
||
|
||
* Directly query reachability per symbol
|
||
|
||
**DoD**
|
||
|
||
* For a PR that changes a controller path or core code, top diffs show reachable paths
|
||
* For a PR that only touches unreachable code (dead feature flags), system marks unreachable
|
||
|
||
---
|
||
|
||
### Milestone M3 — Smart‑Diff v2 (symbol-level diffs)
|
||
|
||
**Goal:** Move from “package changed” to “what functions/types changed.”
|
||
|
||
**Work items**
|
||
|
||
1. **Symbol indexer (.NET)**
|
||
|
||
* Extract public/internal symbols
|
||
* Map symbol → file/module + hash of IL/body (or semantic hash)
|
||
* Record signature + accessibility + attributes
|
||
|
||
2. **Symbol-level diff**
|
||
|
||
* Added/removed/modified methods/types
|
||
* Semantic hashing to avoid noise from non-functional rebuild differences
|
||
* Generate unit IDs like:
|
||
|
||
* `pkg:nuget/Newtonsoft.Json@13.0.3#method:Namespace.Type::Method(args)`
|
||
|
||
3. **Unit grouping**
|
||
|
||
* Group symbol deltas under:
|
||
|
||
* package delta
|
||
* assembly delta
|
||
* “API surface delta” (public symbol changes) for exposure
|
||
|
||
**DoD**
|
||
|
||
* Attestation units list individual changed symbols with reachable evidence
|
||
|
||
---
|
||
|
||
### Milestone M4 — Feature extraction for WII
|
||
|
||
**Goal:** Compute the features that make WII meaningful and explainable.
|
||
|
||
**Work items**
|
||
|
||
1. **Exposure classification**
|
||
|
||
* `exposure=true` if symbol is:
|
||
|
||
* directly an entrypoint method
|
||
* in the shortest path to an entrypoint
|
||
* part of public API surface changes
|
||
* Store explanation: “reachable from HTTP POST /x”
|
||
|
||
2. **Privilege sink detection**
|
||
|
||
* Maintain a versioned sink catalog:
|
||
|
||
* deserialization entrypoints
|
||
* process execution
|
||
* filesystem writes
|
||
* network dial / SSRF primitives
|
||
* crypto key handling
|
||
* dynamic code evaluation
|
||
* Mark if any witness path crosses sinks
|
||
* Store sink list in `paths[]`
|
||
|
||
3. **Guard coverage detection**
|
||
|
||
* Catalog of guard functions:
|
||
|
||
* input validation, sanitizers, authz checks, schema validators
|
||
* Heuristic: on witness path, detect guard call before sink
|
||
* `guardCoverage=true` reduces WII
|
||
|
||
4. **Library depth**
|
||
|
||
* Compute “lib call depth” heuristics:
|
||
|
||
* number of frames from entrypoint to unit
|
||
* number of boundary crossings (app → lib → lib)
|
||
* Use in scoring normalization
|
||
|
||
5. **Runtime heat integration (optional but high impact)**
|
||
|
||
* Ingest APM sampling / pprof / eBPF:
|
||
|
||
* `(symbolId → invocationCount or CPU%)`
|
||
* Normalize to 0..1 `hotPath`
|
||
* Add mapping strategy:
|
||
|
||
* route names → controller action symbols
|
||
|
||
**DoD**
|
||
|
||
* Every unit has a features object with enough fields to justify its score
|
||
|
||
---
|
||
|
||
### Milestone M5 — Vulnerability intelligence + determinism linkage
|
||
|
||
**Goal:** Use Concelier data to raise score when reachable changes align with exploitable vulns.
|
||
|
||
**Work items**
|
||
|
||
1. **Vuln snapshot service (Concelier)**
|
||
|
||
* Provide API:
|
||
|
||
* `GET /vuln/by-purl?purl=...` → CVEs + CVSS v4 + EPSS
|
||
* `GET /vuln/snapshot/{date}` for reproducibility
|
||
|
||
2. **Package ↔ symbol ↔ vuln mapping**
|
||
|
||
* Map CVE affected package versions to `DiffUnit` packages
|
||
* (Optional advanced) map to symbols if your feed provides function-level info
|
||
|
||
3. **Determinism rule**
|
||
|
||
* If `reachable=true` AND (cvss>threshold OR epss>threshold) add bonus
|
||
* Record “why” in unit explanation metadata
|
||
|
||
**DoD**
|
||
|
||
* A dependency bump that introduces/removes a CVE changes WII accordingly
|
||
* Attestation includes vuln snapshot digest
|
||
|
||
---
|
||
|
||
### Milestone M6 — WII scoring engine v1 + calibration
|
||
|
||
**Goal:** Produce a stable numeric index and calibrate thresholds.
|
||
|
||
**Work items**
|
||
|
||
1. **Scoring engine**
|
||
|
||
* Implement WII as:
|
||
|
||
* weighted sum of normalized features
|
||
* clamp to 0..100
|
||
* Make scoring config **external + versioned**:
|
||
|
||
* weights
|
||
* normalization caps (e.g., delta path len capped at ±5)
|
||
* determinism bonus amounts
|
||
* Include `scoring.version` and config hash in predicate
|
||
|
||
2. **Golden tests**
|
||
|
||
* Fixture diffs with expected WII
|
||
* Regression tests for scoring changes (if weights change, version bumps)
|
||
|
||
3. **Calibration workflow**
|
||
|
||
* Backtest on historical PRs/incidents:
|
||
|
||
* correlate WII with incidents / rollbacks / sev tickets
|
||
* Produce recommended initial gate thresholds:
|
||
|
||
* block: `maxWII >= X` and reachable and privileged
|
||
* warn: `p95WII >= Y`
|
||
* Store calibration report as an artifact (so you can justify policy)
|
||
|
||
**DoD**
|
||
|
||
* Score doesn’t oscillate due to minor code movement
|
||
* Thresholds are defensible and adjustable
|
||
|
||
---
|
||
|
||
### Milestone M7 — Policy engine + CI/CD integration
|
||
|
||
**Goal:** Make it enforceable.
|
||
|
||
**Work items**
|
||
|
||
1. **Policy evaluation component**
|
||
|
||
* Inputs:
|
||
|
||
* DSSE attestation
|
||
* verification result
|
||
* environment context (prod/stage)
|
||
* Output:
|
||
|
||
* allow / warn / block + reason
|
||
|
||
2. **CI integration**
|
||
|
||
* Pipeline step:
|
||
|
||
* build artifact
|
||
* generate evidence
|
||
* compute diff against deployed baseline
|
||
* emit + sign attestation
|
||
* run policy gate
|
||
* Attach attestation to build metadata / OCI registry (as referrers if supported in your ecosystem)
|
||
|
||
3. **Deployment integration**
|
||
|
||
* Admission controller / deploy-time check:
|
||
|
||
* verify signature
|
||
* enforce policy
|
||
|
||
**DoD**
|
||
|
||
* A deployment with `reachable && maxWII >= threshold` is blocked or requires approval
|
||
|
||
---
|
||
|
||
### Milestone M8 — UI/UX and operator experience
|
||
|
||
**Goal:** People can understand and act quickly.
|
||
|
||
**Work items**
|
||
|
||
1. **Diff attestation viewer**
|
||
|
||
* Show:
|
||
|
||
* aggregate WII (max/p95)
|
||
* top units by WII
|
||
* per unit: features + witness path(s)
|
||
* sinks/guards
|
||
* vuln evidence (CVSS/EPSS) with snapshot date
|
||
|
||
2. **Explainability**
|
||
|
||
* “Why this score” breakdown:
|
||
|
||
* weights * feature values
|
||
* determinism bonus
|
||
* Link to evidence objects (SBOM digest, call graph digest)
|
||
|
||
3. **Notifications**
|
||
|
||
* Rules:
|
||
|
||
* page if `maxWII >= hard` and reachable and privileged
|
||
* slack/email if `maxWII >= warn`
|
||
* Include the top 3 units with paths
|
||
|
||
**DoD**
|
||
|
||
* Operators can make a decision within ~1–2 minutes reading the UI (no digging through logs)
|
||
|
||
---
|
||
|
||
### Milestone M9 — Multi-language expansion + runtime reachability improvements
|
||
|
||
**Goal:** Expand coverage beyond .NET and reduce static-analysis blind spots.
|
||
|
||
**Work items**
|
||
|
||
1. **Language adapters**
|
||
|
||
* Java: bytecode analyzer (ASM/Soot-like approach), Spring entrypoints
|
||
* Node: TypeScript compiler graph, Express/Koa routes (heuristics)
|
||
* Python: AST + import graph; Django/FastAPI routes (heuristics)
|
||
|
||
2. **Dynamic call handling**
|
||
|
||
* Reflection / DI / dynamic dispatch:
|
||
|
||
* conservative edges in static
|
||
* supplement with runtime traces to confirm
|
||
|
||
3. **Distributed reachability**
|
||
|
||
* Cross-service edges inferred from:
|
||
|
||
* OpenTelemetry traces (service A → service B endpoint)
|
||
* Build “service-level call graph” overlay:
|
||
|
||
* entrypoints + downstream calls
|
||
|
||
**DoD**
|
||
|
||
* Coverage reaches your top N services and languages
|
||
* False negatives reduced by runtime evidence
|
||
|
||
---
|
||
|
||
## 5) Detailed engineering work breakdown (by component)
|
||
|
||
### A) Smart‑Diff engine
|
||
|
||
**Deliverables**
|
||
|
||
* `ISmartDiff` interface with pluggable diff sources:
|
||
|
||
* SBOM diff
|
||
* lockfile diff
|
||
* symbol diff
|
||
* route diff (entrypoints changed)
|
||
|
||
**Key implementation tasks**
|
||
|
||
* Normalization layer (PURL, symbol IDs)
|
||
* Diff computation:
|
||
|
||
* add/remove/update
|
||
* semantic hash comparison
|
||
* Output:
|
||
|
||
* stable `DiffUnit` list
|
||
* deterministic ordering (sort by unitId)
|
||
|
||
**Risk controls**
|
||
|
||
* Deterministic hashing and ordering to keep DSSE stable
|
||
* “Noise filters” for rebuild-only diffs
|
||
|
||
---
|
||
|
||
### B) Call graph builder
|
||
|
||
**Deliverables**
|
||
|
||
* `CallGraph` object:
|
||
|
||
* nodes, edges
|
||
* node metadata
|
||
* entrypoints list
|
||
|
||
**Key tasks**
|
||
|
||
* Static analysis per language
|
||
* Entrypoint extraction (routes/jobs/consumers)
|
||
* Graph serialization format:
|
||
|
||
* versioned
|
||
* compressed adjacency lists
|
||
|
||
**Risk controls**
|
||
|
||
* Expect incomplete graphs; never treat as perfect truth
|
||
* Maintain confidence score per edge if desired
|
||
|
||
---
|
||
|
||
### C) Reachability service
|
||
|
||
**Deliverables**
|
||
|
||
* `IReachabilityService` with:
|
||
|
||
* `IsReachable(symbol)`
|
||
* `ShortestPathLen(symbol)`
|
||
* `PathPreview(symbol)` (witness)
|
||
* `LibCallDepth(symbol)`
|
||
* `PathHasPrivilegedSinks(symbol)`
|
||
* `PathHasGuards(symbol)`
|
||
|
||
**Key tasks**
|
||
|
||
* BFS from entrypoints
|
||
* Store distances and witnesses
|
||
* Cache per artifact digest
|
||
* Incremental updates:
|
||
|
||
* recompute only impacted parts when call graph changes (optional optimization)
|
||
|
||
---
|
||
|
||
### D) Feature extraction + WII scorer
|
||
|
||
**Deliverables**
|
||
|
||
* `WiiFeatures` + `WiiScorer`
|
||
* Versioned `ScoringConfig` (weights/normalizers)
|
||
|
||
**Key tasks**
|
||
|
||
* Normalization functions (caps and monotonic transforms)
|
||
* Determinism bonus logic
|
||
* Aggregation (max, p95, counts by changeKind)
|
||
|
||
**Risk controls**
|
||
|
||
* Scoring changes require a version bump
|
||
* Golden tests + backtests
|
||
|
||
---
|
||
|
||
### E) Attestation service
|
||
|
||
**Deliverables**
|
||
|
||
* `BuildStatement(...)`
|
||
* `SignDsse(...)`
|
||
* `VerifyDsse(...)`
|
||
|
||
**Key tasks**
|
||
|
||
* Canonical JSON serialization (avoid map-order randomness)
|
||
* Key management:
|
||
|
||
* key IDs
|
||
* rotation and revocation list handling
|
||
* Attach evidence digest set
|
||
|
||
**Risk controls**
|
||
|
||
* Sign only canonical bytes
|
||
* Record scanner version and config hash
|
||
|
||
---
|
||
|
||
### F) Persistence + ledger
|
||
|
||
**Deliverables**
|
||
|
||
* DB migrations
|
||
* Object store client
|
||
* Ledger publish/verify integration
|
||
|
||
**Key tasks**
|
||
|
||
* Store DSSE envelope bytes and computed digest
|
||
* Index by:
|
||
|
||
* subject digest
|
||
* before/after
|
||
* maxWII
|
||
* reachable count
|
||
* Retention policies
|
||
|
||
---
|
||
|
||
### G) Policy + Notifications
|
||
|
||
**Deliverables**
|
||
|
||
* Policy rules (OPA/Rego or internal DSL)
|
||
* CI step and deploy-time verifier
|
||
* Notify workflows
|
||
|
||
**Key tasks**
|
||
|
||
* Verification must be mandatory before policy evaluation
|
||
* Provide human-readable reasons
|
||
|
||
---
|
||
|
||
## 6) Testing strategy (ship safely)
|
||
|
||
### Unit tests
|
||
|
||
* Smart‑Diff normalization and diff correctness
|
||
* Reachability BFS correctness
|
||
* WII scoring determinism
|
||
* Predicate schema validation
|
||
* DSSE sign/verify roundtrip
|
||
|
||
### Integration tests
|
||
|
||
* Build sample .NET service → generate call graph → diff two versions → attest
|
||
* Concelier mocked responses for CVSS/EPSS
|
||
|
||
### End-to-end tests
|
||
|
||
* In CI: build → attest → store → verify → policy gate
|
||
* In deployment: admission check verifies signature and policy
|
||
|
||
### Performance tests
|
||
|
||
* Large call graph (100k+ nodes) BFS time and memory
|
||
* Batch scoring of thousands of diff units
|
||
|
||
### Security tests
|
||
|
||
* Tampered evidence digest detection
|
||
* Signature replay attempts (wrong subject digest)
|
||
* Key rotation tests
|
||
|
||
---
|
||
|
||
## 7) Rollout plan and operational guardrails
|
||
|
||
### Rollout stages
|
||
|
||
1. **Observe-only mode**
|
||
|
||
* Generate attestations, no gates
|
||
* Tune scoring weights and thresholds
|
||
|
||
2. **Warn mode**
|
||
|
||
* Notify only for high WII or reachable vuln combos
|
||
|
||
3. **Enforce mode**
|
||
|
||
* Block only on clear high-risk conditions (reachable + privileged + high WII)
|
||
* Add “break glass” path with audit logging
|
||
|
||
### Operational metrics
|
||
|
||
* Attestation generation success rate
|
||
* Verification failure rate
|
||
* Reachability coverage (% units with reachable computation)
|
||
* False positive/negative reports (human feedback)
|
||
* Policy gate blocks over time
|
||
|
||
### Playbooks
|
||
|
||
* What to do when:
|
||
|
||
* call graph generation fails
|
||
* Concelier feed unavailable
|
||
* signature verification fails
|
||
* scoring config mismatch
|
||
|
||
---
|
||
|
||
## 8) Concrete “first 10 commits” checklist
|
||
|
||
1. Add predicate JSON schema + canonical JSON serializer
|
||
2. Implement DSSE sign/verify library + CLI command
|
||
3. Create DB schema + evidence storage plumbing
|
||
4. Implement SBOM ingestion + SBOM diff -> `DiffUnit[]`
|
||
5. Emit DSSE attestation for SBOM diffs only
|
||
6. Implement .NET entrypoint extraction (minimal API + controllers)
|
||
7. Implement .NET call graph builder (basic invocations)
|
||
8. Implement reachability BFS + path witness extraction
|
||
9. Add WII scoring with config + golden tests
|
||
10. Add CI policy step (verify + evaluate thresholds) in warn-only mode
|
||
|
||
---
|
||
|
||
## 9) Deliverables bundle (what you should end up with)
|
||
|
||
* **Code**
|
||
|
||
* Smart‑Diff engine + plugins
|
||
* Call graph builders (starting with .NET)
|
||
* Reachability service + caching
|
||
* WII scoring service + config
|
||
* Attestation builder + DSSE signer/verifier
|
||
* Policy evaluation step
|
||
* UI endpoints + viewer
|
||
|
||
* **Schemas and specs**
|
||
|
||
* `smart-diff-wii@v1` JSON schema
|
||
* Evidence media types and versioning rules
|
||
* Scoring config format + versioning policy
|
||
|
||
* **Ops**
|
||
|
||
* Playbooks and runbooks
|
||
* Metrics dashboards
|
||
* Key rotation procedure
|
||
* Backtest/calibration report
|
||
|
||
---
|
||
|
||
If you want the plan converted into a **Jira-ready epic/story breakdown** (with each story having acceptance criteria and dependencies), tell me whether you’re implementing **only .NET first** or **multi-language from day one**—and I’ll output the backlog in that format.
|