reachable

Dependency triage with source awareness

Find the vulnerable code paths that matter, not just the packages that happen to be installed.

REACHABLE UNKNOWN UNREACHABLE

reachable parses JavaScript and TypeScript, builds a project call graph, looks up advisories, and shows the call path when your entry points reach a vulnerable function. When it finds no path, that is a best-effort static result: lower priority, never proof of safety.

Verdicts

Cut false positives without pretending static analysis is magic.

reachable is static analysis of a dynamic language, and it makes the uncertainty visible. REACHABLE comes with a call path as evidence: a call to the function an advisory names, on an import path from your entry points. UNREACHABLE is a best-effort static result: no path from the entry points to the functions the advisory names, and none of the dynamic uses reachable recognizes (the module handed on whole, a member read through a computed name on the package or on a value made from it, the package's name in a string, a module path or a config file, the function's name in a file it searches, another installed package that declares the package or names it in its code). It is a triage signal, lower priority, never proof of safety: code that picks a function or module name at runtime, or a dependency that loads a package by a name it computes, can reach a function unseen. Anything else is UNKNOWN, with the reason, instead of a bluff.

REACHABLE

A call to the vulnerable function is on an import path from your entry points, and the path is shown. This is the one you fail CI on first.

UNKNOWN

A dependency may call it, the advisory does not name the function, or resolution hit a dynamic pattern. Each one says why. It is a triage queue, not a hidden error.

UNREACHABLE

A best-effort static result: no call path to the functions the advisory names, and none of the dynamic uses reachable recognizes. Lower priority, never proof of safety. Known blind spots: a flaw in an internal helper other functions share (lodash's _.pullAt reaches baseUnset, while the advisories name unset and omit); a function or package name computed at runtime from parts the project never spells out; a dependency that builds a package's name itself; a wrapper indexed after it leaves the function that made it; and code in components and templates, which are only searched as text. All of them.

Why reachable

Package-level alerts are cheap. Source-aware triage is the part teams actually need.

`npm audit` and hosted scanners are useful for visibility, but they do not know whether your app imports or calls the vulnerable code. reachable fills that gap with a local-first CLI you can run on demand or in CI.

reachable

call graph + advisory mapping

Ranks advisories by whether a call path from your entry points reaches the vulnerable function.

package-only scanners

dependency list only

Good at inventory. Bad at telling you whether the risky code path touches your app.

Install

Run it locally first. Wire it into CI when the verdicts feel trustworthy.

reachable is designed to slot into a normal JavaScript or TypeScript workflow with a short install path and a direct CLI surface.

npm install -g @merupatel/reachable

reachable scan --format table
reachable trace lodash
reachable graph src/index.ts
Use it for:

- reachable-only CI gates
- lockfile triage
- advisory investigation
- local audit workflows