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

trawly

v0.1.1

Published

Dependency risk gate for JavaScript projects: OSV advisories, SBOM scans, baselines, install blocking, and supply-chain risk signals.

Readme

trawly

A dependency risk gate for JavaScript projects and SBOMs. Reads exact installed versions from npm, pnpm, and Yarn lockfiles, or from SPDX/CycloneDX Package URLs, and queries the OSV advisory database for known vulnerabilities. It also flags supply-chain risk signals such as install scripts, deprecated packages, unexpected registries, and unusually new packages.

Limitation: trawly reports known advisories and heuristic signals. It cannot prove a package is safe : absence of findings is not absence of risk.

Install

npm install --save-dev trawly
# or run ad-hoc:
npx trawly scan

Requires Node.js >= 20.

Quickstart

# log-only run (always exits 0) : best for interactive inspection
npx trawly inspect

# gating run : exits non-zero when findings meet --fail-on (default: high). Use this in CI.
npx trawly scan

# create trawly.toml and a baseline for existing findings
npx trawly init

# scan specific lockfiles or SBOMs
npx trawly scan --lockfile path/to/package-lock.json
npx trawly scan --lockfile pnpm-lock.yaml --lockfile yarn.lock
npx trawly scan --sbom bom.cdx.json --sbom bom.spdx.json

# machine-readable output
npx trawly scan --format json > trawly-report.json
npx trawly scan --format sarif --output trawly.sarif
npx trawly scan --format markdown --output trawly.md

# include committed .env-file checks
npx trawly scan --env

# use built-in policy presets
npx trawly scan --policy strict
npx trawly scan --policy library

# fail only on findings absent from a saved baseline
npx trawly scan --baseline trawly-baseline.json
npx trawly inspect --write-baseline trawly-baseline.json

# explain where a package appears in the lockfile
npx trawly why lodash

# only production deps
npx trawly scan --prod

# show every advisory instead of grouping by package
npx trawly scan --details

Two ways to run

trawly intentionally separates the gating and reporting concerns:

| Command | Exit code on findings | When to use | | ---------------- | ------------------------------------------- | --------------------------------------------- | | trawly scan | non-zero (--fail-on, default high) | CI, pre-commit, anywhere a build should fail | | trawly inspect | always 0 unless an operational error occurs | local exploration, dashboards, "just show me" |

Both produce identical output. Only the exit behaviour differs.

CLI

trawly scan [path]      Gating run. Exits non-zero when --fail-on is met.
trawly inspect [path]   Log-only run. Always exits 0 on findings.
trawly init [path]      Write trawly.toml and an initial baseline.
trawly why <pkg> [path] Explain where a package appears in lockfiles.

Common options (both commands):

  --lockfile <path>          Explicit lockfile path; may be repeated
  --sbom <path>              Explicit SPDX/CycloneDX SBOM path; may be repeated
  --format table|json|markdown|sarif
                             Output format (default: table)
  --output <path>            Write report output to a file
  --config <path>            Path to trawly.toml
  --policy ci|strict|library|app
                             Apply built-in scan defaults
  --baseline <path>          Mark existing findings and fail only on new ones
  --write-baseline <path>    Write the current active findings baseline
  --risk / --no-risk         Override risk signals (default: config, otherwise on)
  --env / --no-env           Override committed .env scanning (CLI default: off)
  --prod                     Skip dev dependencies
  --include-dev              Include dev dependencies (default)
  -v, --details              Show one row per advisory instead of grouping
  -q, --summary              Print only the one-line severity summary

scan-only:
  --fail-on <level>          Severity gate
                             (critical|high|moderate|low|none, default: high)

Policy presets

Policy presets are conservative defaults that can be overridden by explicit CLI flags or trawly.toml.

| Policy | Fail on | Risk signals | Env scan | Dev deps | Best for | | --------- | -------- | ------------ | -------- | -------- | ------------------------- | | ci | high | on | off | included | Default CI gate | | strict | moderate | on | on | included | Security-sensitive repos | | library | moderate | on | off | excluded | Published packages | | app | high | on | on | included | Deployed applications |

Exit codes

| Code | Meaning | | ---- | --------------------------------------------------------------------------------------------- | | 0 | inspect: any outcome with no operational error. scan: no finding at or above --fail-on. | | 1 | scan only: at least one finding at or above --fail-on. | | 2 | Operational error (e.g. lockfile read failed, OSV unreachable). | | 3 | Invalid CLI input. |

Library API

import { scanProject, scanLockfile } from "trawly";

const result = await scanProject({ cwd: process.cwd() });
for (const finding of result.findings) {
  console.log(finding.packageName, finding.severity, finding.id);
}

The result follows this shape:

{
  "scannedAt": "2026-05-03T12:34:56.000Z",
  "packagesScanned": 412,
  "findings": [
    {
      "id": "GHSA-...",
      "source": "osv",
      "type": "vulnerability",
      "severity": "high",
      "ecosystem": "npm",
      "packageName": "lodash",
      "installedVersion": "4.17.20",
      "summary": "Prototype pollution in lodash",
      "url": "https://github.com/advisories/GHSA-...",
      "fixedVersions": ["4.17.21"],
      "affectedPaths": ["node_modules/lodash"],
      "fingerprint": "sha256...",
      "aliases": ["CVE-..."],
    },
  ],
  "ignoredFindings": [],
  "summary": {
    "critical": 0,
    "high": 1,
    "moderate": 0,
    "low": 0,
    "unknown": 0,
  },
  "errors": [],
  "warnings": [],
}

Config

trawly auto-discovers trawly.toml in the scanned project, or you can pass --config.

failOn = "high"
policy = "ci"
risk = true
env = false
allowedRegistries = ["https://registry.npmjs.org", "https://registry.yarnpkg.com"]

[[ignore]]
id = "GHSA-example"
package = "lodash"
ecosystem = "npm"
expires = "2026-06-30"
reason = "Not reachable in our app"

Every ignore entry must include an expiry date.

CI example (GitHub Actions)

name: trawly
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v6
      # Pin to the reviewed release commit SHA instead of a mutable branch.
      - uses: Arindam200/trawly@<full-length-commit-sha>
        with:
          fail-on: high
          upload-sarif: "true"
          env-scan: "true"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: trawly-report
          path: |
            trawly.sarif
            trawly.md

Testing

npm test
npm run typecheck
npm run build

# Extra parser dialect corpus and generated invariant checks.
npm run test:corpus

# Live differential checks against npm, pnpm, Yarn classic, and Yarn Berry.
npm run test:differential

# Larger generated graphs and OSV reliability behavior.
npm run test:stress

The optional trawly-reliability workflow can run the corpus, package-manager differential, and stress layers on demand in GitHub Actions.

Roadmap

Implemented in this branch:

  • pnpm and Yarn lockfile support
  • SARIF + Markdown reporters and a GitHub Action
  • Config file with ignore entries (with required expiry)
  • Baseline mode (fail only on new findings)
  • init onboarding command
  • Policy presets: ci, strict, library, app
  • why lockfile locator command
  • Risk signals: install scripts, deprecated packages, unexpected registries, package age
  • Multi-ecosystem scanning via SBOM (SPDX, CycloneDX)