The reference for every reachability mechanism the harness runs: which engines exist, what each may conclude, who consumes the result, and the guardrails that bound it. The per-language coverage matrix is in language-support.md.
Coverage is tracked, not just documented. /drift-watch's
reachability-coverage:* rows flag any portfolio language dominant across
many repositories with no symbol-tier engine, and reachability-watch:review
puts a review clock on the revisit triggers that have no machine feed. A
language with no engine is a standing precision gap, not a neutral absence:
every dependency hit in it ceilings at likely_affected on manifest evidence.
Joern does not deliver dependency reachability for Java, and the reason is structural. Java resolves calls through interfaces; an advisory names a library-internal implementation, and the static call graph cannot connect the application's interface call to that implementation. Widening the CPG to the dependency closure links the JARs but still does not resolve the path. Measured on a full Maven advisory sweep the promotion rate to
affectedwas zero, and the Java tier was withdrawn. The C/C++ Joern tier is promotion-only: a found call site upgrades evidence; an absent path proves nothing. Before adding or restoring a Joern reachability tier, answer: does the language resolve calls statically or through interfaces/duck typing; does the validation corpus contain library-internal advisory symbols or only directly-called API; does the tier change a classification or only an evidence level; and what is its measured promotion rate on a real advisory corpus.
Reachability tooling obeys the deterministic-tooling doctrine (deterministic-inferential-mix.md): a deterministic tool may route, gate, tag, or index — never conclude. Reachability output is a tag with evidence attached (a call path or call site), consumed by audit/triage agents and classification logic. It is never a finding, never a verdict, and never grounds for auto-dismissal.
| Engine | Languages | Question answered | Soundness | Artifact |
|---|---|---|---|---|
| govulncheck (python3 -m traust.cli adapters govulncheck) | Go | is a vulnerable function on a static call path from first-party code? | Sound both directions: symbol_reachable promotes; package_imported_not_observed / module_required_not_observed are honest negatives (vocabulary is deliberately not_observed, never "unreachable") |
<repo>-govulncheck.json — one candidate per OSV advisory, shortest observed call path caller-first, full tool/DB/SHA provenance at emission time |
Joern c2cpg (python3 -m traust.cli adapters joern, --language c) |
C/C++ | does first-party code call the vulnerable function directly? Exact function-name matching (prefix matching over-matches short C identifiers) | Promotion-only: a found call site upgrades evidence; an absent path proves nothing (function pointers hide edges). Line numbers are approximate — file + caller function are authoritative | <clone>-joern-reachability.json — call sites with caller attribution, test_path tags, explicit caps, soundness block on every artifact |
Symbol-usage scan (inside traust_engine.impact) |
Java, Python, JS/TS, Rust, Ruby, .NET | does first-party source reference the vulnerable module's symbols textually? | presence evidence, not a call path — caps at symbol-usage. For Java, traust_engine.impact.expand_advisory_entry_points first expands a library-internal advisory symbol to the public API an application calls, so the scan looks for the right names |
recorded in impact-analysis evidence fields |
| ELF scans (python3 -m traust.cli util elf, via impact-analysis) | C/C++, Go binaries | is the library linked (DT_NEEDED) / are the vulnerable symbol names present in the shipped binary's strings? |
presence evidence, not a call path — caps at symbol-usage/binary tiers |
recorded in impact-analysis evidence fields |
| Taint-flow enumerator (python3 -m traust.ops.enumerate_taint_flows) | Java, C | related but distinct: does request data flow source→sink? Emits judged rows, not reachability verdicts | opt-in ops tool, invoked by no skill; calibration against a precision bar gates any default use | flow rows with path excerpts, passes_through, sanitizer_hint |
The Joern adapter itself still supports the Java frontend (javasrc2cpg);
nothing in the impact pipeline invokes it, for the reason in the note above.
/impact-analysis records per-repo evidence_level
(symbol > symbol-usage > binary > manifest > none) and classifies:
affectedrequires symbol-tier proof: govulnchecksymbol_reachable(Go) or a Joern resolved direct call to the vulnerable function itself (C/C++, witness cited injoern_witness).- Manifest evidence ceilings at
likely_affected— "a pin is never reachability." Textual symbol usage and package-level calls reachsymbol-usage, same ceiling. - Only Go may demote on a negative (govulncheck's
not_observedclasses are sound). The Joern tier never demotes anything —no_call_sites_foundis recorded honestly and the classification stays where the cheaper tiers put it.
/impact-analysis— classification per the ladder above; CVE blast-radius sweeps./secure-code-auditPrecision Gate — a critical/high Go dependency-CVE finding may not be filed without govulncheck symbol-mode evidence; manifest-only matches downgrade todependency_audit./triage— two paths: a SHA-matched<repo>-govulncheck.jsonbeside the baseline feeds matched findings aDEPENDENCY-REACHABILITYblock as annotation-only verifier context (no vote-count changes); andnormalize_input.pyingests govulncheck native-jsonstreams as candidates with their reachability classes preserved./verify-remediation—--impact-filterscopes full sweeps to repos an impact analysis classifiedaffected/likely_affected.- The assurance promotion ladder —
affectedis machine-static (ledger class 3);/create-fuzzing(reproducing crash = E0) and live validation promote to execution evidence, with the static call path doubling as the attack-plan seed.
- "Not observed" never auto-dismisses. Static analysis misses reflection, plugins, dynamic dispatch, config-driven invocation. A reachability tag may at most lower a prior; false-positive determinations remain countersign-gated and execution-overridable.
- Never a finding of record. Reachability hits route attention; the finding list stays agent-authored, or the harness converges to commodity SAST.
- Provenance at emission time. Tool versions, DB freshness, SHAs, and tags are written into artifacts when produced — replays never re-derive them.
- Honest vocabulary.
not_observed/unknown, never "unreachable";test_onlyentry points labeled, never laundered into production reachability; Joern line numbers labeled approximate on every artifact.
Every engine is admitted or kept on the strength of a ground-truth corpus,
and the corpora live in the deployment's harness-QA trees (registered in the
corpus config with ownership: harness-qa, so they never enter metrics):
- Joern tiers — a validation corpus of human-audited direct calls, one
directory per target, under
analysis-results/scan-testing/. - Taint enumerator — its judge-pass precision measurement, under the same tree.
- govulncheck — the manual-approval-gate pilot artifact in the findings tree, whose symbol-reachable versus module-only split motivated the program.
Wiring a new engine follows the same pattern: a facts-only wrapper validated against a ground-truth corpus before any default use, with the measured promotion rate on a real advisory corpus as the admission criterion.