Is your lockfile hiding something?
Drop a package-lock.json, pnpm-lock.yaml, yarn.lock or bun.lock and see, in seconds, which exact versions are malware, vulnerable, too fresh to trust, pulled from npm, run install scripts, or carry copyleft licences. It runs in your browser, in your terminal and in CI.
No install, no account, and your lockfile never leaves the tab.
In March 2026 a hijacked release of axios, axios@1.14.1, shipped a payload through a new dependency, plain-crypto-js@4.2.1 (advisories MAL-2026-2307 and MAL-2026-2306). Both versions have since been removed from npm, but any project that installed them in that window still has them written into its lockfile. The same pattern runs through TanStack, Shai-Hulud, chalk/debug and dozens of smaller incidents.
The questions people then ask are simple, but most tools answer only part of them:
- Did we install a malicious version?
npm auditis npm-only, andpnpm auditstarted failing with HTTP 410 in April 2026 when npm retired its legacy audit endpoints (pnpm#11265). - Did we install anything brand new? Hijacked releases are often caught and pulled within days. Release cooldowns (
min-release-age,minimumReleaseAge,npmMinimalAgeGate) exist for this reason, but nothing tells you what in your current lockfile is inside that window. - Is anything in here no longer on npm? An unpublished version in your lockfile is a red flag.
lockspect answers all of them from the file you already have, for every major package manager, without installing anything.
| Check | What it means | Source |
|---|---|---|
| π₯ Malware | Version is listed as malicious (MAL-* advisories from OpenSSF, GitHub, Amazon Inspector and others) |
OSV.dev |
| π§ Vulnerable | Published GHSA / CVE advisories, with severity and the lowest version that fixes them | OSV.dev |
| πͺ Not on npm | Version is missing from the public registry: unpublished (often removed malware) or private | deps.dev + npm registry |
| π¨ Too fresh | Published inside your cooldown window (1, 3, 7, 14 or 30 days) | deps.dev + npm registry |
| π¦ Install scripts | Package runs code during install (where the lockfile records it) | lockfile |
| β¬ Deprecated | Maintainer-deprecated versions, with the reason | deps.dev |
| β¬ Strong copyleft | GPL / AGPL / SSPL and similar, with a licence breakdown for everything else | deps.dev |
| π© Provenance | Which versions have a verified npm provenance attestation | deps.dev |
It also generates the config to refuse fresh releases at install time for npm, pnpm, Yarn, Bun, Renovate and Dependabot, in the right unit for each (days, minutes or seconds).
Open https://feresbr1.github.io/lockspect/, then drop, choose or paste (Ctrl/β + V) your lockfile. Click a lockfile hit by the axios compromise to see a real detection.
Requires Node.js 18.17 or newer. Nothing else is installed: the CLI is a single bundled file.
npx github:feresbr1/lockspectOr download lockspect.mjs from the latest release and run node lockspect.mjs.
$ npx github:feresbr1/lockspect examples/axios-compromise/package-lock.json
package-lock.json Β· npm lockfile v3 Β· 178 packages
β 2 malicious packages found
malware 2
vulnerable 7 (3 critical, 13 high, 26 moderate, 3 low advisories)
not on npm 2
fresh (<7d) 27
install scripts 2
deprecated 0
strong copyleft 0
provenance 47/178
β axios@1.14.1 (direct) MALWARE Β· 34 high Β· unpublished β 1.20.0
MAL-2026-2307 Malicious code in axios (npm)
β¦
β plain-crypto-js@4.2.1 MALWARE Β· unpublished
MAL-2026-2306 Malicious code in plain-crypto-js (npm)
tinypool@1.1.1 2 critical β 2.1.2
β¦
2 packages matched --fail-on malware
(Counts for vulnerabilities and fresh releases change as new advisories and releases are published.)
lockspect [lockfile] [options]
-c, --cooldown <days> Flag versions younger than this (default 7)
-f, --fail-on <rules> Exit 1 when a package matches (default "malware")
malware, vuln (any), vuln:critical, vuln:high, vuln:moderate,
vuln:low, missing, fresh, install-script, deprecated, copyleft, none
--json Print the full report as JSON
--markdown Print a Markdown summary (for PR comments)
--markdown-file <p> Also append the Markdown summary to a file
-a, --all List every package, not only flagged ones
--allow-incomplete Exit 0/1 even if a data source could not be reached
--no-color Disable colours (NO_COLOR is also respected)
Exit codes: 0 nothing matched, 1 something matched --fail-on, 2 usage error, unreadable lockfile or incomplete scan.
# Block the build on malware or critical/high advisories
npx github:feresbr1/lockspect --fail-on malware,vuln:high
# Treat anything published in the last 3 days as a failure
npx github:feresbr1/lockspect pnpm-lock.yaml --cooldown 3 --fail-on malware,fresh
# Post a summary on a pull request
npx github:feresbr1/lockspect --markdown > lockspect.md
# Feed another tool
npx github:feresbr1/lockspect --json --fail-on none | jq '.packages[] | select(.flags | index("malware"))'# .github/workflows/lockspect.yml
name: lockspect
on: [pull_request, push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: feresbr1/lockspect@v1
with:
fail-on: malware,vuln:critical # default: malware
cooldown: 7 # days
# path: apps/web/pnpm-lock.yaml # default: first lockfile foundThe report is written to the job summary. This repository scans its own lockfile with the action on every push.
| Package manager | File | Versions | Direct deps | Dev flag | Install scripts |
|---|---|---|---|---|---|
| npm | package-lock.json, npm-shrinkwrap.json |
lockfileVersion 1, 2, 3 | v2+ | β | v2+ |
| pnpm | pnpm-lock.yaml |
5.x, 6.x, 9.x | β | β | 5.x, 6.x |
| Yarn classic | yarn.lock |
v1 | |||
| Yarn Berry | yarn.lock |
v2, v3, v4 (incl. patch:) |
β | ||
| Bun | bun.lock |
text lockfile | β | direct only |
npm aliases (npm:real@1.0.0) resolve to the real package. Workspace, git, file: and tarball-URL dependencies are listed as not checked. Bun's binary bun.lockb is not supported; run bun install --save-text-lockfile to create bun.lock.
- The lockfile is parsed locally, in the browser tab or in the CLI process. It is never uploaded.
- Only package names and versions are sent to the public OSV.dev and deps.dev APIs (both maintained by Google), plus
registry.npmjs.orgfor versions deps.dev has not indexed yet. - The website has no backend, cookies, analytics or accounts. It is a static page on GitHub Pages. The only thing stored is your cooldown and theme preference, in
localStorage.
lockfile ββparseβββΆ [name@version β¦] βββΆ OSV querybatch βββΆ advisories (MAL-*, GHSA, CVE)
βββΆ deps.dev versionbatch βββΆ publish date, licence, deprecation, provenance
βββΆ npm registry (only for versions deps.dev doesn't know) βββΆ unpublished? publish date
Everything lives in src/core: lockfile parsers, data sources and report building, with no Node or DOM dependencies. The web app (src/web, Preact + Vite) and the CLI (src/cli, bundled with esbuild into one file) are thin layers on top.
- Results are only as complete as OSV.dev and deps.dev. A brand-new compromise may not have an advisory yet. That is what the Too fresh check is for.
- Install-script data comes from the lockfile, so it is unavailable for Yarn, Bun and pnpm v9 lockfiles. pnpm 5/6
requiresBuildcan also mark packages that only need a build step. - Only the npm ecosystem is supported. PyPI, Cargo and Go lockfiles are not.
- Private packages from other registries show as Not on npm when their lockfile entry points at the public registry.
- Very large lockfiles (thousands of packages) look up at most 150 versions on the npm registry when deps.dev has no record of them. The report says so when this happens.
git clone https://github.com/feresbr1/lockspect && cd lockspect
npm install
npm run dev # web app at http://localhost:5173/lockspect/
npm test # unit + integration tests (mocked network)
LIVE=1 npm test # also hit the real OSV.dev / deps.dev / npm APIs
npm run test:e2e # Playwright, desktop + mobile (npx playwright install chromium first)
npm run check # format, lint, typecheck, tests, buildnpm run build writes the site to dist/web and the CLI to dist/cli/lockspect.mjs. npm run deploy publishes dist/web to the gh-pages branch that GitHub Pages serves. The CLI bundle is committed so that npx github: and the GitHub Action work without a build step, and CI fails if it is out of date.
See CONTRIBUTING.md for the details, and docs/PRODUCT_DECISIONS.md for why lockspect works the way it does.
- Live app: https://feresbr1.github.io/lockspect/
- Releases: https://github.com/feresbr1/lockspect/releases
- Issues and ideas: https://github.com/feresbr1/lockspect/issues
- Security policy: SECURITY.md
MIT. Advisory data Β© the respective OSV.dev sources; package metadata from deps.dev and the npm registry.