/methodology
Methodology.
How each stage works, and exactly how every published number is computed.
<?php$page = ["matching" => explicit pins only,"confidence" => deterministic,"metrics" => timestamps only,"missing" => n/a, never estimated,];
Patch state machine
Pipeline stages
- Public advisory. Advisories are pulled from NVD, GitHub Security Advisories and CISA KEV. Each record keeps its provider, record ID, source URL, retrieval time and a hash of the raw response.
- Normalize. Provider records are merged into one schema: identifier, severity, affected packages and version ranges. Text is stored as plain data and never rendered as HTML.
- Understand affected versions. Affected ranges are compared against the authoritative fixed-release table. For PHP2Shell that means the php.net security releases per supported branch.
- Search index. Only repositories that have opted in are indexed. The agent reads Dockerfiles, compose files, CI workflows and composer.json.
- Match repositories. A repository matches when a file pins an affected PHP version explicitly. Floating tags and ambiguous constraints are not treated as matches.
- Check authorization. Writes require the GitHub App to be installed on the repository and, if present, a valid .patchbot policy that allows this severity and patch type.
- Generate defensive change. A deterministic, in-place version bump to the first fixed release on the same branch. Anything else becomes HUMAN REVIEW REQUIRED. The change must pass the file allowlist, diff-size limit and secret and dangerous-content checks.
- Test. Tests run in the repository's own CI on the PR branch, never on this server. A failure stops the pipeline.
- Open PR. The change is committed to a new patch-agent/* branch and opened as a pull request. The default branch is never pushed to.
- Monitor. Check runs and review state are observed through the GitHub App until the PR is merged or closed.
- Verify merge. A merge is recorded only from a signed GitHub webhook, with the PR URL and merge commit SHA as evidence.
Matching rules (phase 1)
Dockerfile,*.dockerfile, compose files:php:<major>.<minor>.<patch>[-variant]with a full three-part version..github/workflows/*.yml:php-version: X.Y.Z.composer.json:config.platform.phponly.require.phpis a compatibility constraint, not a deployed runtime, and is never rewritten.- Floating tags (
php:8.4,php:latest) are not changed: they already resolve upstream. - Commented lines are ignored.
- Unsupported branches (8.1 and older) produce HUMAN REVIEW REQUIRED, never an automatic cross-branch upgrade.
Confidence
Phase 1 has two outcomes: a deterministic in-place bump on the same branch, or human review. There is no probabilistic confidence score, so none is shown.
Validation
- File allowlist; no path traversal.
- Line count unchanged; at most 20 changed lines.
- Secret patterns (private keys, GitHub, AWS and Slack tokens) rejected.
- Dangerous content (pipe-to-shell, base64 decode, eval, setuid) rejected.
- Repository CI must pass when
verification.require_ciis true (the default).
Metrics
- Time to patch = PR opened − agent detected the advisory.
- Time to merge = merged − PR opened.
- Median time to patch = median over patches with both timestamps.
- Success rate = merged ÷ attempts.
- Runway = liquid treasury balance ÷ 30-day average daily operating cost. Unavailable until 30 days of expenses are recorded.
- Missing data is shown as n/a. No values are estimated or compared against unsourced industry figures.
Data labelling
Every record carries dataMode: REAL or DEMO. DEMO records can be written only when the server runs with DEMO_MODE=true, live under the reserved demo-org/ namespace, carry no GitHub URLs, SHAs or signatures, and are excluded from /proof in every environment. This invariant is covered by automated tests.