Function-Level Evidence Guide
Last updated: 2025-12-13. Owner: Docs Guild.
Audience: Scanner, Signals, Policy, and Replay contributors implementing or consuming function-level reachability evidence.
This guide documents the cross-module function-level evidence chain that lets Stella Ops make provable reachability claims. It covers the core identifiers, the richgraph schema, API usage, CLI commands, OpenVEX integration, and the replay manifest that ties it all together.
1. Overview
Stella Ops implements a function-level evidence chain that anchors every vulnerability finding to immutable identifiers (code_id, symbol_id, graph_hash), enabling:
- Provable reachability: Deterministic call-path evidence from entry points to vulnerable functions.
- Stripped binary support:
code_id+code_block_hashprovides identity when symbols are absent. - Evidence replay: Sealed artifacts with DSSE attestation allow offline verification.
- Cross-module linking: Scanner -> Signals -> Policy -> VEX -> UI/CLI evidence chain.
1.1 Core Identifiers
| Identifier | Format | Purpose | Example |
|---|---|---|---|
symbol_id | sym:{lang}:{base64url} | Canonical function identity | sym:java:R3JlZXRpbmc... |
code_id | code:{lang}:{base64url} | Identity for name-less code blocks | code:binary:YWJjZGVm... |
graph_hash | blake3:{hex} | Content-addressable graph identity | blake3:a1b2c3d4e5f6... |
symbol_digest | sha256:{hex} | Hash of symbol_id for edge linking | sha256:e5f6a7b8c9d0... |
build_id | gnu-build-id:{hex} | ELF/PE debug identifier | gnu-build-id:5f0c7c3c... |
1.2 Evidence Chain Flow
Scanner -> richgraph-v1 -> Signals -> Scoring -> Policy -> VEX -> UI/CLI
| | | | | | |
| | | | | | +-- stella graph explain
| | | | | +-- OpenVEX with call-path proofs
| | | | +-- Policy gates + reachability.state
| | | +-- Lattice state + confidence + riskScore
| | +-- Runtime facts + static paths
| +-- BLAKE3 graph_hash + DSSE attestation
+-- code_id, symbol_id, build_id per node
2. Schema Reference
2.1 SymbolID Construction
Per-language canonical tuple format (NUL-separated, then SHA-256 -> base64url):
| Language | Tuple Components | Example |
|---|---|---|
| Java | {package}\0{class}\0{method}\0{descriptor} | com.example\0Foo\0bar\0(Ljava/lang/String;)V |
| .NET | {assembly}\0{namespace}\0{type}\0{member_signature} | MyApp\0Controllers\0UserController\0GetById(int) |
| Go | {module}\0{package}\0{receiver}\0{func} | github.com/user/repo\0handler\0*Server\0Handle |
| Node | {pkg_or_path}\0{export_path}\0{kind} | lodash\0get\0function |
| Binary | {file_hash}\0{section}\0{addr}\0{name}\0{linkage}\0{code_block_hash?} | sha256:abc...\0.text\00x401000\0ssl3_read\0global\0 |
| Python | {pkg_or_path}\0{module}\0{qualified_name} | requests\0api\0get |
| Ruby | {gem_or_path}\0{module}\0{method} | rails\0ActionController::Base\0render |
| PHP | {composer_pkg}\0{namespace}\0{qualified_name} | symfony/http-kernel\0Kernel\0handle |
2.2 CodeID Construction
For stripped binaries or name-less code blocks:
code:{lang}:{base64url_sha256(format + file_hash + addr + length + section + code_block_hash)}
Example for stripped ELF:
code:binary:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo
2.3 Graph Node Schema
Each node in a richgraph-v1 document includes:
{
"id": "sym:java:R3JlZXRpbmdTZXJ2aWNl...",
"symbol_id": "sym:java:R3JlZXRpbmdTZXJ2aWNl...",
"code_id": "code:java:...",
"lang": "java",
"kind": "method",
"display": "com.example.GreetingService.greet(String)",
"purl": "pkg:maven/com.example/greeting-service@1.0.0",
"build_id": "gnu-build-id:5f0c7c3c...",
"symbol_digest": "sha256:e5f6a7b8...",
"code_block_hash": "sha256:deadbeef...",
"symbol": {
"mangled": null,
"demangled": "com.example.GreetingService.greet(String)",
"source": "DWARF",
"confidence": 0.98
},
"evidence": ["import", "bytecode"],
"attributes": {}
}
2.4 Graph Edge Schema
Edges carry callee purl and symbol_digest for SBOM correlation:
{
"from": "sym:java:caller...",
"to": "sym:java:callee...",
"kind": "call",
"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
"symbol_digest": "sha256:f1e2d3c4...",
"confidence": 0.92,
"evidence": ["bytecode", "import"],
"candidates": []
}
2.5 Evidence Block Schema
Evidence blocks in Policy/VEX responses cite all relevant identifiers:
{
"evidence": {
"graph_hash": "blake3:a1b2c3d4e5f6...",
"graph_cas_uri": "cas://reachability/graphs/a1b2c3d4e5f6...",
"dsse_uri": "cas://reachability/graphs/a1b2c3d4e5f6....dsse",
"path": [
{"symbol_id": "sym:java:...", "display": "main()"},
{"symbol_id": "sym:java:...", "display": "processRequest()"},
{"symbol_id": "sym:java:...", "display": "log4j.error()"}
],
"path_length": 3,
"confidence": 0.85,
"runtime_hits": ["probe:jfr:1234"],
"analyzer": {
"name": "scanner.java",
"version": "1.2.0",
"toolchain_digest": "sha256:..."
}
}
}
3. API Usage
3.1 Signals Callgraph Ingestion
Submit a callgraph and receive a deterministic graph_hash:
POST /signals/callgraphs
Authorization: Bearer <token>
Content-Type: application/json
{
"schema": "richgraph-v1",
"analyzer": {"name": "scanner.java", "version": "1.2.0"},
"nodes": [...],
"edges": [...],
"roots": [...]
}
Response:
{
"graphHash": "blake3:a1b2c3d4e5f6...",
"casUri": "cas://reachability/graphs/a1b2c3d4e5f6...",
"dsseUri": "cas://reachability/graphs/a1b2c3d4e5f6....dsse",
"nodeCount": 1247,
"edgeCount": 3891
}
3.2 Signals Runtime Facts
Submit runtime observations with code_id anchors:
POST /signals/runtime-facts/ndjson?scanId=scan-123&imageDigest=sha256:abc123
Authorization: Bearer <token>
Content-Type: application/x-ndjson
Content-Encoding: gzip
{"symbolId":"sym:java:...","codeId":"code:java:...","hitCount":47,"loaderBase":"0x7f...","processId":1234,"observedAt":"2025-12-13T10:00:00Z"}
{"symbolId":"sym:java:...","codeId":"code:java:...","hitCount":12,"loaderBase":"0x7f...","processId":1234,"observedAt":"2025-12-13T10:00:01Z"}
Response:
{
"accepted": 128,
"duplicates": 2,
"evidenceUri": "cas://reachability/runtime/sha256:xyz789..."
}
3.3 Fetch Reachability Facts
Query reachability state for a subject:
GET /signals/facts/{subjectKey}
Authorization: Bearer <token>
Response:
{
"subjectKey": "scan:123:pkg:maven/log4j:2.14.1:CVE-2021-44228",
"metadata": {
"fact": {
"digest": "sha256:abc123...",
"version": 3
}
},
"states": [
{
"symbol": "sym:java:...",
"latticeState": "CR",
"bucket": "runtime",
"confidence": 0.92,
"score": 0.78,
"path": ["sym:java:main...", "sym:java:process...", "sym:java:log4j..."],
"evidence": {
"static": {"graphHash": "blake3:...", "pathLength": 3, "confidence": 0.85},
"runtime": {"probeId": "probe:jfr:1234", "hitCount": 47, "observedAt": "2025-12-13T10:00:00Z"}
}
}
],
"score": 0.78,
"aggregateTier": "T2",
"riskScore": 0.65
}
3.4 Policy Findings with Reachability Evidence
GET /api/policy/findings/{policyId}/{findingId}/explain?mode=verbose
Authorization: Bearer <token>
Response (excerpt):
{
"findingId": "P-7:S-42:pkg:maven/log4j@2.14.1:CVE-2021-44228",
"reachability": {
"state": "CR",
"confidence": 0.92,
"evidence": {
"graph_hash": "blake3:a1b2c3d4...",
"path": [
{"symbol_id": "sym:java:...", "display": "main()"},
{"symbol_id": "sym:java:...", "display": "Logger.error()"}
],
"runtime_hits": 47,
"fact_digest": "sha256:abc123..."
}
},
"steps": [
{"rule": "reachability_gate", "state": "CR", "allowed": true},
{"rule": "severity_baseline", "severity": {"normalized": "Critical", "score": 10.0}}
]
}
4. CLI Usage
4.1 Graph Explain Command
View the call path and evidence for a finding:
stella graph explain --finding "pkg:maven/log4j@2.14.1:CVE-2021-44228" --scan-id scan-123
# Output:
Finding: CVE-2021-44228 in pkg:maven/log4j@2.14.1
Reachability: CONFIRMED_REACHABLE (CR)
Confidence: 0.92
Graph Hash: blake3:a1b2c3d4e5f6...
Call Path (3 hops):
1. main() [sym:java:R3JlZXRpbmcuLi4=]
-> processRequest() [direct call]
2. processRequest() [sym:java:cHJvY2Vzcy4uLg==]
-> Logger.error() [virtual call]
3. Logger.error() [sym:java:bG9nNGouLi4=]
[VULNERABLE: CVE-2021-44228]
Runtime Evidence:
- JFR probe hit: 47 times
- Last observed: 2025-12-13T10:00:00Z
DSSE Attestation: cas://reachability/graphs/a1b2c3d4....dsse
4.2 Graph Export Command
Export a reachability graph for offline analysis:
stella graph export --scan-id scan-123 --output ./evidence-bundle/
# Creates:
# ./evidence-bundle/richgraph-v1.json # Canonical graph
# ./evidence-bundle/richgraph-v1.json.dsse # DSSE envelope
# ./evidence-bundle/meta.json # Metadata
# ./evidence-bundle/runtime-facts.ndjson # Runtime observations
4.3 Graph Verify Command
Verify a graph’s DSSE signature and Rekor inclusion:
stella graph verify --graph ./evidence-bundle/richgraph-v1.json \
--dsse ./evidence-bundle/richgraph-v1.json.dsse \
--rekor-log
# Output:
Graph Hash: blake3:a1b2c3d4e5f6...
DSSE Signature: VALID (key: scanner-signing-2025)
Rekor Entry: 12345678 (verified)
Timestamp: 2025-12-13T09:30:00Z
5. OpenVEX Integration
5.1 OpenVEX with Reachability Evidence
When Policy emits VEX decisions, reachability evidence is included:
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://stellaops.example/vex/2025-12-13/001",
"author": "StellaOps Policy Engine",
"timestamp": "2025-12-13T10:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": {"@id": "CVE-2021-44228"},
"products": [{"@id": "pkg:oci/myapp@sha256:abc123..."}],
"status": "affected",
"justification": "vulnerable_code_in_container",
"impact_statement": "Vulnerable Log4j method reachable from main entry point.",
"action_statement": "Upgrade to log4j 2.17.1 or later.",
"stellaops:reachability": {
"state": "CR",
"confidence": 0.92,
"graph_hash": "blake3:a1b2c3d4e5f6...",
"path_length": 3,
"evidence_uri": "cas://reachability/graphs/a1b2c3d4..."
}
}
]
}
5.2 VEX “not_affected” with Unreachability Evidence
When code is provably unreachable:
{
"statements": [
{
"vulnerability": {"@id": "CVE-2023-XXXXX"},
"products": [{"@id": "pkg:oci/myapp@sha256:abc123..."}],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"impact_statement": "Vulnerable function not reachable from any entry point.",
"stellaops:reachability": {
"state": "CU",
"confidence": 0.88,
"graph_hash": "blake3:d4e5f6a7b8c9...",
"evidence_uri": "cas://reachability/graphs/d4e5f6a7b8c9...",
"runtime_observation_window": "72h",
"runtime_hits": 0
}
}
]
}
6. Replay Manifest v2
6.1 Manifest Structure
Replay manifests now enforce BLAKE3 hashing and CAS registration:
{
"schema": "stellaops.replay.manifest@v2",
"subject": "scan:123",
"generatedAt": "2025-12-13T10:00:00Z",
"hashAlg": "blake3",
"artifacts": [
{
"kind": "richgraph",
"uri": "cas://reachability/graphs/blake3:a1b2c3d4e5f6...",
"hash": "blake3:a1b2c3d4e5f6...",
"dsseUri": "cas://reachability/graphs/blake3:a1b2c3d4e5f6....dsse"
},
{
"kind": "runtime-facts",
"uri": "cas://reachability/runtime/sha256:xyz789...",
"hash": "sha256:xyz789..."
},
{
"kind": "sbom",
"uri": "cas://scanner-artifacts/sbom.cdx.json",
"hash": "sha256:def456..."
}
],
"analyzer": {
"name": "scanner.java",
"version": "1.2.0",
"toolchain_digest": "sha256:..."
},
"code_id_coverage": {
"total_symbols": 1247,
"with_code_id": 1189,
"coverage_pct": 95.3
}
}
6.2 Determinism Verification
Replay a manifest to verify determinism:
stella replay verify --manifest ./manifest.json --sealed
# Output:
Manifest: stellaops.replay.manifest@v2
Subject: scan:123
Artifacts: 3
Verifying richgraph...
Computed: blake3:a1b2c3d4e5f6...
Expected: blake3:a1b2c3d4e5f6...
Status: MATCH
Verifying runtime-facts...
Computed: sha256:xyz789...
Expected: sha256:xyz789...
Status: MATCH
Verifying sbom...
Computed: sha256:def456...
Expected: sha256:def456...
Status: MATCH
All artifacts verified. Determinism check PASSED.
7. Module Integration Guide
7.1 Scanner -> Signals
Scanner emits richgraph-v1 with code_id and symbol_id:
- Scanner analyzes container/artifact
- Callgraph generators emit nodes with
symbol_id,code_id,build_id - RichGraphWriter canonicalizes (sorted arrays/keys) and computes
graph_hash(BLAKE3) - DSSE signer wraps canonical JSON
- CAS store persists body + envelope
- Signals ingestion API receives URI reference
7.2 Signals -> Policy
Signals provides reachability facts to Policy:
- Policy queries
/signals/facts/{subjectKey} - Response includes
metadata.fact.digest,states[],score - Policy gates check
latticeState(U, SR, SU, RO, RU, CR, CU, X) - Evidence blocks in findings reference
graph_hash,path[],runtime_hits[]
7.3 Policy -> VEX/UI
Policy emits OpenVEX with evidence:
- VexDecisionEmitter serializes OpenVEX with
stellaops:reachabilityextension - UI explain drawer fetches evidence via
/api/policy/findings/{id}/explain - CLI
stella graph explainrenders call path and attestation refs
8. CAS Layout Reference
cas://reachability/
graphs/
{blake3}/ # Graph body (canonical JSON)
{blake3}.dsse # DSSE envelope
edges/
{graph_hash}/{bundle_id} # Edge bundle body (optional)
{graph_hash}/{bundle_id}.dsse
runtime/
{sha256}/ # Runtime facts NDJSON
9. Related Documentation
- Reachability Lattice Model — state definitions and join rules
- richgraph-v1 Contract — schema specification
- Evidence Schema — detailed field definitions
- Signals API Contract — API reference
- Policy Gates — gate configuration
- Hybrid Attestation — graph and edge-bundle DSSE
- Ground Truth Schema — test fixture format
Last updated: 2025-12-13. See Sprint 0401 GAP-DOC-008 for change history.
