Skip to content

Repository files navigation

packageproof — check the name before the install

packageproof

Audit dependency names before installation.

packageproof is a zero-runtime-dependency Node.js CLI that reads dependency manifests and checks direct package names against public registry metadata. It is designed to surface preinstall warning signs such as names that do not exist on the expected public registry, packages that are unusually new, and npm lifecycle scripts reported by the registry.

It does not install packages and never runs package scripts.

Important

packageproof is a heuristic, not a malware detector. Do not use it as the sole approval or CI gate for dependencies. Review source, provenance, maintainers, lockfile changes, advisories, and organizational policy too.

Requirements

  • Node.js 20 or newer
  • Network access to the relevant public registries, unless --offline is used

There are no runtime npm dependencies.

Quick start

npx --yes github:hehehe224/packageproof#v0.1.0 /path/to/project

The default path is the current working directory. packageproof automatically discovers supported manifest files there. You can also pass one or more files or directories:

npx --yes github:hehehe224/packageproof#v0.1.0 ./package.json ./services/api

Supported manifests

  • npm: package.json
  • Python: requirements.txt and pyproject.toml
  • Rust: Cargo.toml
  • Go: go.mod

Parsers are restricted, not complete implementations of TOML or ecosystem resolution. npm dependencies/dev/optional/peer entries and aliases are supported. Python supports single-line requirements and plain-string PEP 621 dependency arrays (including multiline arrays), optional dependency groups, and build-system requirements; markers are not evaluated. Cargo supports version strings and one-line inline tables, including package renames. Go supports single-line and block require entries. Workspace members and requirements includes are not traversed. Cargo per-dependency tables and Python tool-specific/escaped declarations warn as unsupported. Local, Git, URL, workspace, and alternate-registry dependency sources warn and skip lookup. Go replace/exclude directives warn but are not applied: original required names are still queried.

The audit covers names declared directly in these manifests. It does not inspect lockfiles or transitive dependencies.

CLI

packageproof [paths...] [--format text|json|github]
             [--config path] [--fail-on warning|error|never]
             [--offline] [--help] [--version]
  • --format text|json|github selects human-readable output, structured JSON, or GitHub workflow annotations. The default is text.
  • --config path reads configuration from the specified JSON file instead of .packageproof.json.
  • --fail-on warning|error|never controls the finding severity that makes the audit fail (default: warning).
  • --offline parses manifests without contacting registries and marks packages offline with unchecked warnings.
  • --help prints usage.
  • --version prints the CLI version.

Exit codes:

  • 0: the audit completed and no finding met the configured failure threshold
  • 1: at least one finding met the failure threshold
  • 2: invalid CLI usage, configuration, or manifest input

Registry/network failures become unknown warnings; they are not silently treated as passes. A name missing from a public registry is not proof of a typo, malware, or absence from a private registry.

Configuration

Create .packageproof.json in the project being audited:

{
  "allowlist": [
    "npm:@company/*",
    "pypi:private-name"
  ],
  "minAgeDays": 30,
  "timeoutMs": 10000,
  "concurrency": 4
}
  • allowlist contains exact npm:, pypi:, cargo:, or go: names, optionally ending in * for prefix matching. PyPI names are lowercase with runs of ., _, or - normalized to -; use canonical names here. Matching dependencies skip public-registry lookup; use this for known private packages.
  • minAgeDays sets the age below which a published package is flagged as unusually new.
  • timeoutMs sets the timeout for each registry request.
  • concurrency limits simultaneous registry requests.

Treat allowlist changes as security-sensitive code review. An allowlisted package is not verified by packageproof.

Output formats

JSON output has a stable top-level schema version:

{
  "schemaVersion": 1,
  "version": "0.1.0",
  "files": [],
  "packages": [],
  "findings": [],
  "summary": {}
}

Every finding has a severity of error, warning, or info. Consumers should check schemaVersion before interpreting output and tolerate additional fields in future compatible releases.

The github format emits escaped workflow commands suitable for annotations; untrusted filenames and package metadata cannot inject extra GitHub Actions commands.

GitHub Actions

Pin the reviewed GitHub release in automation:

name: Dependency preinstall audit

on:
  pull_request:
    paths:
      - "**/package.json"
      - "**/requirements.txt"
      - "**/pyproject.toml"
      - "**/Cargo.toml"
      - "**/go.mod"
      - ".packageproof.json"

permissions:
  contents: read

jobs:
  packageproof:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx --yes github:hehehe224/packageproof#v0.1.0 . --format github --fail-on error

Pin the package to a reviewed version in automation. Choose --fail-on according to your risk policy; warning is stricter and can fail on network uncertainty.

Privacy and network behavior

Online audits issue read-only HTTPS GET requests to registry.npmjs.org, pypi.org, crates.io, and proxy.golang.org. Package names are sent to the registry associated with their ecosystem. User-supplied registry URLs are not accepted and redirects are rejected. Responses are limited to 8 MiB and manifests to 1 MiB. No authentication is read from npm/pip/Cargo/Go configuration. Other protections are described in the threat model.

If dependency names are confidential, use --offline, add known private names to the allowlist, or do not run the tool against those manifests. See SECURITY.md for reporting and operational guidance.

Limitations

  • Heuristics can produce false positives and false negatives.
  • Public metadata cannot establish whether package contents are safe.
  • A nonexistent public package can be a private dependency, an unsupported source, or a registry/network issue.
  • Package age is not a measure of trustworthiness.
  • Lifecycle scripts are a review signal, not proof of malicious behavior.
  • npm script checks use an exact version when explicitly named and present, otherwise registry latest; semver ranges are not resolved. Metadata may omit scripts, including implicit native-build hooks. No archives are downloaded.
  • PyPI age uses the earliest visible release upload, not account/project creation. Go's proxy does not expose module creation age, so age is explicitly unavailable; latest-version time is not treated as creation time.
  • Only direct manifest declarations are audited; lockfiles, resolved versions, integrity hashes, and transitive dependency trees are not covered.
  • Restricted parsers deliberately skip syntax they cannot interpret safely.
  • Registry responses can change between audit and installation.
  • Offline mode cannot verify existence, age, or registry metadata.

Use packageproof before installation as one layer in a broader dependency-review process—not as a malware scanner or sole security gate.

Common questions

How can I check whether a package exists before running npm install?

Run packageproof against the project directory or its package.json before installation:

npx --yes github:hehehe224/packageproof#v0.1.0 ./package.json

It reads declared names without installing dependencies or executing lifecycle scripts, then checks public registry metadata. The same command can inspect Python, Cargo, and Go manifests.

How is packageproof different from npm audit?

npm audit checks a resolved npm dependency tree against known advisories, normally after dependencies or a lockfile exist. packageproof runs before installation, supports four ecosystems, and focuses on declared-name signals such as a package missing from the expected public registry, unusual newness, or npm lifecycle metadata. The tools answer different questions and can be used together.

Does packageproof stop slopsquatting or dependency confusion?

No tool can establish package safety from registry metadata alone. packageproof can flag nonexistent public names and help reviewers notice suspicious declarations, but it cannot determine author intent, identify every hallucinated name, resolve private-registry policy, or prove that an existing package is trustworthy. Its allowlist exists for known private names and should be code-reviewed carefully.

License and identity

Copyright (C) 2026 Lena. Code is licensed under AGPL-3.0-only. See SECURITY.md for private vulnerability reporting, the threat model for exact trust boundaries, and TRADEMARKS.md for the separately reserved project name and artwork.

Contributions are welcome; see CONTRIBUTING.md.

About

Check npm, PyPI, Cargo, and Go dependency names before install for missing packages and risky metadata.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages