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

package-tripwire

v1.1.1

Published

Fail CI when dependency changes look suspicious: new install scripts, publisher/maintainer changes, freshly published versions, typosquats, deprecated packages, integrity swaps and git/URL deps. Diffs package-lock, pnpm and yarn lockfiles against a git re

Readme

package-tripwire

CI npm version license: MIT Node

Fail CI when dependency changes look suspicious.

package-tripwire diffs your lockfile against a git ref (e.g. the PR base branch) or a saved baseline, looks up every new or changed package in the npm registry, and fails the build when something looks like a supply-chain attack. It works as a CLI, a library and a GitHub Action, and has zero runtime dependencies.

Supported lockfiles: package-lock.json / npm-shrinkwrap.json (v1–v3), pnpm-lock.yaml (v5–v9), yarn.lock (classic v1 and Berry).

Tests live under test/ and run in GitHub Actions on Node 18, 20 and 22 (including ESM import and CommonJS require against the built bundle).

What it flags

| Rule | Default | What it catches | |---|---|---| | install-script | error | New preinstall / install / postinstall scripts (or implicit node-gyp), or scripts that changed since the previous version | | disallowed-install-script | error | Package runs install scripts but is not listed in allowScripts when enforcement is on | | integrity-change | error | Same name@version with a different integrity hash or tarball source, or a lockfile hash that doesn't match the registry | | registry-signature-invalid | error | Registry tarball ECDSA signature does not verify (same rules as npm audit signatures) | | unsigned-package | warn | Registry metadata has no tarball signature while the registry publishes signing keys | | provenance-lost | error | npm provenance attestation was present on the previous version but missing on the new one | | provenance-missing | note | Resolved version has no provenance attestation (--require-provenance promotes to a failure) | | disallowed-host / disallowed-scheme / name-url-mismatch | error | lockfile-lint style tarball URL policy (opt-in via lockfileLint) | | typosquat | error | New names close to popular packages (edit distance, -/_/. confusion, homoglyphs like rn→m, 0→o, scoped copies, -js/node- affixes) | | publisher-change | error | The new version was published by a different npm user than the previous one | | recent-publish | error | The resolved version was published less than --min-age ago (default 72h) | | non-registry-source | warn | Git, GitHub, HTTP tarball or file: dependencies | | maintainer-change | warn | The maintainer list changed between versions | | deprecated | warn | The resolved version is deprecated | | unknown-package / registry-error | warn | Not in the registry, or the lookup failed | | new-dependency | note | Every newly added package (direct or transitive) |

Diff rules only query the registry for added/changed packages, so runs stay fast. When signature verification is enabled, every registry package in the lockfile is checked against /-/npm/v1/keys. --offline skips the registry and keeps lockfile-only checks (integrity, typosquat, git/URL, hasInstallScript, optional lockfileLint).

Install

npm install --save-dev package-tripwire
# or run it once
npx package-tripwire --base origin/main

Requires Node.js 18 or later.

CLI

package-tripwire                          # compare working-tree lockfile against HEAD
package-tripwire --base origin/main       # compare against a branch / tag / commit
package-tripwire --save-baseline .tripwire/baseline.json   # snapshot the current lockfile
package-tripwire --baseline .tripwire/baseline.json         # compare against the snapshot
package-tripwire --min-age 7d --fail-on warn --sarif tripwire.sarif --json tripwire.json

| Option | Description | |---|---| | --base <ref> | Git ref to compare against (default HEAD) | | --baseline <file> | Compare against a saved baseline (or any lockfile) instead of git | | --save-baseline <file> | Write a baseline snapshot of the current lockfile and exit | | --lockfile <path> | Lockfile to check (auto-detected by default) | | --min-age <dur> | Minimum publish age: 90m, 72h, 3d, 1w, 0 | | --offline | No registry lookups | | --registry <url> | Registry URL (default https://registry.npmjs.org) | | --no-dev | Ignore dev dependencies | | --no-signatures | Skip registry tarball signature verification | | --require-provenance | Treat missing provenance attestations as findings | | --enforce-allow-scripts | Fail when install scripts run outside allowScripts | | --write-allow-scripts <file> | Write the configured allowScripts list to a JSON policy file | | --config <file> | Config file path | | --fail-on <level> | error (default), warn, note or none | | --json [file] | JSON report to a file, or stdout | | --sarif <file> | SARIF 2.1.0 report for GitHub code scanning | | --markdown <file> | Markdown report for PR comments and job summaries | | --quiet, --no-color | Output tweaks |

Exit codes: 0 passed · 1 findings at or above --fail-on · 2 usage or runtime error.

Sample output

package-tripwire — 2 → 6 packages (4 added, 1 updated, 0 removed)
  ERROR [install-script] [email protected] runs install scripts → postinstall: node install.js
  ERROR [typosquat] expresss looks like a typosquat: edit distance 1 from "express"
  WARN  [maintainer-change] [email protected] maintainers changed -thebigredgeek,-tjholowaychuk
  WARN  [unknown-package] [email protected] not found in registry
  WARN  [non-registry-source] [email protected] is installed from a git source: git+ssh://[email protected]/someone/my-fork.git#3f2c1a9
  WARN  [deprecated] [email protected] is deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
  NOTE  [new-dependency] [email protected] added
  ...

2 error(s), 4 warning(s), 4 note(s).
✖ Failing (fail-on: error).

Configuration and allowlist

Put the config in .package-tripwire.json (or .package-tripwirerc.json, or a "package-tripwire" key in package.json):

{
  "minAge": "72h",
  "failOn": "error",
  "includeDev": true,
  "registry": "https://registry.npmjs.org",
  "rules": { "new-dependency": "off", "deprecated": "error" },
  "popular": ["my-company-ui"],
  "allow": [
    "@my-company/*",
    "[email protected]",
    { "package": "sharp", "rules": ["install-script"], "reason": "prebuilt binaries" },
    { "package": "left-pad", "version": "1.3.0", "rules": ["recent-publish"], "expires": "2026-12-31" }
  ],
  "allowScripts": ["esbuild", "sharp", "@my-org/*"],
  "enforceAllowScripts": true,
  "verifySignatures": true,
  "requireProvenance": false,
  "lockfileLint": {
    "allowedHosts": ["registry.npmjs.org", "registry.yarnpkg.com", "npm.pkg.github.com"],
    "allowedSchemes": ["https:"],
    "validatePackageNames": true
  }
}

Wire install-script enforcement in CI the same way as @lavamoat/allow-scripts: keep allowScripts in config, turn on enforceAllowScripts, and optionally commit the policy with --write-allow-scripts .package-tripwire-allow-scripts.json.

  • rules sets each rule's level to error, warn, note or off.
  • allow entries can be name, name@version or a glob like @scope/*, or an object with package, version, rules, reason and expires. An expired entry stops matching, so exceptions don't stay forever.
  • popular adds your own names to the list used for typosquat checks.

Allowlisted findings are counted in the summary and listed under suppressed in the JSON.

GitHub Action

name: Dependency tripwire
on: pull_request
permissions:
  contents: read
  security-events: write   # only needed for SARIF upload
jobs:
  tripwire:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: MrLouisWright/package-tripwire@v1
        with:
          min-age: 72h
          fail-on: error
          sarif: tripwire.sarif
      - if: always()
        uses: github/codeql-action/upload-sarif@v3
        with: { sarif_file: tripwire.sarif }

On a pull request it compares against origin/<base branch> by default. Otherwise it uses HEAD~1.

| Input | Default | | |---|---|---| | base | PR base / HEAD~1 | Git ref to compare against | | baseline | | Baseline file, used instead of a git ref | | lockfile | auto | Lockfile path | | min-age | 72h | Minimum publish age | | fail-on | error | error / warn / note / none | | config | | Config file path | | offline | false | Skip registry lookups | | sarif, json | | Report paths | | job-summary | true | Write the Markdown report to the job summary | | args | | Extra CLI arguments |

Library

Works with ESM and CommonJS (Node 18+):

import { run, analyze, parseAny, createRegistry, formatSarif, lintLockfile, findDisallowedInstallScripts } from 'package-tripwire';
// const { run } = require('package-tripwire');

const result = await run({ base: 'origin/main', config: { minAge: '3d', enforceAllowScripts: true, allowScripts: ['esbuild'] } });
console.log(result.findings);

TypeScript declarations are generated from source into dist/ when you run npm run build (also run automatically before npm test / publish).

Limitations

  • Typosquat detection is a heuristic built on a list of popular packages. Expect the occasional false positive, and allowlist it.
  • For git, URL and file: dependencies there is no registry metadata, so only the source is flagged.
  • Yarn Berry stores its own checksum format, so the check that compares lockfile integrity with the registry is skipped for Berry. The same-version change check still runs.
  • Signature verification depends on registry signing keys and dist.signatures metadata (private registries must expose the same /-/npm/v1/keys convention as the public npm registry).
  • enforceAllowScripts blocks disallowed packages at scan time; combine with your package manager's lifecycle hooks if you need install-time enforcement like LavaMoat.

License

MIT © Louis Wright