Skip to main content

Docs / GitHub PR Bot

GitHub PR Bot

Turns a finding with a real patch into a draft pull request. Nothing merges itself.

What it does

Said plainly, up front, because it's the thing that makes this feature acceptable to turn on at all: this clones your repository at a branch you choose, applies each qualifying finding's patch as an exact string replacement in the one file it names, commits under a Ryvx bot identity, pushes a new branch, and opens a draft pull request back to the branch you started from: draft: true is a hardcoded literal in the API call that opens it, not a default that can be toggled off. It never pushes to your base branch directly and never merges anything. A human reviews the diff on GitHub, the same as any other PR. Unless you skip it, the bot also re-scans the new branch afterward and posts a comment on the PR confirming which findings were fixed and flagging any that are still present after the patch: the PR carries a check on its own claim, not just an assertion that it worked.

Which findings it can use, and why

This is the section most worth reading before you try it. A finding is usable by this bot only if it carries three fields, all three, not any one of them: fix_before, fix_after, and file. The exact rule, from ryvx/vcs_common.py's applicable_fixes:

[f for f in findings if f.get("fix_before") and f.get("fix_after") and f.get("file")]

Those three fields are source-code patch fields: a literal before/after string pair and the file path to apply it in. A live-URL, black-box pentest never has anything to put in them; it has no source checkout to diff against, so the fixer subagent that would normally produce a patch has nothing to patch. That scan can still produce real, valid findings; it just can't produce ones this bot can act on. If you point this bot at a scan and see "no source patches (live URL scan)" against every finding, that is the expected result for that kind of scan, not a bug and not something worth filing a report about. It only ever picks up findings from a scan that had real source access to diff against.

Even among findings that pass that check, one can still fail to apply at PR-open time: if the exact fix_before text isn't found in the file anymore (it likely changed since the finding was filed) or matches more than once (ambiguous: which occurrence does the fix replace?), that one finding is skipped with a stated reason, and the run continues with whatever else applied cleanly. Nothing here is force-applied.

Connecting a repository

This isn't a self-serve toggle, and it isn't included with any plan. Email us and we'll set up a fine-grained GitHub personal access token scoped to the repository you want reviewed, with Contents: read and write and Pull requests: read and write, nothing broader.

However it reaches us, the token is sealed, not merely "encrypted in transit": it's encrypted (RSA-OAEP, SHA-256) directly to the public half of a key pair whose private half never leaves the worker box, before it's ever written anywhere. Only the resulting ciphertext, plus which key sealed it, is stored (public.github_sealed_tokens); the plaintext token is never a query argument, never in a URL, and it is never something even we can read back out. The worker box is the only place the matching private key exists, and unsealing only ever happens there, server-side. The only thing any client can ever read back is a connection-status check (connected or not, a display fingerprint, when it was last updated) through a dedicated function; the raw token table has no read policy for anyone but the worker's own service credential, so there is no path, even a buggy one, for a browser to read a previously-submitted token back out.

The caps

Up to 3 connected repositories, ever, and up to 150 remediation PRs per calendar month, both counted org-wide and enforced at the database level on every insert. These caps aren't a plan perk that scales with what you pay for: connecting the bot at all is arranged directly with us rather than something any plan includes, and the caps are the same regardless. Re-running the bot against a repository you've already connected never counts as a new one against the 3-repo cap; only a genuinely new fourth repository does. The repo cap counts distinct repositories the bot has ever touched, not a rolling window; it behaves like a standing entitlement (which repos this org's bot is installed on), the same way a real GitHub App installation would be counted, not like the monthly review count, which does reset.

Both are hard caps, not metered overflow: this job type makes no LLM call at all (the hosted path always passes --skip-verification to the CLI underneath it, keeping it LLM-free and zero marginal cost), so there is no real per-job spend for an overflow charge to protect against: a customer who hits either cap is told to disconnect a repo or wait for the month to roll over, not offered a way to pay past it.

Running it

Hosted

Once we've connected your repository, open New remediation PR and fill in: Repository (owner/name, must match a repo the connected token can push to), Clone URL (https only, and your token never appears on this page or in this URL), Base branch (a real branch on that repo the PR will target), and Scan to pull findings from, a dropdown of your completed pentest scans, each labelled with how many usable findings it has (or "no source patches (live URL scan)" when it has none, see above). It needs a GitHub token connected and a positive credit balance to be queued at all, even though the job itself spends none once it runs.

Local (CLI)

python -m ryvx fix-pr <run_name> --repo owner/name --repo-dir /path/to/checkout \
  --base main --fix-pr-token "$RYVX_FIX_PR_TOKEN"

run_name is an existing local scan (the one ryvx_runs/<run_name>/findings.json came from), and --repo-dir is your own pre-existing checkout of that repo, already on the branch you want fixes applied to; unlike the hosted path, the CLI never clones anything for you. --fix-pr-token defaults to $RYVX_FIX_PR_TOKEN if omitted. Add --dry-run to see what would be applied and skipped without touching any file, git, or network state, or --skip-verification to open the PR without the automatic re-scan. Pass --provider gitlab (with --fix-pr-token defaulting instead to $RYVX_GITLAB_FIX_PR_TOKEN) to open a draft merge request on GitLab instead: the hosted dashboard path is GitHub-only today; GitLab is CLI-only. This command is human-invoked only: it is never called from inside the agent tool loop, a deliberate boundary this feature keeps in v1.

Limits and known rough edges

  • There is no self-serve way to connect or disconnect a repository. Hitting the 3-repo cap, or wanting to connect a repository in the first place, means emailing support, not clicking a "connect" or "disconnect" button.
  • Only findings with a complete fix_before/fix_after/file patch are ever considered, for the reason explained above: there is no partial-credit path for a finding missing one of the three.
  • A finding that qualifies can still be skipped individually at apply time if its exact fix_before text no longer matches the file, or matches more than once; the rest of the run continues regardless.
  • The re-scan verification step, when it runs, invokes the same scan pipeline as an ordinary run and can itself take real time; skipping it with --skip-verification trades that confirmation for a faster PR.

Next

  • Verification: the PoC-gated discipline this bot's own re-scan step is built on.
  • How it works: where a finding's fix_before/fix_after patch comes from in the first place.

← Back to Docs
STAY IN THE LOOP

Release notes and product updates, by email.

We'll send a confirmation email; you're not on the list until you click the link in it. See our privacy policy.