603 lines
67 KiB
Markdown
603 lines
67 KiB
Markdown
# 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](policy-engine-data-pipeline.md) - How reachability feeds policy
|
||
- [Runtime Agents Architecture](runtime-agents-architecture.md) - Runtime observation
|
||
- [Binary Analysis](../../modules/binary-index/architecture.md) - Binary symbol extraction
|
||
- [ReachGraph Module](../../modules/reachgraph/architecture.md) - ReachGraph dossier
|
||
- [Policy Evaluation Flow](../../flows/04-policy-evaluation-flow.md) - K4 lattice usage
|