Scan Python dependencies for exploited CVEs and abandoned packages that handle untrusted input, ranked by CISA KEV and EPSS. Runs in CI and as a Claude Code hook.
See the codeTells you which Python dependencies to fix first: the ones being exploited, and the ones nobody is left to patch. Then stops your coding agent from adding another.

A demo project with deliberately old pins, scanned on 16 September 2026. Advisory and exploitation data change daily, so the same pins will not read the same later.
In late August 2026, Anthropic's coordinated disclosure programme reported 2,300 vulnerabilities across 392 open source projects. 421 had been patched upstream. Discovery is becoming automated; remediation still needs a human. So the question worth asking about a dependency is not "is it healthy?" It is:
If a vulnerability lands in this package tomorrow, am I exposed, and is anyone home to fix it?
mock going
quiet is not a finding; an archived auth library is. The boundary call comes
from a human-reviewed map of about 1,500 packages, each with its reason.Findings are grouped by what to do about them:
| Section | Means | Fails the build by default |
|---|---|---|
| exploited | a CVE on CISA's known-exploited list affects your version | yes |
| replace | proof nobody is home - archived or marked Inactive - or an unfixable advisory in a project that has gone quiet | at a boundary |
| upgrade | advisories affect your version, and a newer release is clear of them | at a boundary |
| mitigate | an advisory with no fix anywhere, in a project that is still active | no |
| quiet | gone quiet, nothing actually wrong | no |
| unchecked | not enough data to judge | no |
pip install package-doctor
package-doctor scan # everything your project depends on
package-doctor explain pillow # the evidence behind one row
package-doctor check requests pillow==10.0.0 # before adding a dependency
It reads uv.lock, poetry.lock, Pipfile.lock, pyproject.toml, Pipfile,
setup.cfg, setup.py and requirements*.txt. Without a lockfile it assumes
the newest release a fresh install would get, and marks that version ?.
In CI, one line scans the checkout, fails the job on what needs work at a trust boundary, and writes the report to the job summary:
- uses: binuka200/package-doctor@v1.0.0
It also runs as a pre-commit hook, writes SARIF for code scanning, and lets you accept a known risk on the record, with a reason and an expiry date.
As a Claude Code hook, add this to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "package-doctor hook claude-code", "timeout": 60 }] }
],
"PostToolUse": [
{ "matcher": "Bash|Edit|Write|MultiEdit",
"hooks": [{ "type": "command", "command": "package-doctor hook claude-code", "timeout": 60 }] }
]
}
}
PreToolUse checks what an install names before it runs. PostToolUse checks
what it pulled in with it, and names written straight into a dependency file.
Measured on 60 open source repositories and 13,043 packages, on 16 September 2026:
_pytest imports, which pytest ships.The exposure map carries real signal too: among entries decided from what a package does, the ones marked exposed have security advisories 8.6× as often as the ones reviewed and cleared. The method, and its limits, are in accuracy.
pip-audit is the PyPA tool and is better at what it does: telling you, on every
commit, which pinned versions have known CVEs. Most of what lands in
fix today, upgrade and mitigate here, it would also find.
What it does not do is tell you which of those to fix first, which of them your code actually imports, or which of your dependencies has nobody left to ship a patch at all. That is this tool's job, and it is a different cadence — a quarterly maintenance review rather than a per-commit gate.
check and the Claude Code hook block, warn on and allow, and whyThe most useful contribution isn't code — it's arguing with
exposure.toml,
about 1,500 judgement calls about which packages sit where an attacker can
reach, each with a one-sentence reason. A verdict you think is wrong is worth
the same: post it in
Discussions
with the output of package-doctor explain. See
CONTRIBUTING.md
to get started.
Being listed here is not an accusation. Most unmaintained packages are the work of volunteers who gave what they could, and "no releases since 2021, repository archived" is a fact that helps a user without indicting anyone. Findings are worded that way on purpose. If you find output that reads as a judgement on a maintainer rather than a description of risk, that is a bug — please report it.
The reasoning behind this tool is set out in Rethinking Dependency Maintenance in the Age of AI Vulnerability Research.
MIT
Python
100.0%
Scan Python dependencies for exploited CVEs and abandoned packages that handle untrusted input, ranked by CISA KEV and EPSS. Runs in CI and as a Claude Code hook.
See the codeTells you which Python dependencies to fix first: the ones being exploited, and the ones nobody is left to patch. Then stops your coding agent from adding another.

A demo project with deliberately old pins, scanned on 16 September 2026. Advisory and exploitation data change daily, so the same pins will not read the same later.
In late August 2026, Anthropic's coordinated disclosure programme reported 2,300 vulnerabilities across 392 open source projects. 421 had been patched upstream. Discovery is becoming automated; remediation still needs a human. So the question worth asking about a dependency is not "is it healthy?" It is:
If a vulnerability lands in this package tomorrow, am I exposed, and is anyone home to fix it?
mock going
quiet is not a finding; an archived auth library is. The boundary call comes
from a human-reviewed map of about 1,500 packages, each with its reason.Findings are grouped by what to do about them:
| Section | Means | Fails the build by default |
|---|---|---|
| exploited | a CVE on CISA's known-exploited list affects your version | yes |
| replace | proof nobody is home - archived or marked Inactive - or an unfixable advisory in a project that has gone quiet | at a boundary |
| upgrade | advisories affect your version, and a newer release is clear of them | at a boundary |
| mitigate | an advisory with no fix anywhere, in a project that is still active | no |
| quiet | gone quiet, nothing actually wrong | no |
| unchecked | not enough data to judge | no |
pip install package-doctor
package-doctor scan # everything your project depends on
package-doctor explain pillow # the evidence behind one row
package-doctor check requests pillow==10.0.0 # before adding a dependency
It reads uv.lock, poetry.lock, Pipfile.lock, pyproject.toml, Pipfile,
setup.cfg, setup.py and requirements*.txt. Without a lockfile it assumes
the newest release a fresh install would get, and marks that version ?.
In CI, one line scans the checkout, fails the job on what needs work at a trust boundary, and writes the report to the job summary:
- uses: binuka200/package-doctor@v1.0.0
It also runs as a pre-commit hook, writes SARIF for code scanning, and lets you accept a known risk on the record, with a reason and an expiry date.
As a Claude Code hook, add this to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "package-doctor hook claude-code", "timeout": 60 }] }
],
"PostToolUse": [
{ "matcher": "Bash|Edit|Write|MultiEdit",
"hooks": [{ "type": "command", "command": "package-doctor hook claude-code", "timeout": 60 }] }
]
}
}
PreToolUse checks what an install names before it runs. PostToolUse checks
what it pulled in with it, and names written straight into a dependency file.
Measured on 60 open source repositories and 13,043 packages, on 16 September 2026:
_pytest imports, which pytest ships.The exposure map carries real signal too: among entries decided from what a package does, the ones marked exposed have security advisories 8.6× as often as the ones reviewed and cleared. The method, and its limits, are in accuracy.
pip-audit is the PyPA tool and is better at what it does: telling you, on every
commit, which pinned versions have known CVEs. Most of what lands in
fix today, upgrade and mitigate here, it would also find.
What it does not do is tell you which of those to fix first, which of them your code actually imports, or which of your dependencies has nobody left to ship a patch at all. That is this tool's job, and it is a different cadence — a quarterly maintenance review rather than a per-commit gate.
check and the Claude Code hook block, warn on and allow, and whyThe most useful contribution isn't code — it's arguing with
exposure.toml,
about 1,500 judgement calls about which packages sit where an attacker can
reach, each with a one-sentence reason. A verdict you think is wrong is worth
the same: post it in
Discussions
with the output of package-doctor explain. See
CONTRIBUTING.md
to get started.
Being listed here is not an accusation. Most unmaintained packages are the work of volunteers who gave what they could, and "no releases since 2021, repository archived" is a fact that helps a user without indicting anyone. Findings are worded that way on purpose. If you find output that reads as a judgement on a maintainer rather than a description of risk, that is a bug — please report it.
The reasoning behind this tool is set out in Rethinking Dependency Maintenance in the Age of AI Vulnerability Research.
MIT
Python
100.0%