Reachability internals
The tree-sitter binding, how release binaries are built with CGo, how the engine runs a query, and where the code lives.
For people packaging the CLI or checking how a verdict was reached.
Code map
| Path | Role |
|---|---|
cmd/vdb.go, cmd/vdb_reachability.go |
--reachability on vdb; query fetch, scan, memory write, x_reachability |
cmd/cli_sca.go |
Reachability for scan findings, the semantic fallback, verdict labels |
internal/treesitter/languages.go |
Language registry: ID to grammar, extension to ID, aliases |
internal/reachability/engine.go |
Parser pool and query execution |
internal/reachability/scanner.go |
File walk and parallel analysis |
internal/reachability/discovery.go |
Install directory lookup and skipped directories |
internal/reachability/symbol_match.go |
Name-matching fallback when no query exists |
internal/memory/ |
reachability_evidence in memory.yaml |
Binding and build
The CLI uses github.com/smacker/go-tree-sitter, which ships each grammar’s C source as a Go package. Lua comes from a vendored grammar under third_party/treesitter-lua. Release builds use CGO_ENABLED=1; grammars are linked statically, so the binary needs no tree-sitter or toolchain at run time.
| Target | Built with |
|---|---|
linux/amd64, linux/arm64, linux/arm, linux/386 |
zig cc for musl |
windows/amd64, windows/arm64 |
zig cc for windows-gnu |
darwin/amd64, darwin/arm64 |
native clang on a macOS runner |
How one query runs
- A parser for the language comes from a per-language pool.
- The file is parsed. A failed or cancelled parse closes the parser instead of returning it to the pool, because a half-finished parse would resume on the next file with the wrong byte offsets. A tree whose root spans more bytes than the source is rejected.
- The S-expression is compiled against the grammar. A compile error skips that query for that file.
- A query cursor runs it and
#eq?,#match?and#any-of?predicates are applied. A#match?regex that panics while compiling is recovered and reported as an error for that query only. - Each surviving match becomes
start_linetoend_line: the first and last line any capture touches, 1-indexed.
How a scan runs
- Queries are grouped by language and deduplicated by query hash, so a query shared by several CVEs runs once.
- Direct mode: the package’s install directory is found and walked.
- Transitive mode: the project root is walked with the install directory and the skipped directories pruned.
- The walk collects files first, in a fixed order, then analyses them in parallel (
VULNETIX_REACHABILITY_CONCURRENCY, defaultGOMAXPROCS, 1 to 32). Files over 4 MiB are skipped. - CVEs still without a verdict and with affected routine, file or module names from the VDB go through the semantic fallback, which builds one literal regex per file extension and walks the same tree.
Parse and permission errors are not fatal; the run continues with the other files.