Vulnerability scanner · Written in Rust · v0.5.0

Fast, reliable vulnerability scanning for Python dependencies.

PySentry reads your lock file, resolves the full dependency tree, and checks every package against three advisory databases, then reports what is affected, how severe it is, and the smallest upgrade that fixes it. When a source fails mid-scan, it says so. A report is never silently incomplete.

$ uvx pysentry-rs
up to78×
faster than pip-audit on cached runs, same PyPI source, same 29 findings
0.011s
to audit 692 packages once the vulnerability DB is cached
30
distinct vulnerabilities found across the 692-package benchmark dataset
3
advisory databases queried by default

The report

Real output · v0.5.0
$pysentry --fail-on high exit 1
SUMMARY: 4 packages scanned • 4 vulnerable • 7 vulnerabilities found

SEVERITY:  3 HIGH     3 MEDIUM     1 LOW

FIXABLE: 7 vulnerabilities can be fixed by upgrading packages

SUPPRESSED: 2 findings suppressed by policy (still reported,
excluded from the exit condition)

FAILING: 1 finding at or above 'high' — exit non-zero

VULNERABILITIES
IDPackageVersionSeverityType
PYSEC-2024-230 certifi v2023.7.22 HIGH direct
PYSEC-2026-1433 (SUPPRESSED) gunicorn v21.2.0 HIGH direct
PYSEC-2026-1434 (SUPPRESSED) gunicorn v21.2.0 HIGH direct
PYSEC-2026-1872 requests v2.31.0 MEDIUM direct
PYSEC-2026-1873 requests v2.31.0 MEDIUM direct
PYSEC-2026-2275 requests v2.31.0 MEDIUM direct
PYSEC-2026-2151 flask v3.0.0 LOW direct
FIX SUGGESTIONS
  certifi   2023.7.22 → 2024.7.4
  flask     3.0.0 → 3.1.3
  gunicorn  21.2.0 → 22.0.0  fixes 2 vulnerabilities
  requests  2.31.0 → 2.33.0  fixes 3 vulnerabilities

Run pysentry --detailed for full descriptions

A real audit of four pinned packages, with gunicorn muted by policy in .pysentry.toml. Tables redrawn for the web; every value is verbatim.

Suppressed

[ignore] mutes an advisory or a whole package. Muted findings stay in the report and every export format. They stop failing the build, nothing more.

Failing

The report names the threshold that tripped and how many findings crossed it, so a red CI job explains itself.

Fix suggestions

The smallest upgrade that clears every advisory on a package, rather than the newest release. The report notes backport-only fixes.

Compact by default

One screen for CI logs. --detailed adds full descriptions, CVSS vectors, and references.

Features

01 — 08
01

Every dependency format

uv.lock, poetry.lock, Pipfile.lock, pylock.toml, pyproject.toml, Pipfile, requirements.txt, PEP 723 inline metadata. Lock files take precedence.

02

Three databases, one report

PyPA Advisory Database, PyPI JSON API, and OSV.dev, queried concurrently. The same flaw often ships under GHSA, PYSEC, and CVE IDs; PySentry folds the aliases into one finding.

03

CVSS v3 & v4 scoring

Reads CVSS v3 and v4 vectors and shows the exact score behind every finding. When databases disagree, the newer-version rating wins.

04

Tree-aware findings

Distinguishes direct from transitive dependencies and names the top-level package that pulls a vulnerable one in.

05

PEP 792 lifecycle checks

Flags archived, deprecated, and quarantined packages. --forbid-quarantined turns known malware into a failing build.

06

Security policy

--fail-on gates the build by severity without hiding lower findings, and each dependency group can carry its own threshold: strict in production, permissive in dev. [ignore] mutes an advisory or a whole package; muted findings still show in the report, they just stop failing the build.

07

Partial scans don’t pass

If one advisory source fails while others answer, PySentry finishes the scan, marks the report partial, and exits 2. --no-fail-on-partial accepts the partial result. It also reads OSV to the last page, so long advisory lists never get cut short.

08

Rust core

Async fetching, local caching, and isolated resolvers. A warm audit of a ~700-package project finishes in under a second.

Performance

vs pip-audit 2.10.1

Median of 5 runs. Dataset: 692 pinned packages, audit-only mode. Cold clears all caches (fresh CI run); hot has a warm vulnerability DB. On this dataset pysentry reports 30 distinct vulnerabilities, pip-audit 29. Environment: 4-core Linux CI runner, Python 3.11.

Cold — fresh CI
seconds · lower is better · median of 5
pysentry (all sources)2.50s
pysentry (pypi only)1.21s
pip-audit (pypi)18.8s
Hot — cached
seconds · lower is better · median of 5
pysentry (all sources)0.624s
pysentry (pypi only)0.011s
pip-audit (pypi)0.890s

The solid bar is the fastest of 5 runs; the faded extension reaches the median; its length is the run-to-run variance. Cold runs are network-bound and swing (see pip-audit); hot runs barely move.

Tool Cold (fresh CI) Hot (cached) Peak memory (hot) Vulns found
pysentry (all sources, default) 2.50s 0.624s 353 MB 30no duplicates
pysentry (pypi only) 1.21s 0.011s 7.7 MB 29no duplicates
pip-audit (pypi) 18.8s 0.890s 57 MB 3029 after deduplication
pip-audit (osv) 1m 40.6s 1m 40.3s 57 MB 4930 after deduplication

Memory: the default config loads the full PyPA advisory database in memory (353 MB); the PyPI-only config runs in 7.7 MB.

Vulnerability counts collapse GHSA / PYSEC / CVE aliases. pip-audit's OSV feed lists the same flaw under several advisory IDs (49 reported vs 30 distinct here); PySentry de-duplicates them by default.

Differences besides speed
pysentry pip-audit
Advisory databases Three, queried concurrently and merged into one report One per run: PyPI, OSV, or ecosyste.ms
Duplicate advisories GHSA / PYSEC / CVE aliases collapsed into one finding Each alias listed separately
Severity gate --fail-on threshold, per-group overrides; lower findings still shown Any finding exits non-zero; no severity threshold
Source failure Keeps scanning the healthy sources, marks the report partial, exits 2 Single source; a fetch failure ends the audit
Dependency context Direct vs transitive, naming the top-level package that pulls it in Flat list

Full methodology, cold-cache runs, and resolver benchmarks in the benchmark report.

CI-native

GitHub Action · Job summary · Exit codes

GitHub Action

Downloads a checksum-verified binary and audits the project. Every run writes a compact Markdown report to the job summary; pushes also upload SARIF to Code Scanning. On pull requests, that upload is off by default; upload-sarif-on-pr: 'true' turns it back on for same-repo PRs.

Any other CI

--fail-on high exits non-zero when findings reach the threshold, and the report states which threshold tripped. Exit code 2 means the run never finished cleanly: resolver errors, bad input, or a partial scan.

Pre-commit

A hook from pysentry/pysentry-pre-commit scans on every commit. Output is compact by default; --detailed expands it.

permissions:
  security-events: write # for the SARIF upload

steps:
  - uses: actions/checkout@v4
  - uses: nyudenkov/pysentry@v0.5.0
    with:
      fail-on: high

Supported formats

Lock files take precedence
uv.lock poetry.lock Pipfile.lock pylock.toml pyproject.toml Pipfile requirements.txt PEP 723 scripts

Marked formats are lock files, scanned exactly as pinned. PySentry resolves manifests with uv (pip-tools fallback) in an isolated environment.

Install

Pick your toolchain
# Run instantly, no install
uvx pysentry-rs

# Scan another project, JSON output
uvx pysentry-rs /path/to/project --format json
# Install from PyPI
pip install pysentry-rs

# Run
pysentry-rs /path/to/project
# Install from crates.io
cargo install pysentry

# Run (note: the binary is `pysentry` here)
pysentry /path/to/project
# Pre-built, checksum-verified binaries for Linux / macOS / Windows
# attached to every release:
https://github.com/nyudenkov/pysentry/releases