Usage

This page documents every way to invoke reachability and every output it produces.

The --reachability flag

Registered as a persistent flag on vdb, so every subcommand that produces vulnerability data inherits it.

--reachability {direct|transitive|both|off}     (default: both)
ValueDirect scanTransitive scanFetches queries
directYes
transitiveYes
both (default)Yes
offNo — no API request, no scan, no output block

The off value is the only setting that skips the GET /vuln/{id}/tree-sitter call. Use it when:

  • The CLI is running in a sandbox without filesystem access to dependencies.
  • You want a strictly offline vdb vuln lookup.
  • A CI job has already produced reachability evidence and you only want to render the rest of the vuln detail in a different stage.

Aliases accepted on input: off / none / false / 0 all map to off. The empty string maps to both (the default).

Performance tuning

Direct mode walks the install folder of a single package — even on a deep monorepo this completes in well under a second.

Transitive mode walks the entire project root excluding standard build dirs. On a ~10k-file Node monorepo it typically completes in ~2–5 seconds; on a ~200k-file mixed-language monorepo it can take 30+ seconds. If you need to gate CI on reachability and the transitive sweep is too slow, run with --reachability=direct in CI and re-run with both locally during triage.

The scanner caps any individual file at 4 MiB (reachability.MaxFileSize) and silently skips larger files. This keeps memory bounded on minified bundles and generated source.


Commands that produce reachability

vulnetix vdb vuln <id>

The primary user-facing command. Always runs reachability when the mode is non-off, the API version is v2 (the default), and queries exist for the named identifier.

# Default: direct + transitive
vulnetix vdb vuln CVE-2021-23337

# JSON output for automation
vulnetix vdb vuln CVE-2021-23337 --output json

# Skip transitive (faster for CI)
vulnetix vdb vuln CVE-2021-23337 --reachability=direct

# Pure offline lookup
vulnetix vdb vuln CVE-2021-23337 --reachability=off

vulnetix vdb remediation plan <id>

The remediation plan endpoint surfaces fix candidates. Reachability evidence helps prioritise between them (a CVE with no reachable code paths might warrant a deferred upgrade rather than an emergency patch). When run with v2, reachability is fetched and rendered on the same flow as vdb vuln.

vulnetix scan

The repo-wide scan flow produces a stream of findings rather than a single-vuln view. Reachability fans out across the produced CVEs — each finding gets its own direct + transitive scan recorded into .vulnetix/memory.yaml. Disable globally with --reachability=off when scanning large monorepos where you don’t need per-CVE source-evidence.


Pretty output

The reachability block renders below the standard vuln detail. Example annotated:

Reachability: 2 direct, 1 transitive (1 queries)
│  └ Per-mode counts                  └ Number of distinct queries executed
  direct       node_modules/lodash/template.js  142:158  (prototype-pollution)
  │            │                                │       └ Query name (TreeSitterQuery.name)
  │            │                                └ start_line:end_line, 1-indexed inclusive
  │            └ Path relative to project root
  └ Match mode (direct = inside install dir; transitive = elsewhere)
  direct       node_modules/lodash/template.js  201:204  (prototype-pollution)
  transitive   src/render.ts                    88:97    (prototype-pollution)

When a mode was requested but couldn’t run, a muted “skipped” line replaces the matches:

Reachability: 0 direct, 1 transitive (1 queries)
  transitive   src/render.ts  88:97  (prototype-pollution)
  direct skipped: install directory for npm/lodash not found

JSON output

Reachability is attached to the response as x_reachability. The CLI uses an x_-prefixed key because the vuln payload is the CVE 5.0 JSON shape; x_ extensions are the documented way to attach non-CVE-spec data.

{
  "cveMetadata": { "cveId": "CVE-2021-23337", ... },
  "containers": { ... },
  "x_reachability": {
    "queries_run": 1,
    "direct": [
      {
        "file": "node_modules/lodash/template.js",
        "start_line": 142,
        "end_line": 158,
        "query": "prototype-pollution",
        "language": "javascript",
        "captures": {
          "callee": "_.template",
          "arg": "userInput"
        }
      },
      {
        "file": "node_modules/lodash/template.js",
        "start_line": 201,
        "end_line": 204,
        "query": "prototype-pollution",
        "language": "javascript"
      }
    ],
    "transitive": [
      {
        "file": "src/render.ts",
        "start_line": 88,
        "end_line": 97,
        "query": "prototype-pollution",
        "language": "javascript"
      }
    ]
  }
}

Field reference

FieldTypeNotes
queries_runintDistinct (language, queryHash) pairs executed. Always non-zero when the block is present.
directMatch[]Matches inside the installed-package folder. Empty/omitted if mode skipped direct.
transitiveMatch[]Matches elsewhere in the project tree. Empty/omitted if mode skipped transitive.
skipped_directstringSet when direct was requested but couldn’t run (e.g. install folder not found).
skipped_transitivestringSet when transitive was requested but couldn’t run (rare; no project root).

Match object

FieldTypeNotes
filestringPath relative to the project root (or absolute if outside it).
start_lineint1-indexed, inclusive. The earliest source line touched by any capture.
end_lineint1-indexed, inclusive. The latest source line touched by any capture.
querystringThe TreeSitterQuery.name field for the query that matched.
languagestringCanonical language ID — see Languages.
capturesobjectMap of capture name → matched source text. Truncated by tree-sitter to the captured node’s literal source.

.vulnetix/memory.yaml integration

Every reachability run appends evidence to the corresponding finding under threat_model.reachability_evidence. Existing memory.yaml files without this field continue to load (it’s omitempty on the Go struct).

findings:
  CVE-2021-23337:
    package: lodash
    ecosystem: npm
    status: under_investigation
    severity: HIGH
    threat_model:
      attack_vector: Network
      attack_complexity: Low
      reachability: Network         # CVSS-derived attack-vector reachability
      reachability_evidence:        # tree-sitter-derived code reachability
        direct:
          - file: node_modules/lodash/template.js
            range: "142:158"
            query: prototype-pollution
          - file: node_modules/lodash/template.js
            range: "201:204"
            query: prototype-pollution
        transitive:
          - file: src/render.ts
            range: "88:97"
            query: prototype-pollution
    history:
      - date: 2026-05-17T09:14:32Z
        event: vdb-lookup
        detail: Queried VDB for vulnerability details
      - date: 2026-05-17T09:14:34Z
        event: reachability-scan
        detail: "2 direct, 1 transitive"

How downstream tooling consumes it

ConsumerWhat it readsWhy
vdb vex publishreachability_evidence + decision.choiceGenerates OpenVEX/CycloneDX VEX attestations citing specific file:line evidence for not_affected claims.
vulnetix:dashboard skillreachability_evidence countsSurfaces “vulns with reachable code” as a priority tier above “vulns with no reachable code”.
Compliance bundle (vdb compliance)Full reachability blockEmbeds match locations into the audit deliverable so reviewers can verify the triage rationale.
Triage agentsreachability_evidenceAuto-suggest not_affected when both direct and transitive arrays are empty for a CVE with reliable queries.

Range syntax

The range field is rendered as start_line:end_line using n:n for single-line matches — matching the rest of the CLI’s location format. Tools consuming the file can split on : to get the two integers.

Clearing stale evidence

Re-running reachability overwrites the reachability_evidence for the same CVE — it never appends or merges with a previous run, so evidence always reflects the most recent scan. To wipe reachability without re-scanning, edit memory.yaml directly or use the dashboard skill.