67 KiB
67 KiB
Call Graph Analysis Pipeline
Overview
This document describes the ReachGraph module's call graph construction, analysis, and reachability determination. The call graph connects static analysis (from SBOM/binaries) with dynamic observation (from runtime) to produce high-confidence reachability verdicts.
Call Graph Construction
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ CALL GRAPH CONSTRUCTION PIPELINE │
└─────────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ INPUT SOURCES │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ SBOM │ │ Binaries │ │ Source Code │ │ Runtime │ │
│ │ (packages) │ │ (ELF/PE) │ │ (if avail) │ │ (hot syms) │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Package Deps │ │ Symbol Tables │ │ Import/Call │ │ Observed │ │
│ │ (DEPENDS_ON) │ │ (exports, │ │ Extraction │ │ Invocations │ │
│ │ │ │ imports) │ │ │ │ │ │
│ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ └───────┬───────┘ │
│ │ │ │ │ │
│ └──────────────────┴──────────────────┴──────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ GRAPH INDEXER │ │
│ │ (Node + Edge │ │
│ │ Construction) │ │
│ └────────────┬────────────┘ │
│ │ │
└──────────────────────────────────────┼──────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ GRAPH DATA MODEL │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ NODE TYPES │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ PackageNode │ │ FunctionNode │ │ FileNode │ │ │
│ │ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ │
│ │ │ purl: string │ │ name: string │ │ path: string │ │ │
│ │ │ version: string │ │ signature: str │ │ hash: string │ │ │
│ │ │ ecosystem: str │ │ address: u64 │ │ language: str │ │ │
│ │ │ license: str │ │ package: ref │ │ loc: (start,end)│ │ │
│ │ └─────────────────┘ │ file: ref │ │ package: ref │ │ │
│ │ │ visibility: enum│ └─────────────────┘ │ │
│ │ │ is_entry: bool │ │ │
│ │ └─────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ EntryPointNode │ │ CVENode │ │ │
│ │ ├─────────────────┤ ├─────────────────┤ │ │
│ │ │ type: HTTP|CLI │ │ id: string │ │ │
│ │ │ |EVENT|... │ │ severity: enum │ │ │
│ │ │ function: ref │ │ cvss: f32 │ │ │
│ │ │ route: string │ │ affected: [ref] │ │ │
│ │ │ method: string │ │ fixed: string │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ EDGE TYPES │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Edge Type │ Source │ Target │ Properties │ │ │
│ │ ├─────────────────┼─────────────────┼─────────────────┼─────────────┤ │ │
│ │ │ DEPENDS_ON │ Package │ Package │ scope, dev │ │ │
│ │ │ CONTAINS │ Package │ File │ │ │ │
│ │ │ DEFINES │ File │ Function │ line_start │ │ │
│ │ │ CALLS │ Function │ Function │ call_sites │ │ │
│ │ │ IMPORTS │ Package │ Package │ symbols[] │ │ │
│ │ │ ENTRY_FOR │ EntryPoint │ Function │ route │ │ │
│ │ │ AFFECTS │ CVE │ Package/Func │ version_rng │ │ │
│ │ │ OBSERVED │ Function │ Function │ count, ts │ │ │
│ │ └───────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Graph Construction by Source
Package Dependency Graph
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ PACKAGE DEPENDENCY GRAPH CONSTRUCTION │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ Input: SBOM (CycloneDX or SPDX) │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ SBOM PARSING │ │
│ │ │ │
│ │ CycloneDX: SPDX: │ │
│ │ ───────────── ─────── │ │
│ │ { { │ │
│ │ "components": [ "packages": [ │ │
│ │ { { │ │
│ │ "purl": "pkg:npm/express@4.18", "SPDXID": "SPDXRef-...", │ │
│ │ "type": "library" "name": "express", │ │
│ │ } "versionInfo": "4.18" │ │
│ │ ], } │ │
│ │ "dependencies": [ ], │ │
│ │ { "relationships": [ │ │
│ │ "ref": "pkg:npm/express@4.18", { │ │
│ │ "dependsOn": [ "relationshipType": │ │
│ │ "pkg:npm/body-parser@1.20" "DEPENDS_ON", │ │
│ │ ] "spdxElementId": "...", │ │
│ │ } "relatedSpdxElement": "..."│ │
│ │ ] } │ │
│ │ } ] │ │
│ │ } │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ GRAPH CONSTRUCTION │ │
│ │ │ │
│ │ For each component/package: │ │
│ │ 1. Create PackageNode with PURL as ID │ │
│ │ 2. Parse version constraints │ │
│ │ 3. Identify ecosystem (npm, maven, pypi, etc.) │ │
│ │ │ │
│ │ For each dependency relationship: │ │
│ │ 1. Create DEPENDS_ON edge │ │
│ │ 2. Mark scope (compile, runtime, dev, optional) │ │
│ │ 3. Track transitive vs direct │ │
│ │ │ │
│ │ Result: │ │
│ │ │ │
│ │ [myapp@1.0] ─DEPENDS_ON─► [express@4.18] ─DEPENDS_ON─► [body-parser@1.20] │ │
│ │ │ │ │ │
│ │ │ └──DEPENDS_ON─► [accepts@1.3] │ │
│ │ │ │ │
│ │ └──DEPENDS_ON─► [lodash@4.17] │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Binary Symbol Graph
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ BINARY SYMBOL GRAPH CONSTRUCTION │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ Input: ELF/PE/Mach-O binaries from container image │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ SYMBOL EXTRACTION │ │
│ │ │ │
│ │ ELF Binary: │ │
│ │ ──────────── │ │
│ │ │ │
│ │ .dynsym section: .symtab section (if not stripped): │ │
│ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ Symbol │ Type │ Bind│ │ Symbol │ Type │ Bind│ │ │
│ │ ├──────────┼───────┼─────┤ ├──────────┼───────┼─────┤ │ │
│ │ │ malloc │ FUNC │ GLOB│ │ main │ FUNC │ GLOB│ │ │
│ │ │ printf │ FUNC │ GLOB│ │ helper │ FUNC │ LOCAL│ │ │
│ │ │ __libc.. │ FUNC │ WEAK│ │ ... │ │ │ │ │
│ │ └─────────────────────────┘ └─────────────────────────┘ │ │
│ │ │ │
│ │ .plt / .got sections: DWARF debug info (if available): │ │
│ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ External call targets │ │ DW_TAG_subprogram │ │ │
│ │ │ (imported functions) │ │ name: "processData" │ │ │
│ │ │ malloc@GLIBC_2.2.5 │ │ decl_file: "main.c" │ │ │
│ │ │ SSL_read@OPENSSL_1_1 │ │ decl_line: 42 │ │ │
│ │ └─────────────────────────┘ │ low_pc: 0x1234 │ │ │
│ │ └─────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ CALL EDGE EXTRACTION │ │
│ │ │ │
│ │ For each function in binary: │ │
│ │ 1. Disassemble function body │ │
│ │ 2. Identify CALL/JMP instructions │ │
│ │ 3. Resolve call targets: │ │
│ │ - Direct calls: target address → symbol lookup │ │
│ │ - Indirect calls: [reg] or [mem] → mark as dynamic │ │
│ │ - PLT calls: resolve to external library symbol │ │
│ │ │ │
│ │ Example disassembly: │ │
│ │ ┌────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ processData: │ │ │
│ │ │ 0x1234: push rbp │ │ │
│ │ │ 0x1235: mov rbp, rsp │ │ │
│ │ │ ... │ │ │
│ │ │ 0x1250: call 0x4567 ; → resolves to validate() │ │ │
│ │ │ 0x1255: call 0x89ab@plt ; → resolves to SSL_read (extern) │ │ │
│ │ │ 0x125a: call [rax] ; → indirect call (unknown target) │ │ │
│ │ │ ... │ │ │
│ │ └────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Produces edges: │ │
│ │ [processData] ─CALLS─► [validate] (static, direct) │ │
│ │ [processData] ─CALLS─► [SSL_read] (static, external) │ │
│ │ [processData] ─CALLS─► [?] (dynamic, unresolved) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Source Code Analysis
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ SOURCE CODE CALL GRAPH EXTRACTION │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ When source code is available (e.g., interpreted languages), extract calls │
│ using language-specific parsers: │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ LANGUAGE-SPECIFIC EXTRACTION │ │
│ │ │ │
│ │ JavaScript/TypeScript: │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ // source.js │ │ │
│ │ │ const lodash = require('lodash'); │ │ │
│ │ │ const result = lodash.template(input); // ← call site │ │ │
│ │ │ │ │ │
│ │ │ Produces: │ │ │
│ │ │ [source.js:render] ─CALLS─► [lodash:template] │ │ │
│ │ │ [source.js] ─IMPORTS─► [lodash] │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Python: │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ # app.py │ │ │
│ │ │ import pickle │ │ │
│ │ │ data = pickle.loads(user_input) # ← dangerous call │ │ │
│ │ │ │ │ │
│ │ │ Produces: │ │ │
│ │ │ [app.py:process] ─CALLS─► [pickle:loads] │ │ │
│ │ │ [app.py] ─IMPORTS─► [pickle] │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Java: │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ // Service.java │ │ │
│ │ │ import org.apache.logging.log4j.Logger; │ │ │
│ │ │ logger.info("User: " + userInput); // ← CVE-2021-44228 │ │ │
│ │ │ │ │ │
│ │ │ Produces: │ │ │
│ │ │ [Service:handle] ─CALLS─► [Logger:info] │ │ │
│ │ │ [Service] ─IMPORTS─► [org.apache.logging.log4j] │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Graph Analytics Engine
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ GRAPH ANALYTICS ENGINE │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ LABEL PROPAGATION CLUSTERING │ │
│ │ │ │
│ │ Purpose: Group related packages/functions for impact analysis │ │
│ │ │ │
│ │ Algorithm: │ │
│ │ 1. Initialize: each node gets unique label │ │
│ │ 2. Iterate: each node adopts most common neighbor label │ │
│ │ 3. Converge: when labels stabilize │ │
│ │ │ │
│ │ Result: Clusters of tightly connected packages/functions │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Cluster A (HTTP handling): Cluster B (Data processing): │ │ │
│ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │
│ │ │ │ express │ │ lodash │ │ │ │
│ │ │ │ body-parser │ │ underscore │ │ │ │
│ │ │ │ cookie-parser │ │ ramda │ │ │ │
│ │ │ └─────────────────────┘ └─────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ Use case: "If CVE affects lodash, which other packages are │ │ │
│ │ │ in the same processing cluster and might be impacted?" │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ BETWEENNESS CENTRALITY │ │
│ │ │ │
│ │ Purpose: Identify critical nodes (bottlenecks, high-impact) │ │
│ │ │ │
│ │ Formula: C_B(v) = Σ (σ_st(v) / σ_st) │ │
│ │ Where: │ │
│ │ σ_st = number of shortest paths from s to t │ │
│ │ σ_st(v) = number of those paths passing through v │ │
│ │ │ │
│ │ High centrality nodes: │ │
│ │ • Commonly used utility functions (lodash.get, axios.request) │ │
│ │ • Core framework methods (express.Router) │ │
│ │ • Logging/serialization functions │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ [EntryPoint] ──► [auth.validate] ──► [user.fetch] ──► [db.query] │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ ▼ ▼ ▼ │ │ │
│ │ │ └────────► [logging.info] ◄─────────┴───────────────┘ │ │ │
│ │ │ ▲ │ │ │
│ │ │ HIGH CENTRALITY │ │ │
│ │ │ (many paths pass through) │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Reachability Analysis
BFS Path Finding
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ REACHABILITY SLICE SERVICE │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ BFS TRAVERSAL ALGORITHM │ │
│ │ │ │
│ │ Query: "Find all paths from entry points to vulnerable function" │ │
│ │ │ │
│ │ function findReachablePaths(graph, target_func, max_depth=10): │ │
│ │ paths = [] │ │
│ │ queue = [(entry, [entry]) for entry in graph.entry_points] │ │
│ │ visited = set() │ │
│ │ │ │
│ │ while queue: │ │
│ │ node, path = queue.pop(0) │ │
│ │ if len(path) > max_depth: │ │
│ │ continue │ │
│ │ │ │
│ │ if node == target_func: │ │
│ │ paths.append(path) │ │
│ │ continue │ │
│ │ │ │
│ │ if node in visited: │ │
│ │ continue │ │
│ │ visited.add(node) │ │
│ │ │ │
│ │ for edge in graph.outgoing_edges(node, type=CALLS): │ │
│ │ queue.append((edge.target, path + [edge.target])) │ │
│ │ │ │
│ │ return paths │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ SLICE TYPES │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ CVE Slice │ │ │
│ │ │ │ │ │
│ │ │ Input: CVE-2021-23337 (lodash template injection) │ │ │
│ │ │ │ │ │
│ │ │ Steps: │ │ │
│ │ │ 1. Find affected function: lodash.template │ │ │
│ │ │ 2. BFS from all entry points │ │ │
│ │ │ 3. Collect all paths reaching lodash.template │ │ │
│ │ │ │ │ │
│ │ │ Output: │ │ │
│ │ │ [ │ │ │
│ │ │ { path: [main.handler → render.compile → lodash.template], │ │ │
│ │ │ depth: 2, entry_type: HTTP }, │ │ │
│ │ │ { path: [cli.process → template.render → lodash.template], │ │ │
│ │ │ depth: 2, entry_type: CLI } │ │ │
│ │ │ ] │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Package Slice │ │ │
│ │ │ │ │ │
│ │ │ Input: pkg:npm/lodash@4.17.20 │ │ │
│ │ │ │ │ │
│ │ │ Steps: │ │ │
│ │ │ 1. Find all functions in package │ │ │
│ │ │ 2. BFS from all entry points to any package function │ │ │
│ │ │ 3. Group by function │ │ │
│ │ │ │ │ │
│ │ │ Output: │ │ │
│ │ │ { │ │ │
│ │ │ "lodash.get": { reachable: true, paths: [...] }, │ │ │
│ │ │ "lodash.set": { reachable: true, paths: [...] }, │ │ │
│ │ │ "lodash.template": { reachable: true, paths: [...] }, │ │ │
│ │ │ "lodash.chunk": { reachable: false } │ │ │
│ │ │ } │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Entry Point Slice (Forward) │ │ │
│ │ │ │ │ │
│ │ │ Input: HTTP /api/v1/render endpoint │ │ │
│ │ │ │ │ │
│ │ │ Steps: │ │ │
│ │ │ 1. Start from entry point function │ │ │
│ │ │ 2. BFS forward through all CALLS edges │ │ │
│ │ │ 3. Mark all reachable functions │ │ │
│ │ │ │ │ │
│ │ │ Output: Set of all functions reachable from this endpoint │ │ │
│ │ │ (Used for "attack surface" analysis) │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Eight-State Reachability Lattice
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ 8-STATE REACHABILITY LATTICE │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ STATE HIERARCHY │ │
│ │ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ LiveExploitPath │ HIGHEST │ │
│ │ │ (Confirmed exploitable │ CONFIDENCE │ │
│ │ │ with runtime proof) │ │ │
│ │ └────────────┬────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────▼────────────┐ │ │
│ │ │ DynamicReachable │ │ │
│ │ │ (Observed at runtime │ │ │
│ │ │ via eBPF/hot symbols) │ │ │
│ │ └────────────┬────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────▼────────────┐ │ │
│ │ │ StaticReachable │ │ │
│ │ │ (Call path found via │ │ │
│ │ │ static analysis) │ │ │
│ │ └────────────┬────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────▼────────────┐ │ │
│ │ │ PotentiallyReachable │ │ │
│ │ │ (Import exists but no │ │ │
│ │ │ direct call found) │ │ │
│ │ └────────────┬────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────▼────────────┐ │ │
│ │ │ Unknown │ DEFAULT │ │
│ │ │ (Insufficient data │ STATE │ │
│ │ │ to determine) │ │ │
│ │ └────────────┬────────────┘ │ │
│ │ │ │ │
│ │ ┌───────────────────────┼───────────────────────┐ │ │
│ │ │ │ │ │ │
│ │ ┌──────▼──────┐ ┌────────▼────────┐ ┌───────▼───────┐ │ │
│ │ │ NotReachable│ │ GateBlocked │ │ NotApplicable │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ Static │ │ Dead code path │ │ Language/OS │ │ │
│ │ │ analysis │ │ (ifdef, feature │ │ mismatch │ │ │
│ │ │ proves no │ │ flag disabled) │ │ │ │ │
│ │ │ path exists │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────────┘ └───────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ STATE DETERMINATION LOGIC │ │
│ │ │ │
│ │ function determineReachabilityState( │ │
│ │ static_paths: Path[], │ │
│ │ runtime_observations: Observation[], │ │
│ │ exploit_proofs: Proof[], │ │
│ │ platform_match: bool, │ │
│ │ gate_status: GateStatus │ │
│ │ ) -> ReachabilityState: │ │
│ │ │ │
│ │ if not platform_match: │ │
│ │ return NotApplicable │ │
│ │ │ │
│ │ if gate_status == BLOCKED: │ │
│ │ return GateBlocked │ │
│ │ │ │
│ │ if exploit_proofs: │ │
│ │ return LiveExploitPath │ │
│ │ │ │
│ │ if runtime_observations: │ │
│ │ return DynamicReachable │ │
│ │ │ │
│ │ if static_paths: │ │
│ │ return StaticReachable │ │
│ │ │ │
│ │ if import_exists: │ │
│ │ return PotentiallyReachable │ │
│ │ │ │
│ │ if no_path_proven: │ │
│ │ return NotReachable │ │
│ │ │ │
│ │ return Unknown │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Graph Snapshot and Determinism
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ GRAPH SNAPSHOT DETERMINISM │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────────────┐ │
│ │ GraphSnapshotBuilder │ │
│ │ │ │
│ │ Purpose: Create deterministic, hashable graph snapshots for: │ │
│ │ • Audit trails (prove analysis was consistent) │ │
│ │ • Caching (skip re-analysis if inputs unchanged) │ │
│ │ • DSSE attestation (sign analysis results) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Determinism Rules: │ │ │
│ │ │ │ │ │
│ │ │ 1. Node ordering: sorted by (type, purl/name, version) │ │ │
│ │ │ 2. Edge ordering: sorted by (type, source_id, target_id) │ │ │
│ │ │ 3. Property ordering: sorted alphabetically │ │ │
│ │ │ 4. Timestamps: ISO-8601 UTC (no local timezone) │ │ │
│ │ │ 5. Floating point: fixed precision (6 decimal places) │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Snapshot Structure: │ │
│ │ │ │
│ │ { │ │
│ │ "version": "1.0", │ │
│ │ "created_at": "2024-12-29T10:30:00Z", │ │
│ │ "image_digest": "sha256:abc...", │ │
│ │ "nodes": [ ... ], // sorted │ │
│ │ "edges": [ ... ], // sorted │ │
│ │ "reachability": { ... }, // computed states │ │
│ │ "hash": "sha256:snapshot_hash..." │ │
│ │ } │ │
│ │ │ │
│ │ Hash computation: │ │
│ │ hash = SHA256( │ │
│ │ canonical_json(nodes) || │ │
│ │ canonical_json(edges) || │ │
│ │ canonical_json(reachability) │ │
│ │ ) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Related Documentation
- Policy Engine Data Pipeline - How reachability feeds policy
- Runtime Agents Architecture - Runtime observation
- Binary Analysis - Binary symbol extraction
- ReachGraph Module - ReachGraph dossier
- Policy Evaluation Flow - K4 lattice usage