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:

1.1 Core Identifiers

IdentifierFormatPurposeExample
symbol_idsym:{lang}:{base64url}Canonical function identitysym:java:R3JlZXRpbmc...
code_idcode:{lang}:{base64url}Identity for name-less code blockscode:binary:YWJjZGVm...
graph_hashblake3:{hex}Content-addressable graph identityblake3:a1b2c3d4e5f6...
symbol_digestsha256:{hex}Hash of symbol_id for edge linkingsha256:e5f6a7b8c9d0...
build_idgnu-build-id:{hex}ELF/PE debug identifiergnu-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):

LanguageTuple ComponentsExample
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:

  1. Scanner analyzes container/artifact
  2. Callgraph generators emit nodes with symbol_id, code_id, build_id
  3. RichGraphWriter canonicalizes (sorted arrays/keys) and computes graph_hash (BLAKE3)
  4. DSSE signer wraps canonical JSON
  5. CAS store persists body + envelope
  6. Signals ingestion API receives URI reference

7.2 Signals -> Policy

Signals provides reachability facts to Policy:

  1. Policy queries /signals/facts/{subjectKey}
  2. Response includes metadata.fact.digest, states[], score
  3. Policy gates check latticeState (U, SR, SU, RO, RU, CR, CU, X)
  4. Evidence blocks in findings reference graph_hash, path[], runtime_hits[]

7.3 Policy -> VEX/UI

Policy emits OpenVEX with evidence:

  1. VexDecisionEmitter serializes OpenVEX with stellaops:reachability extension
  2. UI explain drawer fetches evidence via /api/policy/findings/{id}/explain
  3. CLI stella graph explain renders 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


Last updated: 2025-12-13. See Sprint 0401 GAP-DOC-008 for change history.