npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

niyantri

v0.1.0

Published

Dependency vulnerability scanner that ranks by real exploitability — reachability analysis, CISA KEV, EPSS, and CI gating for npm and PyPI projects

Readme

Niyantri

Multi-source dependency vulnerability scanner for npm and PyPI projects.

Merges findings from Trivy, npm audit / pip-audit, OSV, RetireJS and the GitHub Advisory Database onto one dependency graph, de-duplicates them, then ranks by what is actually exploitable in your project — not by raw CVSS.

What makes the ranking different

Every finding gets a 0–10 risk score:

risk = CVSS band + reachability + exposure + input surface + EPSS
       (floored to 9.5 if the CVE is in CISA's KEV catalog)
  • Reachability — is the package actually imported in your source? A CVE in a package nothing imports scores 0.2 instead of 2.5.
  • Dev-only — packages reachable only from devDependencies are not part of your deployed attack surface, even if a test file imports them.
  • KEV / EPSS — CISA's actively-exploited catalog and FIRST.org's 30-day exploitation probability, both cached per day.
  • Confidence — every finding reports whether its score rests on evidence (CVSS/EPSS/KEV) or on a regex over the advisory title. A 7.3 built from hard data and a 7.3 built from a keyword guess are not the same number.

Requirements

Node >= 16, plus two external binaries on PATH:

Optional, used when present: osv-scanner, retire, pip-audit.

Usage

niyantri test [project-path] [options]
# or
node scan.js [project-path] [options]

Output options

| Flag | Effect | |---|---| | --json-out <path> | Structured JSON for API consumers | | --sarif-out <path> | SARIF 2.1.0 for GitHub Code Scanning | | --show-all | Include not-imported and dev-only findings | | --risk-sort | Order all findings by risk score | | --severity=<level> | Only show findings at or above this severity |

CI gating

| Flag | Effect | |---|---| | --fail-on=<spec> | Exit 1 when findings match the spec | | --baseline [path] | Compare against a baseline; marks findings as new | | --write-baseline [path] | Record current findings as the accepted set | | --no-ignore-file | Skip .niyantriignore.json | | --strict-sources | Exit 2 if any data source failed (scan incomplete) |

--fail-on spec — comma-separated, OR'd together:

| Token | Meaning | |---|---| | critical high medium low | Severity at or above | | risk:N | Risk score >= N | | kev | Any CISA-KEV finding | | any | Any finding at all | | new | Modifier — restrict to findings absent from the baseline |

niyantri test . --fail-on=high            # fail on any high or critical
niyantri test . --fail-on=risk:7.5        # fail on genuinely exploitable issues
niyantri test . --fail-on=kev             # fail only on active exploitation
niyantri test . --baseline --fail-on=new  # fail only on newly introduced issues

--fail-on=risk:N is usually the better gate. It already accounts for reachability, so an unreachable CRITICAL will pass while a reachable MEDIUM in a request-handling path will fail.

Exit codes

| Code | Meaning | |---|---| | 0 | Clean, or nothing matched --fail-on | | 1 | Findings matched --fail-on | | 2 | Scan could not run (missing tool, bad path, bad flag) |

Without --fail-on the scan always exits 0.

Data source health

A scanner that loses a source and says nothing is worse than one with fewer sources, because a degraded scan looks identical to a clean project. Every run reports what each source actually did:

  Data sources  4 of 7 sources active — 1 FAILED
    ✓ Trivy              6 findings
    ✓ npm audit          4 findings
    – pip-audit          not a Python project
    ✓ RetireJS           0 findings
    – OSV Scanner        osv-scanner not on PATH
    ✗ GitHub Advisory    HTTP 401 Bad credentials — GITHUB_TOKEN expired
    ✓ OSV API            14 findings
  ⚠ A failed source means findings may be MISSING.

The distinction is the point:

| Mark | Meaning | |---|---| | ✓ | Ran and produced usable output (0 findings is legitimate) | | – | Skipped — prerequisite genuinely absent. Expected. | | ✗ | Failed — configured but errored. Findings may be missing. |

Use --strict-sources in CI to exit 2 when any source failed, so an incomplete scan can never be mistaken for a passing one. The same data is in --json-out under source_health; consumers should check degraded before treating a low finding count as clean.

Adopting on an existing codebase

Nobody triages an inherited backlog. Record it once, then gate only on what's new:

niyantri test . --write-baseline      # writes .niyantri-baseline.json — commit it
niyantri test . --baseline --fail-on=new

Findings that disappear are reported as resolved. On a first run with no baseline, nothing is marked new — so a --fail-on=new build won't fail on the backlog it just inherited.

Suppressing accepted findings

.niyantriignore.json in the project root:

{
  "ignore": [
    {
      "id": "CVE-2026-4800",
      "package": "lodash",
      "reason": "only reached by the build script, not shipped",
      "expires": "2026-12-31"
    }
  ]
}

id is required; package, reason and expires are optional.

Expiries matter. When a rule lapses, it stops applying and the finding comes back — which forces someone to re-make the decision instead of the suppression quietly becoming permanent blindness. Rules expiring within 14 days are flagged in the summary.

Forcing a fix for transitive dependencies

When a vulnerable transitive has no parent release that upgrades it, the scan prints a paste-ready block instead of a dead end:

FORCE A FIX  ·  2 packages with no parent upgrade path

{
  "overrides": {
    "lodash": "^4.18.0",
    "qs": "^6.15.2"
  }
}

Add it to package.json (yarn users: resolutions, with a **/ prefix). When several advisories affect one package the highest fixed version is chosen, so a single pin closes all of them. Verify nothing breaks — an override diverges from what the parent package declared.

GitHub Actions

name: Dependency scan
on: [pull_request]

permissions:
  contents: read
  security-events: write   # required to upload SARIF

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }

      - name: Install scanner prerequisites
        run: |
          npm install -g @cyclonedx/cdxgen
          curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
            | sh -s -- -b /usr/local/bin

      - run: npm ci

      - name: Scan
        run: node scan.js . --sarif-out results.sarif --baseline --fail-on=new

      - name: Upload to code scanning
        if: always()          # upload findings even when the gate fails
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

Findings then appear inline on the PR diff and in the Security tab. GitHub ranks them by the risk score, so the reachability discount carries through.

Development

npm test          # unit tests (node:test, no dependencies)
npm run test:watch

lib/ holds the extracted, independently testable pieces of the pipeline — scoring, reachability, intel feeds, policy, baseline, SARIF. Each is pure or takes its I/O by injection; see the header comment in each file.

Server mode

npm run serve

Accepts a zipped project, runs a scan, streams output back and persists results to MongoDB for the dashboard. Requires MONGODB_URI, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD and HMAC_SECRET in the environment.

Note: the server runs scanner tooling over uploaded archives. Run it in a sandboxed container, not alongside anything else you care about.