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

@arcane-spark/ubel-node

v0.2.0

Published

Supply-chain security firewall for Node.js — resolves dependencies, scans via OSV.dev and NVD, and enforces configurable vulnerability policies before anything reaches node_modules.

Readme

UBEL — Unified Bill / Enforced Law

Node.js Supply-Chain Security CLI

Ubel resolves dependencies, generates PURLs, scans them through OSV.dev and NVD, and enforces configurable security policies at install-time to block supply-chain attacks before they reach production.

This document covers the Node.js ecosystem (npm, pnpm, bun, yarn).


Features

  • Full dependency resolution with PURL generation via lockfile dry-run
  • OSV.dev vulnerability scanning via batched API queries and NVD's APIs
  • Concurrent vulnerability enrichment (CVSS, fix recommendations, references)
  • Policy engine — block/allow by severity threshold and unknown-severity packages
  • Malicious package (infection) detection — always blocked regardless of policy
  • check mode — dry-run resolution and scan with no side effects
  • install mode — scan-gate before installation; blocks if policy violated
  • health mode — scan the current project's installed dependencies
  • Atomic lockfile revert — originals are always restored on violation or error
  • Disk-based lockfile backup under .ubel/lockfiles/<timestamp>/ with manual recovery on failure
  • Dependency graph with introduced-by and parent tracking
  • Automatic report generation: timestamped JSON (*.json) + HTML (*.html) + SBOM (*.cdx.json) + SARIF (*.sarif.json) per scan, plus latest.* convenience links
  • Zero external runtime dependencies (Node.js stdlib only)
  • Complete compliant, and enriched SBOM Cyclonedx v1.6 files with full dependencies and vulnerabilities data in VEX.
  • Complete compliant, and enriched SARIF v2.1.0 files.

Installation

npm install -g @arcane-spark/ubel-node

After installation, the following entry-point binaries are available:

| Binary | Package Manager | |---|---| | ubel-npm | npm | | ubel-pnpm | pnpm | | ubel-bun | bun | | ubel-agent | AI agent workspace scan ( OS, runtimes, tools, dependencies ) | | ubel-platform | Host platform scan (OS, runtimes, tools) |

yarn does not support a lockfile-only dry-run — yarn add always writes node_modules. UBEL supports yarn in health scan mode only and cannot provide install-blocking firewall coverage for it.


Requirements

  • Node.js >=18.0.0
  • The package manager binary being targeted (npm, pnpm, or bun) must be available on PATH

Usage

ubel-npm   <mode> [packages...]
ubel-pnpm  <mode> [packages...]
ubel-bun   <mode> [packages...]

Package arguments are optional for check and install — when omitted, the existing lockfile in the working directory is used as the dependency source.


Firewall Mechanics

npm

ubel-npm check and ubel-npm install <pkg> invoke npm's --package-lock-only flag, which resolves the full dependency tree and writes a candidate package-lock.json without touching node_modules/. UBEL scans the candidate lockfile, then makes a binary decision:

  • Clean — the candidate lockfile is accepted and the actual install proceeds via npm ci.
  • Violationpackage-lock.json is reverted to its pre-scan state from the disk backup. node_modules/ is never touched. The process exits non-zero.

pnpm

Identical flow to npm, using pnpm's --lockfile-only flag. The candidate pnpm-lock.yaml is written, scanned, then either accepted or reverted. node_modules/ is never written during the scan phase.

bun

Uses bun's --lockfile-only flag. The candidate bun.lock is written and scanned before any node_modules/ mutation. The revert path is identical to npm and pnpm.

UBEL's firewall always blocks pre/post install scripts to prevent running malicious scripts

All the 3 package manager are triggered with the flag: --ignore-scripts

Lockfile backup and recovery

Before any dry-run mutation, originals are backed up to .ubel/lockfiles/<timestamp>/. If the revert itself fails (e.g. a disk error mid-restore), the original lockfile is preserved at the backup path and its location is printed to stderr so the user can recover manually.

TOCTOU integrity protection

After the dry-run completes and the scan passes policy, there is a window between the scan decision and the real install during which the on-disk lockfile or package.json could be mutated — by another process, a racing script, or a compromised tool. UBEL closes this window with SHA-256 integrity checks before any real install is allowed to proceed.

At the end of every dry-run, UBEL captures two digests in memory:

  • _candidateLockfileHash — SHA-256 of the raw candidate lockfile bytes written to disk by the dry-run (package-lock.json, pnpm-lock.yaml, or bun.lock).
  • _candidatePackageJsonHash — SHA-256 of package.json as it exists on disk after the dry-run. For npm, this digest is re-captured after UBEL regenerates package.json with exact pinned versions from the lockfile, so the hash always reflects the file that will be present at install time.

Immediately before invoking the real install command (npm ci, pnpm install --frozen-lockfile, bun install --frozen-lockfile), both files are re-hashed from disk and compared against the in-memory digests. If either hash does not match, the install is aborted and the lockfile is reverted — nothing is written to node_modules/. The mismatch details (expected hash, actual hash, file path) are printed to stderr.

Lockfile integrity check FAILED — the lockfile was modified after scanning.
  Expected : a3f1…
  Got      : 9c2b…
  File     : /project/package-lock.json

If no lockfile existed before the dry-run (fresh project), the absence itself is recorded as the expected state and enforced the same way.

This protection also extends to the backup manifest files created earlier before reverting the changes.


Modes

health

Scans the current project's installed dependency graph without running any install. Reads the existing lockfile directly and submits resolved packages to OSV.dev and NVD's APIs.

ubel-npm health
ubel-pnpm health
ubel-bun health

Full-stack monorepo scanning

When invoked programmatically with full_stack: true, health walks the entire directory tree from the project root and collects packages across all supported ecosystems in a single pass — no per-language configuration required. Mixed-stack monorepos (e.g. a Node.js frontend, Python backend, Rust service, and Go tooling in the same repo) are fully covered in one invocation.

| Ecosystem | Package Manager | Resolved From | |---|---|---| | Node.js | npm, pnpm, yarn, bun | node_modules/ (on-disk walk) | | Python | pip / virtualenv | .venv, venv, virtualenv directories | | PHP | Composer | vendor/ | | Rust | Cargo | Cargo.lock | | Go | Go Modules | go.sum | | C# / .NET | NuGet | packages.lock.json / obj/project.assets.json | | Java | Maven | pom.xml resolved dependencies | | Ruby | Bundler | Gemfile.lock |

Each discovered package is deduplicated by PURL before submission, so packages shared across sub-projects are scanned exactly once.

Platform scanning (Linux)

When invoked with scan_os: true on Linux, the scanner reads the host's system package database directly — no elevated privileges required — and includes all installed system packages in the scan inventory.

| Distribution | Package Manager | Source | PURL type | |---|---|---|---| | Ubuntu | dpkg | /var/lib/dpkg/status | pkg:deb/ubuntu/ | | Debian | dpkg | /var/lib/dpkg/status | pkg:deb/debian/ | | Alpine / Alpaquita | apk | /lib/apk/db/installed | pkg:apk/alpine/ | | Red Hat / RHEL | rpm | rpm -qa | pkg:rpm/redhat/ | | AlmaLinux | rpm | rpm -qa | pkg:rpm/almalinux/ | | Rocky Linux | rpm | rpm -qa | pkg:rpm/rocky-linux/ | | CentOS / Fedora | rpm | rpm -qa | pkg:rpm/redhat/ |

Each package entry includes its binary install paths and direct dependency edges as reported by the package database.

Platform scanning (Windows)

When invoked with scan_os: true on Windows, the scanner probes the registry and known binary paths — no elevated privileges required — and enumerates the following software components using CPE 2.3 identifiers:

| Category | Components | |---|---| | Operating system | Windows 10 / 11 (build-accurate CPE version) | | Security | Windows Defender | | Runtimes | Node.js, Python, PHP, Go, Rust, Ruby, JRE, JDK | | .NET | All installed .NET Core / Desktop / ASP.NET runtimes (multi-version) | | Browsers | Chrome, Firefox, Microsoft Edge | | Developer tools | Git, Docker Desktop, Visual Studio, Cursor | | Shell | PowerShell |


check

Dry-run: resolves the given packages (or the existing lockfile) via a lockfile-only pass, scans the resolved set, and exits. Nothing is installed and lockfiles are fully reverted to their original state afterwards.

# Scan specific packages without installing
ubel-npm check lodash express

# Scan the current lockfile with no changes
ubel-npm check

Exits 0 if policy passes, 1 if policy blocks or the scan fails.


install

Same pipeline as check, but proceeds to install (via npm ci / pnpm install --frozen-lockfile / bun install) if and only if the policy decision is allow.

ubel-npm install [email protected] express
ubel-npm install                          # resolves from existing lockfile

ubel-pnpm install react react-dom
ubel-bun install

If the policy blocks, installation is aborted, the lockfile is reverted, and the process exits 1.


threshold

Sets the severity level at or above which vulnerabilities block the scan. Accepts low, medium, high, critical, or none (disable threshold blocking).

ubel-npm threshold high       # block high and critical
ubel-npm threshold critical   # block critical only
ubel-npm threshold none       # disable severity blocking

Infections (MAL-* advisories) are always blocked regardless of this setting.

The threshold is persisted to the local policy file and applies to all subsequent scans until changed.


block-unknown

Controls whether packages with unknown-severity vulnerabilities are blocked.

ubel-npm block-unknown true
ubel-npm block-unknown false

Policy

Policy is stored as JSON at .ubel/local/policy/config.json relative to the project root.

Default policy created on first run:

{
    "severity_threshold": "high",
    "block_unknown_vulnerabilities": true
}

Severity threshold — vulnerabilities at or above this level cause a block. Severity order: low → medium → high → critical.

Block unknown — when true, any vulnerability whose severity cannot be determined also causes a block.

Infections — advisories with IDs beginning MAL- are always blocked and are not subject to either setting above.


Package Argument Validation

All package specifiers passed to check and install are validated against an allow-list pattern before any subprocess is invoked. Accepted formats:

name
name@version
@scope/name
@scope/name@version

Specifiers containing shell metacharacters or other unsafe characters are rejected immediately and the process exits non-zero before any filesystem or network operation occurs.


Programmatic API

main() doubles as a programmatic entry point for agents, platform scanners, and the VS Code extension:

import { main } from "@arcane-spark/ubel-node";

const report = await main({
  projectRoot : "/abs/path/to/project",
  engine      : "npm",
  mode        : "health",
  is_script   : true,
  save_reports: true,
  scan_os     : false,
  full_stack  : false,
  scan_node   : true,
  scan_scope  : "repository",   // repository | agent | developer_platform | editor_extension
});
// report is the full finalJson object (inventory, vulnerabilities, decision, …)

When called this way, the banner and interactive console output are suppressed. The return value is the same machine-readable report object written to disk.

IMPORTANT: The programmatic API is primarily exposed for lightweight integrations and automation around UBEL's CLI and VS Code workflows. UBEL is designed first and foremost as a single-operation CLI/runtime scanner, not as a multi-tenant concurrent scanning library.

main() is therefore currently not concurrency-safe. Do not invoke multiple install, check, or health operations in parallel within the same process, as report generation and internal scan state may overlap.

Run operations sequentially until parallel execution support is introduced in a future release.


Reports

Every scan writes two files to a timestamped path and overwrites the latest* convenience links:

.ubel/reports/latest.json          ← always current
.ubel/reports/latest.html          ← always current
.ubel/reports/latest.cdx.json          ← always current
.ubel/reports/latest.sarif.json          ← always current

.ubel/local/reports/<ecosystem>/<mode>/<YYYY>/<MM>/<DD>/
    <ecosystem>_<mode>_<engine>__<timestamp>.json
    <ecosystem>_<mode>_<engine>__<timestamp>.html
    <ecosystem>_<mode>_<engine>__<timestamp>.cdx.json
    <ecosystem>_<mode>_<engine>__<timestamp>.sarif.json

The HTML report is fully self-contained (no server required) and includes:

  • Dashboard with severity breakdown chart and policy decision
  • Searchable, filterable vulnerability table
  • Full inventory with state (safe / vulnerable / infected / undetermined)
  • Interactive force-directed dependency graph with vulnerable-subtree filter
  • Per-vulnerability detail modals (CVSS vector, fix recommendations, OSV/NVD references)
  • System and runtime metadata

The JSON report contains the full machine-readable equivalent and can be consumed by CI/CD tooling directly.


CI/CD Integration

All CLI commands exit non-zero on policy violations, making them native to any CI runner:

# GitHub Actions
- name: UBEL dependency scan
  run: ubel-npm check

- name: UBEL firewall-gated install
  run: ubel-npm install
# Dockerfile
RUN ubel-npm install

Quick-start examples

# Scan the current lockfile without installing anything
ubel-npm check

# Gate the actual install behind a policy scan
ubel-npm install

# Scan a single package for vulnerabilities before it touches node_modules
ubel-npm check [email protected]

# Tighten policy, then re-scan
ubel-npm threshold critical
ubel-npm check

# Scan the installed project dependencies
ubel-npm health

# Same workflows with pnpm and bun
ubel-pnpm install react react-dom
ubel-bun check

Ubel — Secure every dependency, before it reaches production.