CVE-2026-59947 Overview
CVE-2026-59947 is an information disclosure vulnerability in Composer, the widely used dependency manager for PHP. When Composer runs with the -vvv debug verbosity flag, it can print credentials embedded in the username slot of a repository or package URL to debug output. This affects URLs of the form https://TOKEN@host/, such as those containing a GitHub Personal Access Token. The AuthHelper, Url::sanitize, and ProcessExecutor components did not sanitize username-only URL credentials before logging them. The issue is fixed in Composer versions 2.2.29 and 2.10.2.
Critical Impact
Sensitive authentication tokens embedded in repository URLs may be written to debug logs, CI/CD build output, or terminals, exposing them to anyone with read access to those logs.
Affected Products
- Composer versions prior to 2.2.29 (2.2.x branch)
- Composer versions prior to 2.10.2 (2.x branch)
- PHP projects and CI/CD pipelines that invoke Composer with -vvv verbosity
Discovery Timeline
- 2026-07-08 - CVE-2026-59947 published to NVD
- 2026-07-08 - Last updated in NVD database
Technical Details for CVE-2026-59947
Vulnerability Analysis
Composer supports authenticated repository access using URL-embedded credentials. Users can supply a token or username-only credential as https://TOKEN@host/path. When run with maximum verbosity (-vvv), Composer emits detailed debug output that includes URLs and authentication display messages.
The sanitization routines AuthHelper, Url::sanitize, and ProcessExecutor masked the user:password form but did not strip credentials placed only in the username slot. As a result, a GitHub Personal Access Token, GitLab CI token, or similar secret embedded as the URL username was written to debug output verbatim. This condition is classified as insertion of sensitive information into log files [CWE-532].
Root Cause
The sanitization logic assumed credentials always appear as username:password pairs. Username-only URL credentials, common for bearer-style tokens, bypassed the masking checks in Url::sanitize() and were echoed in the AuthHelper display message that reports the authentication method being used.
Attack Vector
Exploitation requires local access to Composer output, log files, or CI/CD build artifacts. An attacker who reads debug logs from a shared build system, an unprotected CI job, or a compromised developer workstation can extract the leaked token and reuse it against the target repository host. No network attack vector or user interaction is required beyond obtaining log access.
// Patch: src/Composer/Util/AuthHelper.php
// The display message now routes the username through Url::sanitizeUsername()
// before being printed to debug output.
} else {
$authStr = base64_encode($auth['username'] . ':' . $auth['password']);
$headers[] = 'Authorization: Basic '.$authStr;
- $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"';
+ $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . Url::sanitizeUsername((string) $auth['username']) . '"';
}
if ($authenticationDisplayMessage && (!isset($this->displayedOriginAuthentications[$origin]) || $this->displayedOriginAuthentications[$origin] !== $authenticationDisplayMessage)) {
Source: Composer commit 6bd66874
// Patch: src/Composer/Util/Git.php
// The hard-coded list of non-secret credential names is replaced with the
// centralized Url::NON_SECRET_CREDENTIALS constant so masking stays consistent.
$maskedCredentials = array();
foreach ($credentials as $credential) {
- if (in_array($credential, array('private-token', 'x-token-auth', 'oauth2', 'gitlab-ci-token', 'x-oauth-basic'))) {
+ if (in_array($credential, Url::NON_SECRET_CREDENTIALS, true)) {
$maskedCredentials[] = $credential;
} elseif (strlen($credential) > 6) {
$maskedCredentials[] = substr($credential, 0, 3) . '...' . substr($credential, -3);
Source: Composer commit 8887ad76
Detection Methods for CVE-2026-59947
Indicators of Compromise
- Composer debug output containing strings such as Using HTTP basic authentication with username "ghp_..." or other token-shaped values in the username field.
- CI/CD job logs referencing repository URLs in the form https://<token>@github.com/ or https://<token>@gitlab.com/.
- Build artifacts, cached console output, or archived pipeline logs containing high-entropy strings adjacent to Composer verbosity banners.
Detection Strategies
- Scan CI/CD log archives and developer workstation shell histories for Composer invocations that combine composer with -vvv and URLs containing an @ in the authority component.
- Grep repository URLs and log stores for known token prefixes (for example ghp_, github_pat_, glpat-) appearing before an @ character.
- Audit installed Composer versions across build agents and developer laptops using composer --version and compare against fixed releases 2.2.29 and 2.10.2.
Monitoring Recommendations
- Enable secret-scanning on internal log aggregation systems and Git repositories to catch tokens leaked by past debug runs.
- Alert on Composer processes launched with -vvv or --verbose --verbose --verbose in production or shared CI runners where debug verbosity should not be routine.
- Track PAT and CI token usage server-side (GitHub, GitLab, private registries) for anomalous source IPs or user agents that could indicate reuse of an exposed token.
How to Mitigate CVE-2026-59947
Immediate Actions Required
- Upgrade Composer to 2.2.29 on the 2.2.x LTS branch or to 2.10.2 on the current branch across all developer machines, container images, and CI/CD runners.
- Rotate any GitHub Personal Access Tokens, GitLab CI tokens, or private registry credentials that were ever embedded in a repository URL passed to Composer with debug verbosity enabled.
- Purge historical CI/CD logs, build artifacts, and cached console output that may contain leaked tokens, or restrict their access to a minimal audience.
Patch Information
The maintainers released fixes in Composer 2.2.29 and Composer 2.10.2. Technical details are documented in GitHub Security Advisory GHSA-g6xq-892h-64w3. The fixes introduce Url::sanitizeUsername() and centralize non-secret credential names in Url::NON_SECRET_CREDENTIALS, ensuring AuthHelper, Url::sanitize, and ProcessExecutor mask username-only credentials before writing them to output.
Workarounds
- Avoid running Composer with -vvv in shared environments or CI/CD pipelines until the upgrade is applied.
- Migrate away from URL-embedded credentials by using composer config to store authentication in auth.json or the COMPOSER_AUTH environment variable, which are not printed in debug output.
- Restrict access to Composer log output and CI artifacts on a least-privilege basis while remediation is in progress.
# Upgrade Composer to a fixed release
composer self-update 2.10.2
# or, for the 2.2 LTS branch
composer self-update 2.2.29
# Move credentials out of URLs and into auth.json
composer config --global --auth github-oauth.github.com <NEW_TOKEN>
composer config --global --auth gitlab-token.gitlab.com <NEW_TOKEN>
# Verify the installed version
composer --version
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

