themed the bulk of advisories
This commit is contained in:
@@ -0,0 +1,992 @@
|
||||
# Reachability Analysis Technical Reference
|
||||
|
||||
**Source Advisories**:
|
||||
- 05-Dec-2025 - Building a Deterministic, Reachability‑First Architecture
|
||||
- 13-Dec-2025 - Designing the Call‑Stack Reachability Engine
|
||||
- 03-Dec-2025 - Reachability Benchmarks and Moat Metrics
|
||||
- 09-Dec-2025 - Caching Reachability the Smart Way
|
||||
- 04-Dec-2025 - Ranking Unknowns in Reachability Graphs
|
||||
- 02-Dec-2025 - Designing Deterministic Reachability UX
|
||||
- 05-Dec-2025 - Design Notes on Smart‑Diff and Call‑Stack Analysis
|
||||
|
||||
**Last Updated**: 2025-12-14
|
||||
|
||||
---
|
||||
|
||||
## 1. ARCHITECTURE PATTERNS
|
||||
|
||||
### 1.1 Core System Architecture
|
||||
|
||||
**Module Responsibilities**
|
||||
- `StellaOps.Scanner.WebService`: Authoritative for reachability pipeline and lattice computation
|
||||
- Language workers: Stateless compute producing `CallGraph.v1.json`
|
||||
- Runtime collectors: Agent/sidecar emitting evidence events only
|
||||
- Concelier/Excititor: Provide pruned sources; never compute reachability
|
||||
- Authority: Manages replay manifests, crypto profiles
|
||||
- Scheduler: Executes rescan/escalation policies
|
||||
|
||||
**Architectural Rules**
|
||||
- Scanner = origin of truth for reachability
|
||||
- Concelier/Vexer = prune-preservers only
|
||||
- Authority = replay manifest owner
|
||||
- Scheduler = executor of policies
|
||||
- Postgres = System of Record (SoR)
|
||||
- Valkey = ephemeral only (dedupe, hot cache, rate limits)
|
||||
|
||||
### 1.2 Evidence Graph Structure
|
||||
|
||||
**Node Types**
|
||||
- `Artifact`, `Component`, `Vulnerability`, `Attestation`, `Build`, `Deployment`, `RuntimeSignal`
|
||||
|
||||
**Edge Types**
|
||||
- `DESCRIBES`, `AFFECTS`, `NOT_AFFECTED_BY`, `FIXED_IN`, `DERIVED_FROM`, `DEPLOYS`, `OBSERVED_AT_RUNTIME`
|
||||
|
||||
**Edge Signing**
|
||||
- Sign edges, not just nodes (edge = claim)
|
||||
|
||||
### 1.3 Determinism Requirements
|
||||
|
||||
**Input Manifest Structure**
|
||||
```jsonc
|
||||
{
|
||||
"scannerVersion": "1.3.0",
|
||||
"rulesetId": "stella-default-2025.11",
|
||||
"feeds": {
|
||||
"nvdDigest": "sha256:...",
|
||||
"osvDigest": "sha256:..."
|
||||
},
|
||||
"sbomDigest": "sha256:...",
|
||||
"policyDigest": "sha256:..."
|
||||
}
|
||||
```
|
||||
|
||||
**Canonicalization Rules**
|
||||
- Sort arrays by stable keys
|
||||
- Normalize paths (POSIX style)
|
||||
- Line endings (LF)
|
||||
- Encodings (UTF-8)
|
||||
- No environment variables in core algorithms
|
||||
- No machine-local files
|
||||
- No system clock inside algorithms
|
||||
|
||||
## 2. DATA CONTRACTS
|
||||
|
||||
### 2.1 CallGraph.v1.json Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "stella.callgraph.v1",
|
||||
"scanKey": "uuid",
|
||||
"language": "dotnet|java|node|python|go|rust|binary",
|
||||
"artifacts": [{
|
||||
"artifactKey": "…",
|
||||
"kind": "assembly|jar|module|binary",
|
||||
"sha256": "…"
|
||||
}],
|
||||
"nodes": [{
|
||||
"nodeId": "…",
|
||||
"artifactKey": "…",
|
||||
"symbolKey": "Namespace.Type::Method(…)",
|
||||
"visibility": "public|internal|private|unknown",
|
||||
"isEntrypointCandidate": false
|
||||
}],
|
||||
"edges": [{
|
||||
"from": "nodeId",
|
||||
"to": "nodeId",
|
||||
"kind": "static|heuristic",
|
||||
"reason": "direct_call|virtual_call|reflection_string|di_binding|dynamic_import|unknown",
|
||||
"weight": 1.0
|
||||
}],
|
||||
"entrypoints": [{
|
||||
"nodeId": "…",
|
||||
"kind": "http|grpc|cli|job|event|unknown",
|
||||
"route": "/api/orders/{id}",
|
||||
"framework": "aspnetcore|minimalapi|spring|express|unknown"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 RuntimeEvidence.v1.json Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "stella.runtimeevidence.v1",
|
||||
"scanKey": "uuid",
|
||||
"collectedAt": "2025-12-14T10:00:00Z",
|
||||
"environment": {
|
||||
"os": "linux|windows",
|
||||
"k8s": {"namespace": "…", "pod": "…", "container": "…"},
|
||||
"imageDigest": "sha256:…",
|
||||
"buildId": "…"
|
||||
},
|
||||
"samples": [{
|
||||
"timestamp": "…",
|
||||
"pid": 1234,
|
||||
"threadId": 77,
|
||||
"frames": ["nodeId","nodeId","nodeId"],
|
||||
"sampleWeight": 1.0
|
||||
}],
|
||||
"loadedArtifacts": [{
|
||||
"artifactKey": "…",
|
||||
"evidence": "loaded_module|mapped_file|jar_loaded"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 ReplayManifest.json Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "stella.replaymanifest.v1",
|
||||
"scanId": "uuid",
|
||||
"inputs": {
|
||||
"sbomDigest": "sha256:…",
|
||||
"callGraphs": [{"language":"dotnet","digest":"sha256:…"}],
|
||||
"runtimeEvidence": [{"digest":"sha256:…"}],
|
||||
"concelierSnapshot": "sha256:…",
|
||||
"excititorSnapshot": "sha256:…",
|
||||
"policyDigest": "sha256:…"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 ProofSpine Data Model
|
||||
|
||||
```csharp
|
||||
public sealed record ProofSpine(
|
||||
string SpineId,
|
||||
string ArtifactId,
|
||||
string VulnerabilityId,
|
||||
string PolicyProfileId,
|
||||
IReadOnlyList<ProofSegment> Segments,
|
||||
string Verdict,
|
||||
string VerdictReason,
|
||||
string RootHash,
|
||||
string ScanRunId,
|
||||
DateTimeOffset CreatedAt,
|
||||
string? SupersededBySpineId
|
||||
);
|
||||
|
||||
public sealed record ProofSegment(
|
||||
string SegmentId,
|
||||
string SegmentType,
|
||||
int Index,
|
||||
string InputHash,
|
||||
string ResultHash,
|
||||
string? PrevSegmentHash,
|
||||
DsseEnvelope Envelope,
|
||||
string ToolId,
|
||||
string ToolVersion,
|
||||
string Status
|
||||
);
|
||||
```
|
||||
|
||||
**Segment Types**
|
||||
- `SBOM_SLICE`: Component relevance
|
||||
- `MATCH`: SBOM-to-vuln mapping
|
||||
- `REACHABILITY`: Symbol reachability
|
||||
- `GUARD_ANALYSIS`: Config/feature flag gates
|
||||
- `RUNTIME_OBSERVATION`: Runtime evidence
|
||||
- `POLICY_EVAL`: Lattice decision
|
||||
|
||||
## 3. DATABASE SCHEMAS
|
||||
|
||||
### 3.1 Core Reachability Tables (Postgres)
|
||||
|
||||
```sql
|
||||
-- Scan tracking
|
||||
CREATE TABLE scan (
|
||||
scan_id uuid PRIMARY KEY,
|
||||
created_at timestamptz,
|
||||
repo_uri text,
|
||||
commit_sha text,
|
||||
sbom_digest text,
|
||||
policy_digest text,
|
||||
status text
|
||||
);
|
||||
CREATE INDEX idx_scan_cache ON scan(commit_sha, sbom_digest);
|
||||
|
||||
-- Artifacts
|
||||
CREATE TABLE artifact (
|
||||
artifact_id uuid PRIMARY KEY,
|
||||
scan_id uuid REFERENCES scan,
|
||||
artifact_key text,
|
||||
kind text,
|
||||
sha256 text,
|
||||
build_id text,
|
||||
purl text,
|
||||
UNIQUE(scan_id, artifact_key)
|
||||
);
|
||||
|
||||
-- Call graph nodes
|
||||
CREATE TABLE cg_node (
|
||||
scan_id uuid,
|
||||
node_id text,
|
||||
artifact_key text,
|
||||
symbol_key text,
|
||||
visibility text,
|
||||
flags int,
|
||||
PRIMARY KEY(scan_id, node_id)
|
||||
);
|
||||
|
||||
-- Call graph edges
|
||||
CREATE TABLE cg_edge (
|
||||
scan_id uuid,
|
||||
from_node_id text,
|
||||
to_node_id text,
|
||||
kind smallint,
|
||||
reason smallint,
|
||||
weight real,
|
||||
PRIMARY KEY(scan_id, from_node_id, to_node_id, kind, reason)
|
||||
);
|
||||
CREATE INDEX idx_cg_edge_from ON cg_edge(scan_id, from_node_id);
|
||||
CREATE INDEX idx_cg_edge_to ON cg_edge(scan_id, to_node_id);
|
||||
|
||||
-- Entrypoints
|
||||
CREATE TABLE entrypoint (
|
||||
scan_id uuid,
|
||||
node_id text,
|
||||
kind text,
|
||||
framework text,
|
||||
route text,
|
||||
PRIMARY KEY(scan_id, node_id, kind, framework, route)
|
||||
);
|
||||
|
||||
-- Runtime samples
|
||||
CREATE TABLE runtime_sample (
|
||||
scan_id uuid,
|
||||
collected_at timestamptz,
|
||||
env_hash text,
|
||||
sample_id bigserial PRIMARY KEY,
|
||||
timestamp timestamptz,
|
||||
pid int,
|
||||
thread_id int,
|
||||
frames text[],
|
||||
weight real
|
||||
);
|
||||
|
||||
-- Symbol-to-component mapping
|
||||
CREATE TABLE symbol_component_map (
|
||||
scan_id uuid,
|
||||
node_id text,
|
||||
purl text,
|
||||
mapping_kind text,
|
||||
confidence real,
|
||||
PRIMARY KEY(scan_id, node_id, purl)
|
||||
);
|
||||
|
||||
-- Reachability results
|
||||
CREATE TABLE reachability_component (
|
||||
scan_id uuid,
|
||||
purl text,
|
||||
status smallint,
|
||||
confidence real,
|
||||
why jsonb,
|
||||
evidence jsonb,
|
||||
PRIMARY KEY(scan_id, purl)
|
||||
);
|
||||
|
||||
CREATE TABLE reachability_finding (
|
||||
scan_id uuid,
|
||||
cve_id text,
|
||||
purl text,
|
||||
status smallint,
|
||||
confidence real,
|
||||
why jsonb,
|
||||
evidence jsonb,
|
||||
PRIMARY KEY(scan_id, cve_id, purl)
|
||||
);
|
||||
```
|
||||
|
||||
### 3.2 Unknowns Ranking Tables
|
||||
|
||||
```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
|
||||
);
|
||||
```
|
||||
|
||||
### 3.3 Proof Spine Tables
|
||||
|
||||
```sql
|
||||
CREATE TABLE proof_spines (
|
||||
spine_id uuid PRIMARY KEY,
|
||||
artifact_id text,
|
||||
vuln_id text,
|
||||
policy_profile_id text,
|
||||
verdict text,
|
||||
verdict_reason text,
|
||||
root_hash text,
|
||||
scan_run_id uuid,
|
||||
created_at timestamptz,
|
||||
superseded_by_spine_id uuid,
|
||||
segment_count int
|
||||
);
|
||||
CREATE INDEX idx_spine_lookup ON proof_spines(artifact_id, vuln_id, policy_profile_id);
|
||||
|
||||
CREATE TABLE proof_segments (
|
||||
segment_id uuid PRIMARY KEY,
|
||||
spine_id uuid REFERENCES proof_spines,
|
||||
idx int,
|
||||
segment_type text,
|
||||
input_hash text,
|
||||
result_hash text,
|
||||
prev_segment_hash text,
|
||||
envelope bytea,
|
||||
tool_id text,
|
||||
tool_version text,
|
||||
status text,
|
||||
created_at timestamptz
|
||||
);
|
||||
```
|
||||
|
||||
## 4. ALGORITHMS
|
||||
|
||||
### 4.1 Reachability Status Classification
|
||||
|
||||
**Status Values**
|
||||
- `UNREACHABLE`: No path from entrypoints
|
||||
- `POSSIBLY_REACHABLE`: Graph incomplete/dynamic behavior
|
||||
- `REACHABLE_STATIC`: Static path exists
|
||||
- `REACHABLE_PROVEN`: Runtime evidence confirms
|
||||
|
||||
**Confidence Scoring (Deterministic)**
|
||||
|
||||
Base scores:
|
||||
```
|
||||
UNREACHABLE → 0.05
|
||||
POSSIBLY_REACHABLE → 0.35
|
||||
REACHABLE_STATIC → 0.70
|
||||
REACHABLE_PROVEN → 0.95
|
||||
```
|
||||
|
||||
Modifiers:
|
||||
```
|
||||
+0.10 if path uses only static edges
|
||||
-0.15 if path includes reflection_string|dynamic_import
|
||||
+0.10 if runtime evidence hits affected component
|
||||
-0.10 if NO_ENTRYPOINTS_DISCOVERED
|
||||
Clamp to [0, 1]
|
||||
```
|
||||
|
||||
### 4.2 Reachability Computation Algorithm
|
||||
|
||||
```
|
||||
Inputs:
|
||||
- Call graph nodes/edges + entrypoints
|
||||
- Runtime evidence (optional)
|
||||
- SBOM with purls
|
||||
- Vulnerability facts (CVE ↔ purl/version)
|
||||
- VEX statements
|
||||
|
||||
Steps:
|
||||
1. Build adjacency list for cg_edge.kind in (static, heuristic)
|
||||
2. Optional: Compress SCCs (Tarjan/Kosaraju)
|
||||
3. Seed from entrypoints; if empty → mark POSSIBLY_REACHABLE with NO_ENTRYPOINTS_DISCOVERED
|
||||
4. Traverse reachable nodes; track:
|
||||
- firstSeenFromEntrypoint[node]
|
||||
- pathWitness[node]
|
||||
5. Map reachable nodes to purls via symbol_component_map:
|
||||
Priority order:
|
||||
a. Exact binary symbol → package metadata
|
||||
b. Assembly/jar/module to SBOM component (hash/purl)
|
||||
c. Heuristics: namespace prefixes, import paths, jar manifest
|
||||
6. Runtime evidence upgrade:
|
||||
- Mark frame nodes as "executed"
|
||||
- Mint runtime edges: consecutive frames → runtime_minted
|
||||
- Upgrade to REACHABLE_PROVEN if executed node maps to affected purl
|
||||
7. Compute confidence using deterministic formula
|
||||
```
|
||||
|
||||
### 4.3 Unknowns Ranking Algorithm
|
||||
|
||||
**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
|
||||
)
|
||||
```
|
||||
|
||||
**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)
|
||||
```
|
||||
|
||||
**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)
|
||||
```
|
||||
|
||||
**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)
|
||||
```
|
||||
|
||||
### 4.4 Node ID Computation (.NET)
|
||||
|
||||
**Primary (IL-based)**
|
||||
```
|
||||
nodeId = SHA256(MVID + ":" + metadataToken + ":" + arity + ":" + signatureShape)
|
||||
```
|
||||
|
||||
**Fallback (source-only)**
|
||||
```
|
||||
nodeId = SHA256(projectPath + ":" + file + ":" + span + ":" + symbolDisplayString)
|
||||
```
|
||||
|
||||
**External nodes**
|
||||
```
|
||||
nodeId = SHA256("ext:" + artifactKey + ":" + symbolKey)
|
||||
```
|
||||
|
||||
### 4.5 Canonical Symbol Key Format
|
||||
|
||||
```
|
||||
{Namespace}.{TypeName}[`Arity][+Nested]::{MethodName}[`Arity]({ParamType1},{ParamType2},...)
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Use `System.*` full names for BCL types
|
||||
- Use `+` for nested types (metadata style)
|
||||
- Use backtick arity for generic type/method definitions
|
||||
- Arrays: `System.String[]`
|
||||
- Byref: `System.String&`
|
||||
|
||||
### 4.6 Reachability Cache Algorithm
|
||||
|
||||
```csharp
|
||||
public readonly record struct ReachKey(
|
||||
string AlgoSig, // e.g., "RTA@sha256:…"
|
||||
string InputsHash, // SBOM slice + policy + versions
|
||||
string CallPathHash // normalized query graph
|
||||
);
|
||||
|
||||
public sealed class ReachCache {
|
||||
private readonly ConcurrentDictionary<ReachKey, Lazy<Task<ReachResult>>> _memo = new();
|
||||
|
||||
public Task<ReachResult> GetOrComputeAsync(
|
||||
ReachKey key,
|
||||
Func<Task<ReachResult>> compute,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var lazy = _memo.GetOrAdd(key, _ => new Lazy<Task<ReachResult>>(
|
||||
() => compute(), LazyThreadSafetyMode.ExecutionAndPublication));
|
||||
|
||||
return lazy.Value.ContinueWith(t => {
|
||||
if (t.IsCompletedSuccessfully) return t.Result;
|
||||
_memo.TryRemove(key, out _);
|
||||
throw t.Exception ?? new Exception("reachability failed");
|
||||
}, ct);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Operational rules**
|
||||
- Canonical everything: sort nodes/edges, normalize paths
|
||||
- Cache scope: per-scan, per-workspace, or per-feed version
|
||||
- TTL: 15-60 minutes or evict after pipeline completes
|
||||
- Max-entries cap
|
||||
- Concurrency: `Lazy<Task<>>` coalesces duplicate in-flight calls
|
||||
|
||||
### 4.7 Proof Spine Construction
|
||||
|
||||
```
|
||||
1. Sbomer produces SBOM_SLICE segment, DSSE-signs it
|
||||
2. Scanner produces MATCH segment
|
||||
3. Reachability produces REACHABILITY segment
|
||||
4. Guard Analyzer produces GUARD_ANALYSIS segment
|
||||
5. Vexer evaluates lattice, produces POLICY_EVAL segment
|
||||
6. ProofSpineBuilder:
|
||||
- Sorts segments by predetermined order
|
||||
- Chains PrevSegmentHash
|
||||
- Computes RootHash = hash(concat of segment hashes)
|
||||
- Stores ProofSpine with deterministic SpineId
|
||||
```
|
||||
|
||||
## 5. API CONTRACTS
|
||||
|
||||
### 5.1 Scanner Ingestion Endpoints
|
||||
|
||||
```
|
||||
POST /api/scans
|
||||
Returns: scanId
|
||||
|
||||
POST /api/scans/{scanId}/callgraphs
|
||||
Body: CallGraph.v1.json
|
||||
Header: Content-Digest (for idempotency)
|
||||
|
||||
POST /api/scans/{scanId}/runtimeevidence
|
||||
Body: RuntimeEvidence.v1.json
|
||||
|
||||
POST /api/scans/{scanId}/sbom
|
||||
Body: CycloneDX/SPDX
|
||||
|
||||
POST /api/scans/{scanId}/compute-reachability
|
||||
Idempotent trigger
|
||||
```
|
||||
|
||||
### 5.2 Query Endpoints
|
||||
|
||||
```
|
||||
GET /api/scans/{scanId}/reachability/components?purl=...
|
||||
GET /api/scans/{scanId}/reachability/findings?cve=...
|
||||
GET /api/scans/{scanId}/reachability/explain?cve=...&purl=...
|
||||
Returns: why[], path witness, sample refs
|
||||
```
|
||||
|
||||
### 5.3 Export Endpoints
|
||||
|
||||
```
|
||||
GET /api/scans/{scanId}/exports/sarif
|
||||
GET /api/scans/{scanId}/exports/cdxr # CycloneDX reachability extension
|
||||
GET /api/scans/{scanId}/exports/openvex
|
||||
```
|
||||
|
||||
### 5.4 Proof Spine Endpoints
|
||||
|
||||
```
|
||||
GET /graph/runtime/{podId}/lineage
|
||||
GET /graph/image/{digest}/vex
|
||||
GET /spines/{spineId}
|
||||
Returns: ProofSpine with all segments
|
||||
```
|
||||
|
||||
## 6. .NET IMPLEMENTATION PATTERNS
|
||||
|
||||
### 6.1 .NET Worker (Roslyn + IL) Required Features
|
||||
|
||||
**Call Graph Extraction**
|
||||
- Direct invocation edges: `InvocationExpressionSyntax`
|
||||
- Object creation edges: constructors
|
||||
- Delegate invocation: record heuristic edge when target unresolved
|
||||
- Virtual/interface dispatch: record `virtual_call` edge to declared method
|
||||
- Async/await: connect logical caller → awaited method
|
||||
|
||||
**Entrypoint Discovery**
|
||||
- `Program.Main` (classic)
|
||||
- ASP.NET Core:
|
||||
- Controllers: `[ApiController]`, route attributes, action methods
|
||||
- Minimal APIs: `MapGet/MapPost/MapMethods` patterns
|
||||
- gRPC: `MapGrpcService<T>()` and service methods
|
||||
- Hosted services: `IHostedService`, `BackgroundService.ExecuteAsync`
|
||||
|
||||
**Reflection and DI Heuristics**
|
||||
- `Type.GetType("…")`, `Assembly.GetType`, `GetMethod("…")`, `Invoke`
|
||||
- `services.AddTransient<IFoo,Foo>()` / `AddScoped` / `AddSingleton`
|
||||
- `Activator.CreateInstance`, `ServiceProvider.GetService`
|
||||
- Produce heuristic edges with `reason = di_binding` or `reflection_string`
|
||||
|
||||
### 6.2 NuGet Dependencies
|
||||
|
||||
```xml
|
||||
<!-- Roslyn / MSBuild -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Build.Locator" Version="*" />
|
||||
|
||||
<!-- IL + metadata -->
|
||||
<!-- Use System.Reflection.Metadata (BCL) -->
|
||||
<!-- Optional: Mono.Cecil for faster IL traversal -->
|
||||
|
||||
<!-- CLI + JSON -->
|
||||
<PackageReference Include="System.CommandLine" Version="*" />
|
||||
<PackageReference Include="System.Text.Json" Version="*" />
|
||||
```
|
||||
|
||||
### 6.3 Roslyn IL Opcodes for Call Detection
|
||||
|
||||
```
|
||||
Recognize as "calls":
|
||||
- call
|
||||
- callvirt
|
||||
- newobj
|
||||
- jmp
|
||||
- ldftn
|
||||
- ldvirtftn
|
||||
```
|
||||
|
||||
### 6.4 MSBuild Workspace Loading Pattern
|
||||
|
||||
```csharp
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.MSBuild;
|
||||
|
||||
var ws = MSBuildWorkspace.Create();
|
||||
var sln = await ws.OpenSolutionAsync(@"path\to.sln");
|
||||
foreach (var proj in sln.Projects)
|
||||
foreach (var doc in proj.Documents)
|
||||
{
|
||||
var model = await doc.GetSemanticModelAsync();
|
||||
var root = await doc.GetSyntaxRootAsync();
|
||||
foreach (var node in root.DescendantNodes()
|
||||
.OfType<InvocationExpressionSyntax>())
|
||||
{
|
||||
var sym = model.GetSymbolInfo(node).Symbol as IMethodSymbol;
|
||||
if (sym != null)
|
||||
{
|
||||
// record edge: caller -> sym
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. CONFIGURATION PATTERNS
|
||||
|
||||
### 7.1 Crypto Profiles
|
||||
|
||||
```csharp
|
||||
public interface ICryptoProfile
|
||||
{
|
||||
string ProfileId { get; }
|
||||
byte[] Sign(byte[] data, string keyId);
|
||||
bool Verify(byte[] data, byte[] signature, string keyId);
|
||||
}
|
||||
|
||||
// Implementations: FipsProfile, GostProfile, EidasProfile, SmProfile, PqcProfile
|
||||
```
|
||||
|
||||
**Profile Metadata**
|
||||
- Algorithm
|
||||
- Key ID
|
||||
- Profile name
|
||||
|
||||
**Key Rotation**
|
||||
- Keys have validity intervals
|
||||
- Spines keep KeyId in each DSSE signature
|
||||
- Authority maintains trust table: which keys trusted for which SegmentType and time window
|
||||
|
||||
### 7.2 Offline Bundle Format
|
||||
|
||||
**Required Contents**
|
||||
- SBOM + feeds + policy bundle + key material
|
||||
- Manifest with hashes of all contents
|
||||
- Replay manifest for deterministic rerun
|
||||
|
||||
**Format**
|
||||
- Zip/tar + manifest
|
||||
- Each entry content-addressed
|
||||
|
||||
### 7.3 Cache Configuration (appsettings)
|
||||
|
||||
```json
|
||||
{
|
||||
"Scanner": {
|
||||
"Reach": {
|
||||
"Cache": {
|
||||
"MaxEntries": 10000,
|
||||
"TtlMinutes": 60,
|
||||
"EvictOnPipelineComplete": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. METRICS AND THRESHOLDS
|
||||
|
||||
### 8.1 Performance SLOs (v1)
|
||||
|
||||
```
|
||||
Medium service (100k LOC .NET) static graph: < 2 minutes
|
||||
Reachability compute: < 30 seconds
|
||||
Query GET finding: < 200ms p95
|
||||
```
|
||||
|
||||
### 8.2 Quality Metrics
|
||||
|
||||
**Proof verification**
|
||||
- % verified proofs
|
||||
- Proof verification failures
|
||||
- Proof age since last verification
|
||||
- % entries with valid inclusion proof (Rekor)
|
||||
|
||||
**Unknowns triage**
|
||||
- Hot/Warm/Cold distribution
|
||||
- Rescan success rate after N attempts
|
||||
- Evidence staleness distribution
|
||||
|
||||
**Drift detection**
|
||||
- Runtime edges not in static graph (above threshold → emit COVERAGE_DRIFT warning)
|
||||
|
||||
### 8.3 Benchmark Metrics (Moat)
|
||||
|
||||
| Metric | Target |
|
||||
|--------|--------|
|
||||
| Time-to-evidence: SBOM → signed call-graph | < 5 minutes for 100k LOC service |
|
||||
| SBOM-diff false positive rate under dependency churn | < 5% change in reachability status for non-code changes |
|
||||
| Deterministic priority scoring under air-gap replay | Bit-identical results given same inputs |
|
||||
| Proof verification time | < 200ms p95 |
|
||||
|
||||
## 9. TESTING PATTERNS
|
||||
|
||||
### 9.1 Golden Corpus Requirements
|
||||
|
||||
**Mandatory Test Cases**
|
||||
1. Minimal ASP.NET controller with reachable endpoint → vulnerable lib call
|
||||
2. Vulnerable lib present but never called → unreachable
|
||||
3. Reflection-based activation → "possible" unless runtime proves
|
||||
4. BackgroundService job case
|
||||
5. Version range ambiguity
|
||||
6. Mismatched epoch/backport
|
||||
7. Missing CVSS vector
|
||||
8. Conflicting severity vendor/NVD
|
||||
9. Unanchored filesystem library
|
||||
|
||||
**Assertions per Test**
|
||||
- Reachability status
|
||||
- At least one `why[]` reason
|
||||
- Deterministic confidence within ±0.01
|
||||
- Segments with expected status (verified/partial/invalid)
|
||||
|
||||
### 9.2 Replay Manifest Tests
|
||||
|
||||
Given manifest containing:
|
||||
- feed hashes
|
||||
- rules version
|
||||
- normalization logic
|
||||
- lattice rules
|
||||
|
||||
Assert: ranking/reachability recomputes identically (byte-for-byte)
|
||||
|
||||
### 9.3 Signature Tampering Tests
|
||||
|
||||
- Flip byte in DSSE payload → UI must show `invalid`
|
||||
- Mark key untrusted → segments show `untrusted-key`
|
||||
- Entire spine marked as compromised
|
||||
|
||||
## 10. SPECIFICATION COMPLIANCE
|
||||
|
||||
### 10.1 SBOM Standards
|
||||
|
||||
**CycloneDX**
|
||||
- Version: 1.6 (ECMA-424) and 1.7 (current)
|
||||
- Media type: include version parameter
|
||||
- Ingest: validate against ECMA-424/1.7 schemas
|
||||
|
||||
**SPDX**
|
||||
- Version: 3.0.1
|
||||
- Validate against canonical spec
|
||||
|
||||
**Ingestion Rules**
|
||||
- Accept only `*.cdx.json` and `*.spdx.json`
|
||||
- Hard fail others
|
||||
- Store: raw bytes + parsed form + normalized canonical form
|
||||
|
||||
### 10.2 VEX Standards
|
||||
|
||||
**OpenVEX**
|
||||
- Minimal, SBOM-agnostic
|
||||
- Predicate type: `https://openvex.dev/ns/v0.2.0`
|
||||
|
||||
**CSAF VEX**
|
||||
- Alternative format for interoperability
|
||||
|
||||
**Required VEX Fields**
|
||||
- status: `not_affected|affected|fixed|under_investigation`
|
||||
- justification
|
||||
- timestamp
|
||||
- author
|
||||
- link to supporting evidence
|
||||
|
||||
### 10.3 Attestation Standards
|
||||
|
||||
**DSSE (Dead Simple Signing Envelope)**
|
||||
- Use for all signed artifacts
|
||||
- Payload: canonical JSON
|
||||
- Envelope: signature + key metadata + profile
|
||||
|
||||
**in-toto**
|
||||
- Statement structure
|
||||
- Predicate types:
|
||||
- SBOM: `https://spdx.dev/Document`
|
||||
- VEX: OpenVEX predicate URI
|
||||
- Custom: reachability predicates
|
||||
|
||||
**Cosign Integration**
|
||||
```bash
|
||||
cosign attest --yes --type https://spdx.dev/Document \
|
||||
--predicate sbom.spdx.json \
|
||||
--key cosign.key \
|
||||
"${IMAGE_DIGEST}"
|
||||
```
|
||||
|
||||
### 10.4 Rekor Integration
|
||||
|
||||
**CLI Verification**
|
||||
```bash
|
||||
rekor-cli verify --rekor_server https://rekor.sigstore.dev \
|
||||
--signature artifact.sig \
|
||||
--public-key cosign.pub \
|
||||
--artifact artifact.bin
|
||||
```
|
||||
|
||||
**Persistence per Entry**
|
||||
- Rekor UUID
|
||||
- Log index
|
||||
- Integrated time
|
||||
- Inclusion proof data
|
||||
|
||||
## 11. CLI COMMANDS
|
||||
|
||||
### 11.1 Worker CLI
|
||||
|
||||
```bash
|
||||
# Artifacts-first scan
|
||||
stella-worker-dotnet scan \
|
||||
--scanKey 00000000-0000-0000-0000-000000000000 \
|
||||
--assemblies ./artifacts/bin/Release \
|
||||
--out ./callgraph.json
|
||||
|
||||
# Build-and-scan
|
||||
stella-worker-dotnet scan \
|
||||
--scanKey ... \
|
||||
--sln ./src/MySolution.sln \
|
||||
--configuration Release \
|
||||
--tfm net10.0 \
|
||||
--buildMode build \
|
||||
--out ./callgraph.json
|
||||
|
||||
# Upload to scanner.webservice
|
||||
stella-worker-dotnet scan \
|
||||
--scanKey ... \
|
||||
--assemblies ./artifacts/bin/Release \
|
||||
--upload https://scanner/api/scans/{scanId}/callgraphs \
|
||||
--apiKey $STELLA_API_KEY
|
||||
```
|
||||
|
||||
### 11.2 Replay CLI
|
||||
|
||||
```bash
|
||||
stellaops scan \
|
||||
--replay-manifest <id-or-file> \
|
||||
--artifact <image-digest> \
|
||||
--vuln <cve> \
|
||||
--explain
|
||||
```
|
||||
|
||||
### 11.3 Reachability Query CLI
|
||||
|
||||
```bash
|
||||
stella scan graph --lang dotnet --sln path.sln --out graph.scc.json
|
||||
stella scan runtime --target pod/myservice --duration 30s --out stacks.json
|
||||
stella reachability join \
|
||||
--graph graph.scc.json \
|
||||
--runtime stacks.json \
|
||||
--sbom bom.cdx.json \
|
||||
--out reach.cdxr.json
|
||||
```
|
||||
|
||||
## 12. DEVELOPER CHECKLIST
|
||||
|
||||
### 12.1 Per-Feature Definition of Done
|
||||
|
||||
For any feature touching scans, VEX, or evidence:
|
||||
|
||||
- [ ] Deterministic: input manifest defined, canonicalization applied, golden fixture(s) added
|
||||
- [ ] Evidence: outputs DSSE-wrapped and linked
|
||||
- [ ] Reachability/Lattice: runs only in allowed services, records algorithm IDs
|
||||
- [ ] Crypto: calls through profile abstraction, tests for ≥2 profiles
|
||||
- [ ] Graph: lineage edges added, node/edge IDs stable and queryable
|
||||
- [ ] UX/API: API to retrieve structured evidence
|
||||
- [ ] Tests: unit + golden + integration test with full SBOM → scan → VEX chain
|
||||
|
||||
### 12.2 Determinism Checklist
|
||||
|
||||
- [ ] Input manifest type defined and versioned
|
||||
- [ ] Canonicalization applied before hashing/signing
|
||||
- [ ] Output stored with `inputsDigest` and `algoDigest`
|
||||
- [ ] At least one golden fixture proves determinism
|
||||
- [ ] No environment variables in core algorithm
|
||||
- [ ] No machine-local files
|
||||
- [ ] No system clock inside algorithms
|
||||
|
||||
### 12.3 Reachability Implementation Checklist
|
||||
|
||||
- [ ] Reachability algorithms only in Scanner.WebService
|
||||
- [ ] Cache lazy and keyed by deterministic inputs
|
||||
- [ ] Output includes explicit evidence pointers
|
||||
- [ ] UI endpoints expose reachability state in structured form
|
||||
- [ ] All modifiers recorded in `why[]`
|
||||
|
||||
### 12.4 Crypto-Sovereign Checklist
|
||||
|
||||
- [ ] No direct crypto calls in feature code; only via profile layer
|
||||
- [ ] All attestations carry algorithm + key id + profile
|
||||
- [ ] Offline bundle type exists for this workflow
|
||||
- [ ] Tests for at least 2 different crypto profiles
|
||||
|
||||
### 12.5 Policy/Lattice Checklist
|
||||
|
||||
- [ ] Facts and policies serialized separately
|
||||
- [ ] Lattice code in allowed services only
|
||||
- [ ] Merge strategies named and versioned
|
||||
- [ ] Artifacts record which lattice algorithm used
|
||||
|
||||
### 12.6 Proof-Linked VEX Checklist
|
||||
|
||||
- [ ] VEX schema includes pointers to all upstream artifacts (sbomDigest, scanId, reachMapDigest, policyDigest, signerKeyId)
|
||||
- [ ] No duplication of SBOM/scan content inside VEX
|
||||
- [ ] DSSE used as standard envelope type
|
||||
|
||||
### 12.7 Unknowns Triage Checklist
|
||||
|
||||
- [ ] Persist all traces for deterministic replay
|
||||
- [ ] Ranking depends only on manifest-declared parameters
|
||||
- [ ] All uncertainty factors are explicit flags
|
||||
- [ ] Scoring reproducible under identical inputs
|
||||
- [ ] Scheduler decision table deterministic and tested
|
||||
- [ ] API exposes full reasoning
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Target Platform**: .NET 10, PostgreSQL ≥16, Angular v17
|
||||
Reference in New Issue
Block a user