The PHP Foundation Ecosystem Security Team has published a practical guide by Sebastian Bergmann that walks PHP project maintainers through the entire lifecycle of a security report. Published on August 19, 2026, it is structured as ten sections with a one-screen cheatsheet, so maintainers can jump to the stage they are in rather than reading front to back.
The core principle is coordinated disclosure: vulnerability details stay private until a fix exists. That means no public issues, no public pull requests, no commit messages like "fix SQL injection in login handler", and no fixes pushed to public branches. A commit titled that way is itself the disclosure. At the same time, maintainers should acknowledge the report within a few days, even with one sentence, because silence is what turns well-meaning reporters into frustrated ones who publish on their own. A report is a claim, not a verdict: the maintainer decides what is valid, what is out of scope, and what the timeline is. "Works as designed" is a legitimate answer if the report assumes capabilities the documented security model treats as trusted.
One section exists because it has the worst failure mode: never run proof-of-concept code on your own machine. A maintainer's development box holds SSH keys, GPG keys, Packagist and GitHub credentials, and a password manager; a malicious PoC turns those into a supply-chain attack on everyone who installs the package. The guide ranks isolation options: a microVM sandbox such as Docker Sandboxes (sbx), which runs its own kernel and denies network access by default; a container, noting that on Linux containers share the host kernel while Docker Desktop on macOS and Windows already sits behind a VM boundary; a hardened VM with snapshots and no shared folders; or a cloud throwaway instance. Crafted input files like .phar archives or serialized payloads count as code and get the same treatment.
Fixes should be prepared in GitHub's temporary private fork, with the regression test written first and commit messages kept neutral. Gotchas: CI does not run in private forks, the fork does not outlive the advisory, and a silent patch without an advisory leaves composer audit and Dependabot blind. The guide also advises sweeping for sibling bugs of the same class before publishing, since an advisory puts a spotlight on that weakness class, and deciding whether older supported branches need backports.
For Composer packages, three advisory fields decide whether the tooling works: the ecosystem must be Composer, the package name must be the exact Packagist name in vendor/package form, and affected version ranges must be precise Composer constraints. This matters more than before because since Composer 2.9 the dependency resolver actively removes advisory-covered versions from the candidate pool, and Composer 2.10 extended this into a unified dependency policy framework that also blocks malware-flagged packages even during composer install. Overbroad ranges cause real breakage: for a PHPUnit advisory in April 2026, GitHub rewrote the specific affected versions 12.5.21 and 13.1.5 into broad ranges, making every older PHPUnit version uninstallable overnight, including the never-affected PHPUnit 11. Corrections go fastest through a pull request to FriendsOfPHP/security-advisories, whose data takes precedence at Packagist, plus a PR to the GitHub Advisory Database.
Release day has a fixed order: merge the fix, tag and release, confirm the release appears on Packagist, publish the advisory, then submit the FriendsOfPHP pull request, then announce. The FriendsOfPHP step should not be skipped: Composer does not read repository-level advisories directly, and GitHub's review into the Advisory Database lags by days or weeks, so the FriendsOfPHP PR is what closes that gap promptly. For projects not hosted on GitHub it is the only route into the tooling. The gap between release and advisory publication should be minutes to hours, not days. If details leak early, the embargo is over and you publish what you have.
On CVEs: GitHub is a CNA and a CVE ID can be requested from the advisory, but requests currently take weeks. The GHSA is what actually protects users, since Composer, composer audit and Dependabot act on it, so never delay a release waiting for a CVE. Credit the reporter in the advisory; it costs nothing and is a large part of what makes responsible reporting worthwhile.
The closing sections cover long-term posture: enable GitHub Private Vulnerability Reporting and add a SECURITY.md; enforce strong 2FA on GitHub, Packagist and email, with Packagist set to surface maintainer MFA status publicly; never re-tag a released version, which Packagist now rejects for stable releases; delete stale branches and workflows to reduce Poisoned Pipeline Execution risk; and harden GitHub Actions workflows, where PHPUnit's own went from 52 findings to zero after fixing template injection, credential persistence, unpinned actions, broad permissions and unnecessary third-party actions, with zizmor recommended as an automated check.
Maintainers who get stuck can contact the Ecosystem Security Team at volker@thephp.foundation or in #ecosystem-security on the phpc Discord. The team helps with triage, reproduction in isolated environments, severity scoring, and coordinated disclosure, and explicitly does not want to stand between a report and a fix.
Comments
No comments yet — be the first.
Open the discussion
No account or password needed — just enter your e-mail and we’ll send you a one-time sign-in link. First time here? You’re set up automatically.
Your rating will be applied automatically after you sign in.
Check your inbox
We’ve sent a sign-in link to …. Open it on this device — this tab will sign you in automatically.
Nothing arrived? Check your spam folder — and mark the mail as "Not spam" so it lands in your inbox next time.