probation 0.2.1

A filtering proxy for npm and PyPI that withholds packages until they are eligible.
docs.rs failed to build probation-0.2.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

probation

crates.io

A firewall for npm and PyPI packages. You run it yourself, in front of your own npm and pip clients.

The problem

A package can be hijacked and a bad version published in seconds. Malware scanners and blocklists take hours or days to catch up. In that gap, anyone who installs the package gets the bad version.

What it does

Probation sits between your developers and the real npm and PyPI registries. It holds back any version published too recently (you choose how long, a day by default). If a newer version is held back, npm and pip fall back to the newest version that is allowed, as if the newer one did not exist yet. Nobody changes what they typed.

It also checks every download against a blocklist you supply, by package name, by version, or by exact file.

On top of your blocklist, it checks every package against OSV, a free, open database of known vulnerabilities. This is on by default and needs no setup. If OSV is slow or unreachable, that never blocks a download. The extra check is skipped for that request. See docs/operations.md §4 to tune or disable it.

"Allowed through" means it passed these checks. It is not a guarantee that a package is safe.

Quick start

Install it:

cargo install probation --locked

No Rust toolchain on this machine? Build it in a container and install the binary on the host. This needs only Docker or Podman:

scripts/build-install.sh

Check a config file is valid:

probation check-config config.sample.toml

Run it:

probation serve --config /etc/probation/config.toml

Or run it in Docker:

docker build -t probation:mvp .
docker run -d -p 127.0.0.1:8080:8080 \
  -v /srv/probation:/var/lib/probation \
  probation:mvp serve --config /etc/probation/config.toml

Then point your tools at it instead of the public registries:

# .npmrc
registry=https://packages.example.org/npm/
audit=false
# pip.conf
[global]
index-url = https://packages.example.org/pypi/simple/

config.sample.toml and blocklist.sample.json show the file formats.

It terminates TLS itself: set tls_cert_path and tls_key_path in the config, with no reverse proxy needed. public_url must always be https, even for local testing. See docs/operations.md §6 for the quickest way to get a trusted local certificate.

Before running this for real, read docs/operations.md. Two things catch people out: cache_max_bytes does not limit the size of its database (section 3), and it does not limit how many requests one client can send. Put that limit on the reverse proxy in front of it (section 6).

What it won't catch

It only checks packages as they are downloaded. So it cannot help with:

  • packages already installed, or already in a client's local cache
  • anything fetched from somewhere other than npm or PyPI: other registries, Git URLs, direct downloads, local files
  • a lockfile that pins a direct URL instead of a package name and version

It has no web UI, cannot publish packages, and does not scan files for malware itself. It enforces the blocklist you give it and what OSV reports. Something else has to produce that blocklist.

Full details: docs/operations.md, sections 7 and 12.

Building and testing

cargo clippy --all-targets -- -D warnings
cargo test                                  # no network access needed
cargo test --test e2e_npm -- --ignored      # runs the real npm client
cargo test --test e2e_pip -- --ignored      # runs the real pip client
cargo bench                                 # performance checks
cargo bench --bench delivery_rated_load     # a slower one, ~10 minutes

You need cmake, a C compiler, and perl (one crypto library builds from C), plus the Rust version pinned in rust-toolchain.toml. The e2e tests also need npm and python3 with pip, setuptools, and build installed.

In testing, it handled about 59,000 requests/second with no dropped records. See docs/operations.md §8 and §10 for how that was measured and what it means for sizing your own setup.

Documents

  • SPEC.md: the specification this was built from.
  • docs/operations.md: how to run it, its limits, backup and restore, and measured performance.
  • docs/plans/: the design and planning history, one folder per feature.

Licensed under Apache-2.0; see LICENSE.