Skip to main content

patchbot

Vulnerability scanning with your scanners, your threat feeds, and a coding agent that opens the fix PR.

PyPI Python 3.11+ CI License: MIT GitHub Action

patchbot finds 5 advisories, fixes them, and re-scans clean

patchbot inventories the packages in your repository, matches them against any threat feed, accepts findings from any scanner, and fixes what it finds. When a version bump is enough, it bumps. When the bump breaks the build, it hands the failure to a coding agent. It re-scans before opening each pull request.

Run it as a CLI, as a GitHub Action, or as a scheduled Claude Managed Agents deployment with no CI involved.

Why patchbot

Dependabot / Renovate Trivy / Grype alone patchbot
Bring your own threat feed (private advisories, OSV format) no no yes
Bring your own scanner (any tool that emits SARIF / Trivy / Grype JSON) no n/a yes
Version bump + lockfile regeneration with no model call yes no yes
Fixes that need code changes (breaking major bump, failing tests) no no yes, agent tier
Re-scan before every PR no no yes
Agent runs off the CI runner; secrets stay out of the sandbox no no yes, Managed Agents
Works without CI (scheduled, any git host) no no yes

Installation

pip install patchbot            # scan + bump-only fixes
pip install "patchbot[api]"     # + Anthropic-backed agent tiers (api, managed)
pip install "patchbot[openai]"  # + OpenAI Agents API tier (openai)

Requires Python 3.11 or newer. No other runtime dependencies.

Quick start

patchbot scan                    # inventory -> OSV.dev -> table report
patchbot fix --dry-run           # preview the fix plan, change nothing
patchbot fix --pr                # bump or fix, verify, open one PR per package
patchbot fix --dry-run

How it works

flowchart LR
    inv["Inventory<br/>lockfiles · CycloneDX SBOM"]
    feeds["Feeds<br/>OSV.dev · your OSV file / URL"]
    scanners["Scanners<br/>trivy · grype · osv-scanner · any cmd"]
    findings["Findings<br/>(deduplicated)"]
    report["Report<br/>table · json · SARIF"]
    fix["Fix<br/>bump → verify → agent → verify → PR"]

    inv --> feeds --> findings
    inv --> scanners --> findings
    findings --> report
    findings --> fix

Inventory. Parsers for npm, pnpm, and yarn lockfiles, requirements*.txt, pyproject.toml, go.mod, and Cargo.toml. For other ecosystems, point patchbot at a CycloneDX SBOM from syft, cdxgen, or your own tooling.

Feeds. OSV.dev is on by default. To add a private feed, serve OSV-schema JSON from a file or URL. You keep one format.

Scanners. Wrappers for trivy, grype, and osv-scanner. The command scanner runs any tool that writes SARIF, Trivy, Grype, or osv-scanner JSON to stdout.

Fix. One branch per vulnerable package, in four steps:

  1. Bump. patchbot rewrites the pin to the lowest version that clears every advisory and regenerates the lockfile with the ecosystem's own tool. No model call.
  2. Verify. patchbot re-scans, runs test_cmd, and rejects any diff that reaches outside the dependency surface (CI config, dotfiles).
  3. Escalate. If step 2 fails, patchbot briefs the agent with the failure itself: "bumped lodash to 4.18.0; tests fail with ...". The agent fixes the code.
  4. Verify again. Then commit, push, and open the PR with the advisory table in the body.

--agent none skips step 3 and gives you bump-only mode.

Commands

Command What it does
patchbot scan [paths...] Run the pipeline and print a report. Exits 1 when any finding meets --fail-on (default high).
patchbot fix [paths...] Run the fix loop. --dry-run previews; --pr pushes and opens pull requests through gh.
patchbot plugins List each registered feed, scanner, and agent, built in and third party.
patchbot managed ... Set up (init) and schedule (deploy, list, pause, unpause) the Managed Agents backend.
patchbot scan
patchbot scan
patchbot scan --format json
patchbot scan --format json
patchbot plugins
patchbot plugins

Configuration

Put patchbot.toml in the repository root. CLI flags override the file.

[inventory]
paths = ["."]
# sbom = "sbom.cdx.json"

[feeds.osv]
enabled = true

[feeds.internal]                # your own threat feed
type = "url"                    # or "file" with path = "..."
url  = "https://intel.example.com/advisories.json"

[scanners.trivy]
enabled = true

[scanners.custom]               # your own scanner
type   = "command"
cmd    = "./scan.sh"
format = "sarif"                # sarif | trivy | grype | osv-scanner

[report]
fail_on = "high"                # critical | high | medium | low | none
ignore  = ["GHSA-xxxx-xxxx-xxxx"]

[fix]
agent    = "claude"             # claude | codex | api | openai | command | managed | none
model    = "claude-opus-5"      # optional; forwarded to the agent
max_prs  = 5
test_cmd = "npm test"
# cmd = "aider --yes --message {prompt}"     # agent = "command" only

[fix.managed]                   # agent = "managed" only; printed by `patchbot managed init`
agent_id       = "agent_..."
environment_id = "env_..."
vault_id       = "vlt_..."

Choosing an agent backend

Backend Runs where Needs Use it for
claude / codex Your machine or runner, through the CLI on PATH The CLI, logged in Local development
api Your runner, in process pip install patchbot[api], ANTHROPIC_API_KEY CI without Node
openai Your runner, in process pip install patchbot[openai], OPENAI_API_KEY CI without Node, on OpenAI models
command Wherever your tool runs Any agent that accepts a prompt (aider, opencode, ...) Non-Anthropic models
managed Anthropic's sandbox, off your runner patchbot managed init once, three IDs as secrets CI where the agent must not see repo secrets

An agent with a shell on your CI runner sits next to your repository secrets. The managed backend moves the agent into a Claude Managed Agents sandbox. Anthropic's git proxy authenticates git push; a vaulted GitHub MCP credential creates the pull request. Your GitHub token stays outside the sandbox. patchbot then re-scans the pushed branch on your side before it treats the fix as done.

pip install "patchbot[api]"
patchbot managed init --github-mcp-token "$GITHUB_MCP_TOKEN"
# prints agent_id, environment_id, vault_id; store them as repo secrets
patchbot fix --agent managed --pr

GitHub Actions

Scan and publish to the Security tab

permissions:
  security-events: write
steps:
  - uses: actions/checkout@v4
  - uses: PrismorSec/patchbot@v0
    with:
      fail-on: high
Code scanning alerts detected by patchbot

Auto-fix with Managed Agents (no Node, no agent on the runner)

permissions:
  contents: write
  pull-requests: write
steps:
  - uses: actions/checkout@v4
  - uses: PrismorSec/patchbot@v0
    with:
      fix: "true"
      agent: managed
      managed-agent-id: ${{ secrets.PATCHBOT_AGENT_ID }}
      managed-environment-id: ${{ secrets.PATCHBOT_ENVIRONMENT_ID }}
      managed-vault-id: ${{ secrets.PATCHBOT_VAULT_ID }}
    env:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Auto-fix with the in-runner api backend

permissions:
  contents: write
  pull-requests: write
  security-events: write
steps:
  - uses: actions/checkout@v4
  - uses: PrismorSec/patchbot@v0
    with:
      fix: "true"
      agent: api
    env:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Weekly, against a private feed

on:
  schedule:
    - cron: "0 6 * * 1"
steps:
  - uses: actions/checkout@v4
  - uses: PrismorSec/patchbot@v0
    with:
      config: patchbot.toml    # [feeds.internal] url = ...

action.yml documents each input. .github/workflows/patchbot-fix-example.yml is a complete workflow you can copy.

A pull request opened by patchbot fix --pr:

A pull request opened by patchbot

Scheduled deployments (no CI)

patchbot managed deploy creates a cron-scheduled Managed Agents session that clones your repositories, scans, fixes, and opens pull requests. Nothing in it depends on GitHub Actions, so it works with GitLab and Bitbucket too.

patchbot managed deploy \
  --repos owner/api,owner/web \
  --cron "0 6 * * *" --tz UTC \
  --agent-id agent_... --environment-id env_... --vault-id vlt_... \
  --run-now                       # fire one session now to test

Manage it with patchbot managed list | pause | unpause. Anthropic jitters scheduled runs by up to nine minutes, and 1 to 3 AM local wall-clock times can skip or double-fire across DST changes. Schedule outside that window, or use UTC.

Extending patchbot

Register a scanner, feed, or agent from your own package through entry points. No fork needed.

# your_package/pyproject.toml
[project.entry-points."patchbot.scanners"]
mytool = "your_package.scanner:run"      # run(cwd: str, config: dict) -> list[Finding]

[project.entry-points."patchbot.feeds"]
myfeed = "your_package.feed"             # match(packages, config) -> list[Finding]

[project.entry-points."patchbot.agents"]
myagent = "your_package.agent"           # run(prompt, cwd, model=None, timeout=600, config=None) -> int

If you would rather not write Python, [scanners.*] type = "command" and [feeds.*] type = "url" | "file" cover most cases. cmd values run through your shell, so point them at tools you trust.

FAQ

Why did a fix report failed? patchbot prints the reason: the advisory still reproduces after the bump or the agent run, test_cmd failed (with the log tail), or the change touched files outside the dependency surface. patchbot discards the branch and commits nothing.

What sets the exit code? scan exits 1 when any finding meets --fail-on or worse; none exits 0. fix exits 1 if it could not fix any package.

How do I suppress one advisory? [report] ignore = ["GHSA-..."].

Do I need Node in CI? Only for --agent claude or codex. The api, openai and managed backends need Python alone.

Which ecosystems include transitive dependencies? npm, pnpm, and yarn, through their lockfiles. For Python, Go, and Rust, set [inventory] sbom to a CycloneDX file to scan the full resolved tree.

Development

git clone https://github.com/PrismorSec/patchbot && cd patchbot
python -m venv .venv && .venv/bin/pip install -e ".[dev,yaml,api]"
.venv/bin/pytest
docs/screenshots.sh              # regenerate docs/img (needs charmbracelet/freeze)

examples/demo-npm ships with a vulnerable lodash on purpose. The repository's own CI scans it, so the Security tab always shows live findings.

License

MIT

Metadata

Release files for patchbot 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for patchbot 0.3.0
File Size Uploaded
patchbot-0.3.0.tar.gz 454.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for patchbot 0.3.0
File Interpreter ABI Platform
patchbot-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 492.8 kB

Release files / patchbot-0.3.0.tar.gz

Download URL patchbot-0.3.0.tar.gz
Size 454.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fb2b0aecc6962f63d1929f7a6eb6a84c42b2738837ca4536ecc7b3abd7fc0efa
BLAKE2b-256 checksum
How to use checksums
9a25ebc547d89eb9d8088e5a700cd9fc129b6cde551384f996d7f3273b31d519
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / patchbot-0.3.0-py3-none-any.whl

Download URL patchbot-0.3.0-py3-none-any.whl
Size 38.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7733c7cfbd08c137afc0df7ddc0ad1dcb00fb4eab685e36d8771b42410e36822
BLAKE2b-256 checksum
How to use checksums
a46b6deda7c327ddb48678bd71e3d185602d5579a7f505ad91ef92bb93574299
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page