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

pkgradar

v0.1.4

Published

Content-based supply-chain scanner for npm/pnpm/yarn/bun: inspects the bytes you actually installed (lifecycle hooks, obfuscated payloads, worm IOCs) instead of just matching package names against an advisory list.

Readme

pkgradar

A supply-chain scanner that reads the bytes you actually installed, not just the package names.

npx pkgradar

That is the whole thing. One command, no install, no config. It scans your project and every package cache on the machine, and tells you whether anything looks like malware.

Why this exists

A lot of "supply-chain" CLIs work like this: download a list of advisories, check whether any package name in your project appears on it, print a scary verdict. So you run one, and it tells you something like:

Verdict: potential supply-chain exposure - 3 package hits
  [email protected]
  [email protected]

...even though zod-to-json-schema is a perfectly normal package downloaded millions of times a week, and the version you have was never touched. The advisory just mentioned that package's ecosystem. Name matching cannot tell the difference between "this package was trojanized" and "this package shares a name with something in a writeup".

pkgradar does the opposite. It opens the files. For every package it finds on disk it checks:

  • Install hooks that detonate. preinstall / install / postinstall scripts, scored by whether they spawn processes, pipe curl | bash, read ~/.npmrc / ~/.ssh / ~/.aws/credentials, or reference NPM_TOKEN / GITHUB_TOKEN / cloud credentials. (prepare is ignored on purpose: it does not run when a package is installed as a dependency.)
  • Known worm artifacts. Files like setup_bun.sh, bun_environment.js, migrate-repos.sh, hard-coded Shai-Hulud exfiltration endpoints, and the rest of the Shai-Hulud / "Mini Shai-Hulud" indicator set. A bundled GitHub Actions workflow that runs a piped shell download or exfiltrates secrets is flagged too.
  • Obfuscated or packed payloads. The javascript-obfuscator _0x... fingerprint, eval / new Function over atob / Buffer.from(base64), child_process fed from decoded data, large base64 blobs. The "this file is just minified" signals are deliberately demoted so a normal bundled CLI does not drown the report.
  • Manifest oddities. bin entries pointing outside the package or at a shell script.
  • Known advisories, precisely (optional). With --online it cross-references OSV.dev by exact (name, version), so you get real GHSA / CVE IDs instead of name collisions, and each advisory is reported at its own severity, so a moderate ReDoS in a dev tool does not shout as loud as an RCE.

A small curated allowlist (data/allowlist.json) downgrades benign-but-trippy findings on famous packages (vercel, corepack, core-js, esbuild, and so on) to INFO. It never suppresses a hard worm indicator, because "a trusted package suddenly ships a worm" is exactly the attack worth catching.

How it works

                            npx pkgradar
                                 |
                                 v
   +-----------------------------------------------------------+
   |  1. DISCOVER STORES                                        |
   |     where package code is extracted on this machine        |
   |                                                            |
   |   project node_modules   pnpm in-project store (.pnpm)     |
   |   npm global root        npx ephemeral cache (~/.npm/_npx) |
   |   bun cache              yarn v1 cache    yarn berry zips  |
   |   pnpm global store                                        |
   +-----------------------------------------------------------+
                                 |
                                 v
   +-----------------------------------------------------------+
   |  2. WALK EACH STORE                                        |
   |     yield every package directory found                    |
   |     dedupe by  name @ version @ path                       |
   +-----------------------------------------------------------+
                                 |
                                 v
   +-----------------------------------------------------------+
   |  3. INSPECT THE BYTES   (per package, budgeted)            |
   |                                                            |
   |   +-------------------+   reads package.json scripts       |
   |   | lifecycle-hook    |   pre/install/postinstall          |
   |   +-------------------+   scored by what they touch        |
   |                                                            |
   |   +-------------------+   walks the package tree           |
   |   | worm-ioc-file     |   matches known artifact names     |
   |   | embedded-workflow |   inspects bundled CI workflows    |
   |   +-------------------+                                    |
   |                                                            |
   |   +-------------------+   reads .js files (<= 1.5MB head    |
   |   | suspicious-js     |   + 96KB tail, <= 24MB per pkg)    |
   |   +-------------------+   obfuscation / eval / exfil combo  |
   |                                                            |
   |   +-------------------+   reads package.json bin           |
   |   | odd-bin           |                                    |
   |   +-------------------+                                    |
   +-----------------------------------------------------------+
                                 |
                  +--------------+--------------+
                  |                             |
            (--online only)                     |
                  v                             |
   +---------------------------+                 |
   | 4. OSV.dev cross-check    |                 |
   |    querybatch -> which    |                 |
   |    pkgs have advisories   |                 |
   |    then /v1/query each    |                 |
   |    -> real severity       |                 |
   +---------------------------+                 |
                  |                             |
                  +--------------+--------------+
                                 |
                                 v
   +-----------------------------------------------------------+
   |  5. ALLOWLIST PASS                                         |
   |     downgrade benign findings on famous packages to INFO   |
   |     (never a hard worm indicator)                          |
   +-----------------------------------------------------------+
                                 |
                                 v
   +-----------------------------------------------------------+
   |  6. REPORT                                                 |
   |     verdict line, findings grouped by severity,            |
   |     coloured badges, evidence paths, what-to-do            |
   |     exit 0 clean / 1 findings / 2 error                    |
   +-----------------------------------------------------------+

A finding object looks like this:

+------------------------------------------------------+
| package    name of the package                       |
| version    version on disk                           |
| store      which store it was found in               |
| code       lifecycle-hook | worm-ioc-file |          |
|            suspicious-js | odd-bin | osv-advisory ... |
| severity   CRITICAL | HIGH | MEDIUM | LOW | INFO      |
| message    what tripped, in plain words              |
| evidence   the file path or script line that did it  |
| dir        full path to the package on disk          |
+------------------------------------------------------+

Usage

npx pkgradar                 # scan the current project plus every cache on this machine
npx pkgradar --online        # also cross-check OSV.dev advisories by exact version
npx pkgradar --min-sev high  # only show HIGH and CRITICAL (good for CI)
npx pkgradar --json          # machine-readable output

Exit codes: 0 clean, 1 findings at or above --min-sev, 2 scanner error. In CI: npx pkgradar --min-sev high.

| flag | meaning | |---|---| | --online | also query OSV.dev for known advisories (needs network) | | --json | machine-readable output | | --min-sev LEVEL | critical, high, medium, low, info (default medium) | | --stores LIST | limit to project,pnpm-project,npm-global,npx-cache,bun-cache,yarn-cache,yarn-berry | | --no-allowlist | do not downgrade findings on well-known packages | | --max-depth N | max node_modules nesting depth (default 12) | | --cwd DIR | project directory to scan (default .) |

What it is not

These are heuristics. They surface candidates, not verdicts. A clean run means "nothing tripped the checks on the bytes that are present", not "this is proven safe". A finding on a big bundled CLI is often a false positive, which is what the allowlist is for. A finding with a worm indicator string or a curl | bash postinstall is not a false positive. Read the evidence line, then decide.

Current coverage gaps, on the roadmap: yarn-berry packages are matched by name only (the zip archives are not opened yet), the pnpm global store and the npm _cacache tarballs are not extracted and scanned, and there is no npm-provenance / attestation check or typosquat-distance check yet.

pkgradar reads files only. It never executes any package code, install script, or workflow. Zero runtime dependencies, Node 18 or newer, built-ins only, which feels right for a tool you are meant to trust about your dependencies.

License

MIT