PURL-Resolved Callgraph Edges

Audience: Scanner and Signals engineers implementing the binary-callgraph-to-SBOM join.

This note captures the required behavior for joining binary callgraphs with SBOM components using PURL + symbol-digest annotations. It supersedes the prior advisories; everything needed to ship the feature is here.

1. Goal

Annotate every call edge in richgraph-v1 with:

This lets graphs from multiple binaries merge naturally and line up with SBOM entries, so reachability answers “is the vulnerable function reachable in my deployment?” without re-identifying components.

2. Data model additions

3. Producer rules

  1. Map callee → file → SBOM component. Use import tables (ELF DT_NEEDED + reloc, PE IAT, Mach-O stubs) or resolved path. If multiple candidates, emit candidates[] and lower confidence.
  2. Compute symbol digest. Normalize the signature, demangle if possible, lowercase type names, strip addresses, then sha256 the canonical form. For stripped symbols, combine synthetic name and code block hash.
  3. Attach to edges. For every call edge, set purl and symbol_digest. If the callee is external but unresolved, emit purl:"pkg:unknown" and also write an Unknowns entry (see the Signals unknowns registry).
  4. Determinism. Sort nodes and edges before hashing; keep evidence arrays sorted (import, reloc, disasm, runtime). Graph hash uses BLAKE3 over canonical JSON.

4. Consumer rules

5. SBOM join strategy

  1. Use purl from component resolver; if absent, fall back to build_id plus hash match and emit purl:"pkg:unknown".
  2. When multiple SBOM components share a purl, keep all matches but prefer those whose file hash equals the binary under analysis.
  3. For runtime traces, attach the same symbol_digest so runtime hits boost confidence on the correct edge.

6. Acceptance tests

7. Deliverables