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.
- Node.js 20 or newer
- Network access to the relevant public registries, unless
--offlineis used
There are no runtime npm dependencies.
npx --yes github:hehehe224/packageproof#v0.1.0 /path/to/projectThe 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- npm:
package.json - Python:
requirements.txtandpyproject.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.
packageproof [paths...] [--format text|json|github]
[--config path] [--fail-on warning|error|never]
[--offline] [--help] [--version]
--format text|json|githubselects human-readable output, structured JSON, or GitHub workflow annotations. The default istext.--config pathreads configuration from the specified JSON file instead of.packageproof.json.--fail-on warning|error|nevercontrols the finding severity that makes the audit fail (default:warning).--offlineparses manifests without contacting registries and marks packagesofflinewith unchecked warnings.--helpprints usage.--versionprints the CLI version.
Exit codes:
0: the audit completed and no finding met the configured failure threshold1: at least one finding met the failure threshold2: 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.
Create .packageproof.json in the project being audited:
{
"allowlist": [
"npm:@company/*",
"pypi:private-name"
],
"minAgeDays": 30,
"timeoutMs": 10000,
"concurrency": 4
}allowlistcontains exactnpm:,pypi:,cargo:, orgo: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.minAgeDayssets the age below which a published package is flagged as unusually new.timeoutMssets the timeout for each registry request.concurrencylimits simultaneous registry requests.
Treat allowlist changes as security-sensitive code review. An allowlisted package is not verified by packageproof.
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.
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 errorPin 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.
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.
- 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.
Run packageproof against the project directory or its package.json before installation:
npx --yes github:hehehe224/packageproof#v0.1.0 ./package.jsonIt 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.
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.
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.
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.