Skip to content
Back to skills

Reachability Analyzer

ASecurity

"Runs dead-dependency analysis across JS, Python, and Rust projects

  • 10 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 12, 2026
testingpythonrustgobashnextjsgitsecurity

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 12, 2026

npx -y skills add testland/qa --skill reachability-analyzer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reachability Analyzer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reachability Analyzer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/testland-reachability-analyzer/badge)](https://www.skillsdirectory.com/skills/testland-reachability-analyzer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: reachability-analyzer
description: "Runs dead-dependency analysis across JS, Python, and Rust projects
  using ecosystem-native static tools (`depcheck`/`knip` for JS, `vulture` for
  Python, `cargo-machete` for Rust), then cross-references the unused-dependency
  list against SCA findings to downrank vulns in code that is never loaded.
  Use when SCA output (from `osv-scanner`, `snyk-test`, or `npm-pip-maven-audit`)
  is too noisy to triage and the team needs to separate unreachable CVEs from
  exploitable ones before sprint planning; sibling cve-exploitability-triage ranks by EPSS/KEV exploitation signal, not code reachability."
metadata:
  keywords: "reachability, dead-dependencies, sca, depcheck, knip, vulture, cargo-machete, prioritization"
  version: "1.2.8"
---

# reachability-analyzer

## Overview

Static vulnerability scanners report a CVE for every declared dependency that
matches a vulnerable version, regardless of whether the vulnerable code is ever
executed. A critical-severity finding in a test-only helper that your production
build tree never reaches is a different risk level than the same CVE in a
hot-path dependency. This skill operationalizes a reachability heuristic:
run an ecosystem-native dead-dependency tool, produce an `unused-deps.txt`
artifact, and feed that list into your SCA prioritization step so it can
assign `reachable: false` and route affected findings to Fix-Backlog instead
of Fix-Now.

The approach is static-analysis heuristic only. A dependency flagged unused by
these tools is a strong signal to deprioritize its CVEs, not a guarantee that
the vulnerable code is unreachable at runtime. Treat it accordingly.

## When to use

- After any SCA scan produces more findings than the team can triage in one
  sprint and you need a principled filter.
- Before feeding findings into SCA prioritization so the `reachable` field is
  populated.
- During dependency cleanup: the same tooling surfaces dead weight (unused
  packages that add attack surface and install time with no benefit).
- When a security reviewer asks "is this CVE in something we actually use?"

## How to use

1. Run SCA first and save the findings JSON - this skill annotates existing
   findings, it does not replace them. See `osv-scanner` (or `snyk-test` /
   `npm-pip-maven-audit`) for full setup:

   ```bash
   osv-scanner scan -r . --format json --output osv.json
   ```

2. Run the ecosystem-native dead-dependency tool for your stack, then extract
   the unused names into `unused-deps.txt`. Full config, flags, exit codes, and
   suppression per tool:
   [references/dead-dependency-tools.md](references/dead-dependency-tools.md):

   ```bash
   # JS/TS - knip (preferred; depcheck was archived June 2025)
   npx knip --reporter json | jq -r '.dependencies[].name' > unused-deps.txt
   # Python - vulture; --min-confidence 90 targets unused imports
   vulture src/ --min-confidence 90 > vulture-report.txt
   # Rust - cargo-machete; --with-metadata catches renamed/feature-gated crates
   cargo machete 2>&1 | grep "unused dependency" | awk '{print $NF}' > unused-deps.txt
   ```

3. Keep production and dev unused-dep lists separate: dev deps with CVEs can
   still reach production in bundled builds.

4. Cross-reference `unused-deps.txt` against the SCA JSON, setting
   `reachable: false` on every finding whose package is unused (script below).

5. Verify: assert `unused-deps.txt` is non-empty and every finding in the
   annotated JSON carries a populated `reachable` field before handoff. If the
   file is empty or a finding lacks the field, re-run the dead-dependency tool
   (a silent tool failure or the wrong workspace root is the usual cause) and
   re-annotate before proceeding.

6. Pass the annotated JSON to your SCA prioritization step; `reachable: false`
   routes to Fix-Backlog unless the CVE is in the CISA KEV catalog.

7. Wire steps 2-4 into CI so the annotated artifact feeds the prioritization job
   on every run, and still schedule each unused package for cleanup.

## Cross-reference unused deps with SCA findings

Once you have an `unused-deps.txt` for your ecosystem, annotate the SCA JSON
output before passing it to your SCA prioritization step:

```python
import json

with open("osv.json") as f:
    findings = json.load(f)

with open("unused-deps.txt") as f:
    unused = {line.strip() for line in f}

for finding in findings.get("results", []):
    pkg = finding.get("package", {}).get("name", "")
    finding["reachable"] = pkg not in unused

with open("osv-annotated.json", "w") as f:
    json.dump(findings, f, indent=2)
```

Pass `osv-annotated.json` to your SCA prioritization step. Per the priority
logic, findings with `reachable: false` route to Fix-Backlog regardless of
severity, unless they are in the CISA KEV catalog.

## CI integration

```yaml
jobs:
  reachability:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: JS unused deps (knip)
        run: npx knip --reporter json | jq -r '.dependencies[].name' > unused-deps.txt
      - name: Annotate OSV output
        run: python3 ci/annotate-reachability.py osv.json unused-deps.txt
      - uses: actions/upload-artifact@v4
        with:
          name: osv-annotated
          path: osv-annotated.json
```

The annotated artifact is then consumed by the prioritization job.

## Worked example

A team's OSV scan of a Next.js service returns 60 CVEs - too many to patch in one
sprint. They run the JS dead-dependency tool (step 2), which flags 12 of the 80
declared dependencies as unused, including `moment` (CVE-2022-31129, CVSS 7.5). The cross-reference script sets
`reachable: false` on the moment finding because `moment` appears in
`unused-deps.txt`. Prioritization routes that CVE to Fix-Backlog instead of
Fix-This-Sprint, and the team removes `moment` in the next dependency-cleanup PR,
eliminating both the CVE and the unused weight in one change.

## Anti-patterns

| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Skip reachability; sort SCA output by CVSS only | Teams spend sprints patching vulns in unused code while exploitable issues sit in Fix-Backlog | Run the cross-reference step before triage |
| Treat `reachable: false` as safe | Static heuristic; dynamic imports and plugin systems can load code at runtime | Use as deprioritization signal only; still schedule Fix-Backlog cleanup |
| Run vulture at default confidence (60%) | Reports unused functions/variables alongside imports; noisy for CVE mapping | Use `--min-confidence 90` to target imports specifically |
| Use depcheck on new JS projects | Archived June 2025; per [github.com/depcheck/depcheck](https://github.com/depcheck/depcheck), maintainers recommend knip | Switch to knip for new setups |
| Skip `--with-metadata` in Rust monorepos | Renamed crates not detected; false "used" verdicts | Always pass `--with-metadata` in CI |
| Ignore devDependencies entirely | Dev deps with CVEs can reach production in bundled builds | Separate prod/dev lists; confirm `--omit=dev` in audit scope |

## Limitations

- All three tools use static analysis only. Dynamic imports (`require(variable)`
  in JS, `importlib.import_module` in Python) and compile-time proc macros in
  Rust can defeat detection, producing false "unused" verdicts.
- Import-to-package mapping for Python requires manual maintenance when import
  names differ from PyPI package names.
- Monorepo setups may require per-workspace runs; a dependency unused in one
  package may be used in another.
- cargo-machete per
  [github.com/bnjbvr/cargo-machete](https://github.com/bnjbvr/cargo-machete)
  is described as "fast yet imprecise" - the trade-off is speed over recall.
- These tools detect unused declarations; they do not trace call graphs to the
  vulnerable function within a used package. For function-level reachability,
  runtime instrumentation (e.g. Snyk's reachability analysis, CodeTF-based
  call-graph tools) is required.

## References

- [references/dead-dependency-tools.md](references/dead-dependency-tools.md) -
  per-ecosystem tool detail: knip/depcheck, vulture, cargo-machete config, flags,
  exit codes, suppression
- [github.com/webpro-nl/knip](https://github.com/webpro-nl/knip) - knip unused
  files/exports/dependencies detection
- [github.com/depcheck/depcheck](https://github.com/depcheck/depcheck) -
  depcheck CLI reference (archived June 2025; knip recommended for new setups)
- [github.com/jendrikseipp/vulture](https://github.com/jendrikseipp/vulture) -
  vulture CLI reference, confidence values, whitelist generation
- [github.com/bnjbvr/cargo-machete](https://github.com/bnjbvr/cargo-machete) -
  cargo-machete CLI reference, exit codes, Cargo.toml metadata
- `osv-scanner`,
  `snyk-test`,
  `npm-pip-maven-audit` - SCA tools whose
  output this skill annotates

Files in this skill

  • .tessl-plugin/plugin.json726 B
  • SKILL.md8.6 KB
  • references/dead-dependency-tools.md5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.