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
The report
Real output · v0.5.0SUMMARY: 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
| ID | Package | Version | Severity | Type |
|---|---|---|---|---|
| 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.
[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.
The report names the threshold that tripped and how many findings crossed it, so a red CI job explains itself.
The smallest upgrade that clears every advisory on a package, rather than the newest release. The report notes backport-only fixes.
One screen for CI logs. --detailed adds full descriptions, CVSS vectors, and references.
Features
01 — 08Every dependency format
uv.lock, poetry.lock, Pipfile.lock, pylock.toml, pyproject.toml, Pipfile, requirements.txt, PEP 723 inline metadata. Lock files take precedence.
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.
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.
Tree-aware findings
Distinguishes direct from transitive dependencies and names the top-level package that pulls a vulnerable one in.
PEP 792 lifecycle checks
Flags archived, deprecated, and quarantined packages. --forbid-quarantined turns known malware into a failing build.
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.
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.
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.1Median 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.
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.
| 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 codesGitHub 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 precedenceMarked formats are lock files, scanned exactly as pinned. PySentry resolves manifests with uv (pip-tools fallback) in an isolated environment.
Used by
In CI pipelinesRoadmap
toward 0.4.x / 0.5.x · updated 2026-09-15roadmap items completed in the 0.4.x / 0.5.x milestone · live from GitHub
feat: support conda packages
plannedfeat: licenses scanning
plannedfeat: SBOM | SPDX 3.0 support
Show 11 more planned
feat: SBOM | CycloneDX 1.6 support
plannedfeat: SBOM | SPDX 2.3 support
plannedfeat: hatch support
plannedfeat: support for NVD access keys
plannedfeat: check packages freshness
plannedfeat: add command to scan separate package
plannedmemory consume optimization
plannedfeat: make --requirements-files support all formats
plannedpip/uv/poetry integrations/hooks
plannedfeat: offline OSV database
plannedfeat: add support for GHSA as a source
10 done for 0.4.x / 0.5.x
- feat: default to compact output
- main scope does not work with uv
- feat: check scripts with PEP 723 metadata
- How to check only certain dependency group?
- option to avoid installing when supplying requirements file
- feat: Ignore entire package
- Bug - Wrong suggestions for LangChain
- pyproject.toml configs besides defaults not being supplied
- Github Action
- Improve SARIF
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