themed the bulk of advisories

This commit is contained in:
StellaOps Bot
2025-12-14 19:58:38 +02:00
parent 00c41790f4
commit 9202cd7da8
63 changed files with 6377 additions and 0 deletions

View File

@@ -0,0 +1,992 @@
# Reachability Analysis Technical Reference
**Source Advisories**:
- 05-Dec-2025 - Building a Deterministic, ReachabilityFirst Architecture
- 13-Dec-2025 - Designing the CallStack 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 SmartDiff and CallStack 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