CVE-2026-46420 Overview
CVE-2026-46420 is a command injection vulnerability [CWE-78] affecting shivammathur/setup-php, a widely used GitHub Action that configures PHP with extensions, php.ini settings, coverage drivers, and tools. Versions from 2.25.0 through 2.37.0 resolve the PHP version from repository-controlled files including .php-version, composer.lock (via platform-overrides.php), and composer.json (via config.platform.php). The action fails to sufficiently constrain these values before embedding them into generated shell or PowerShell setup scripts. Attackers who influence repository contents can inject commands executed on the GitHub Actions runner. The issue is fixed in version 2.37.1.
Critical Impact
Workflows using triggers such as pull_request_target that check out attacker-controlled contents before invoking setup-php can execute arbitrary commands on the runner, exposing repository secrets and workflow tokens.
Affected Products
- shivammathur/setup-php GitHub Action versions 2.25.0 through 2.37.0
- GitHub Actions workflows invoking setup-php after checking out untrusted content
- CI/CD pipelines using pull_request_target or similar privileged triggers with setup-php
Discovery Timeline
- 2026-07-17 - CVE-2026-46420 published to NVD
- 2026-07-23 - Last updated in NVD database
Technical Details for CVE-2026-46420
Vulnerability Analysis
The setup-php action reads the desired PHP version from repository files rather than solely from the workflow input. When the workflow checks out attacker-controlled code before running the action, values from .php-version, composer.lock, or composer.json propagate into shell command strings without adequate escaping. The generated Bash or PowerShell setup script then interprets injected metacharacters as commands. This is a classic OS command injection pattern where untrusted input reaches a shell interpreter unsanitized.
Root Cause
The action concatenates version and extension identifiers directly into command strings executed by bash on Linux/macOS or PowerShell on Windows. Inputs such as ini_values and extensions were passed through without shell-safe escaping. The upstream fix introduces utils.sanitizeShellInput and utils.escapeForShell helpers to neutralize shell metacharacters before interpolation.
Attack Vector
An attacker submits a pull request that modifies .php-version, composer.json, or composer.lock to embed shell metacharacters within the PHP version string. When a maintainer's workflow uses pull_request_target and checks out the pull request head prior to invoking shivammathur/setup-php, the malicious value flows into the generated setup script. Execution occurs under the runner's identity with access to GITHUB_TOKEN and any secrets exposed to the job.
// Patch excerpt: src/install.ts (version 2.37.1)
const filename = os + (await utils.scriptExtension(os));
const script_path = path.join(__dirname, '../src/scripts', filename);
const run_path = script_path.replace(os, 'run');
- const extension_csv: string = await utils.getInput('extensions', false);
+ const extension_csv: string = utils.sanitizeShellInput(
+ await utils.getInput('extensions', false),
+ true
+ );
const ini_values_csv: string = await utils.getInput('ini-values', false);
const coverage_driver: string = await utils.getInput('coverage', false);
const tools_csv: string = await utils.getInput('tools', false);
// Patch excerpt: src/config.ts (version 2.37.1)
});
return (
'echo "' +
- ini_values.join('\n') +
+ ini_values.map(v => utils.escapeForShell(v, 'linux')).join('\n') +
'" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null 2>&1' +
script
);
// Source: https://github.com/shivammathur/setup-php/commit/eeef37e059fb5368a5bc8ed8ce45ff54bd39b80b
The patch wraps user-controllable inputs with sanitizeShellInput and applies per-platform shell escaping to ini_values before concatenation into the echo command piped to sudo tee.
Detection Methods for CVE-2026-46420
Indicators of Compromise
- Unexpected outbound network connections from github.com-hosted or self-hosted runners during setup-php steps
- Modifications to .php-version, composer.json, or composer.lock in pull requests that contain shell metacharacters such as `, $(, ;, or &&
- Runner logs showing setup-php failures or unusual subprocess spawns during PHP version resolution
- Access or exfiltration of GITHUB_TOKEN, secrets, or environment variables from within a setup-php job
Detection Strategies
- Audit repositories for workflows that combine pull_request_target with actions/checkout of the pull request head followed by shivammathur/setup-php
- Scan pull request diffs for suspicious content in .php-version, composer.jsonconfig.platform.php, and composer.lockplatform-overrides.php
- Enforce dependency pinning checks that flag shivammathur/setup-php references below version 2.37.1
Monitoring Recommendations
- Enable GitHub Actions audit logs and forward runner telemetry to a centralized data lake for query-based hunting
- Alert on child processes spawned by setup-php that are not part of expected PHP installation flows
- Track outbound DNS and HTTP requests from runners during setup-php execution to detect data exfiltration
How to Mitigate CVE-2026-46420
Immediate Actions Required
- Upgrade all workflow references from shivammathur/setup-php@v2 or earlier pins to shivammathur/setup-php@2.37.1 or a commit SHA at or after the fix
- Review pull_request_target workflows and remove checkout of untrusted pull request contents before privileged action execution
- Rotate any secrets that may have been exposed to workflows using vulnerable versions with untrusted contributor input
Patch Information
The fix is available in setup-php release 2.37.1. Details are documented in GitHub Security Advisory GHSA-pqwm-q9pv-ph8r and the remediation commit. Pin the action to the release tag or the specific commit SHA to ensure integrity.
Workarounds
- Replace pull_request_target with pull_request where privileged secrets are not required
- Explicitly set the php-version input in the workflow rather than allowing resolution from repository files
- Remove or ignore .php-version, platform-overrides.php, and config.platform.php when the source is untrusted
# Pin setup-php to the patched version in .github/workflows/ci.yml
- name: Setup PHP
uses: shivammathur/setup-php@2.37.1
with:
php-version: '8.3'
extensions: mbstring, intl
ini-values: memory_limit=512M
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

