





























Update May 13th: GitHub has temporarily rolled back the new token format rollout. According to the Composer maintainers, that leaves a few days to update Composer in CI before the rollout resumes next week.
Composer maintainers released emergency patches today for a vulnerability that prints raw GitHub tokens into CI job logs. The fix ships in 2.9.8, 2.2.28 (LTS), and 1.10.28 (legacy). Every PHP CI pipeline running an older Composer with shivammathur/setup-php (or any action that auto-registers GITHUB_TOKEN) is exposed.
The leaked credential is the GITHUB_TOKEN from whatever workflow ran Composer. For workflows triggered by push, schedule, or pull_request_target, that token may have write access to the source repository, depending on the repository's default permissions.
Composer validates GitHub OAuth tokens loaded from auth.json against a regex. The regex was added in 2021 and accepts only the characters [.A-Za-z0-9_]. GitHub's token formats at the time contained no hyphens, so every valid token passed.
GitHub later rolled out a new structured token format for GitHub Actions: ghs_<id>_<JWT>. The announcement stated:
This should not impact your existing Actions workflows.
The JWT segment is base64url-encoded, which often includes hyphens. The new tokens fail Composer's regex on the first hyphen they contain.
When validation fails, Composer interpolates the raw token directly into the exception message:
if (!Preg::isMatch('{^[.A-Za-z0-9_]+$}', $token)) {
throw new \UnexpectedValueException(
'Your github oauth token for '.$domain.
' contains invalid characters: "'.$token.'"'
);
}
Symfony Console renders the unhandled exception to stderr, dropping the full token into the CI job log.
In CI, the common entry point is shivammathur/setup-php, used by tens of thousands of public workflows. The action writes GITHUB_TOKEN into Composer's global auth.json with no opt-in required, so the next composer invocation triggers the failed validation and dumps the token.
GitHub Actions scans each log line for the registered secret bytes and replaces matches with ***. The matcher only catches contiguous copies of the token.
Symfony Console's exception renderer interleaves ANSI color escape sequences through the output and wraps long lines at the terminal width, so the token never appears contiguously in a single write. In archived runs the boxed ##[error] annotation shows ***, while the raw stderr line directly below it prints the token in full.
The leaked token's permissions depend on the repository's GITHUB_TOKEN default. GitHub flipped that default to read-only on February 2, 2023, but only for newly created organizations and personal-account repositories. The announcement is explicit:
This change will not impact any existing enterprises, organizations or repositories.
Any project predating the change continues to issue read/write tokens unless an admin has manually tightened the setting.
GitHub's documentation states the GITHUB_TOKEN "expires when the job finishes or after its effective maximum lifetime." The Composer security advisory repeats the claim, describing tokens that "expire when the job ends."
The leaked credential is a JWT with a 1-hour signed expiry. Once the job completes, GitHub revokes write-scoped tokens within seconds. Sansec and the original researcher independently verified that this revocation can often be raced: a token harvested while the job is still in_progress is accepted by GitHub's API. In Sansec's testing, a window of under 2 seconds was enough to capture the token and push a commit. For workflows that do other work after Composer (test, build, deploy), that window can be minutes.
Composer pulls dependencies from Packagist, which mirrors releases from GitHub repositories. A maintainer who publishes a popular library typically has a CI workflow that runs Composer on every push, and a scheduled workflow that runs nightly. If those workflows use shivammathur/setup-php, the maintainer's GITHUB_TOKEN lands in the job log.
For a public repository, the log is public. An attacker who harvests a write-scoped token from a push or schedule run on a widely depended-on library can push a malicious commit and tag a release. Downstream CI pipelines running composer update pull the compromised code within minutes.
Update Composer: do not pin a specific Composer version in your CI workflows. shivammathur/setup-php installs the latest stable Composer by default, which now contains the fix and will pick up future patches automatically.
Drop default token permissions to read-only: at the organization or repository level, set the default GITHUB_TOKEN permission to read. Grant write only where the workflow needs it. This neutralizes the leak for every workflow that does not need to push.
Freeze dependencies: avoid pulling new package versions until the broader impact is assessed.
| Date | Event |
|---|---|
| 2021 | Token validation regex added to Composer (excludes hyphen) |
| May 12, 2026 | Damien Retzinger discovers the leak during incident triage |
| May 12, 2026 | Reported privately to Composer maintainers and GitHub PSIRT |
| May 12, 2026 | Reported as a public issue by GitHub user kesselb |
| May 13, 2026 | Composer 2.9.8, 2.2.28, and 1.10.28 released. GHSA-f9f8-rm49-7jv2 published |
| May 13, 2026 | Sansec publishes this advisory |
| May 13, 2026 | GitHub temporarily rolls back the new token format |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。