VYPR
Medium severity5.6NVD Advisory· Published May 20, 2026· Updated May 20, 2026

Setup PHP: Command Injection in Repository-Derived PHP Version Resolution

CVE-2026-46420

Description

Summary

A command injection vulnerability was identified in shivammathur/setup-php when the action resolves the PHP version from repository-controlled files and uses that value while generating the platform setup script.

In affected versions, setup-php may read the PHP version from:

  • .php-version
  • composer.lock via platform-overrides.php
  • composer.json via config.platform.php

If an attacker can influence one of these files and the workflow executes setup-php in a trusted context, they may be able to execute commands on the GitHub Actions runner.

Impact

This issue is exploitable when setup-php is run after checking out attacker-controlled repository contents and resolves the PHP version from repository files.

The most significant example is a privileged workflow such as pull_request_target that checks out untrusted pull request code before invoking setup-php. Similar risk can also arise in other workflows that operate on attacker-controlled refs, branches, or repository contents in a trusted context.

This is not a separate security boundary when an attacker can already modify the workflow definition itself or directly control the php-version workflow input, since that level of access already permits arbitrary command execution in GitHub Actions.

Technical details

In affected versions, repository-derived PHP version values were insufficiently constrained before being incorporated into the generated shell or PowerShell setup script executed by the action. This could allow attacker-controlled values from supported repository files to influence script execution in trusted workflow contexts.

Remediation

If you are using shivammathur/setup-php@v2, no action is needed on your end. Users who pin the setup-php release version or release version SHA should upgrade to a patched version.

The fix validates PHP version inputs, constrains manifest-derived versions, hardens script generation at the execution, and includes additional checks in related input-handling paths.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Command injection in shivammathur/setup-php when PHP version is derived from attacker-controlled repository files in trusted workflow contexts.

Vulnerability

A command injection vulnerability exists in shivammathur/setup-php when the action resolves the PHP version from repository-controlled files such as .php-version, composer.lock (via platform-overrides.php), and composer.json (via config.platform.php) [1][3]. In affected versions, the derived version values are insufficiently constrained before being incorporated into the generated shell or PowerShell setup script, allowing an attacker to inject arbitrary commands [3]. All versions prior to the fix commit eeef37e059fb5368a5bc8ed8ce45ff54bd39b80b are vulnerable; users using the @v2 tag are safe [4].

Exploitation

An attacker must be able to influence the repository files that setup-php reads (for example, via a pull_request_target workflow that checks out untrusted pull request code) and the workflow must execute setup-php in a trusted context [3]. The attacker places a crafted PHP version string containing shell metacharacters in one of the supported files. When setup-php runs, it generates and executes a script using that value without proper escaping, leading to command injection [4].

Impact

Successful exploitation allows the attacker to execute arbitrary commands on the GitHub Actions runner with the privileges of the workflow [3]. This can lead to exfiltration of secrets, modification of build artifacts, or further lateral movement within the CI environment [3]. The impact is particularly severe in privileged workflows like pull_request_target that operate on untrusted inputs [4].

Mitigation

Users who pin a specific release version or SHA should upgrade to the patched version (commit eeef37e059fb5368a5bc8ed8ce45ff54bd39b80b or later) [2][4]. If using the @v2 tag, no action is required [4]. No workaround is available for vulnerable pinned versions; the fix includes validation of PHP version inputs, constraining manifest-derived versions, and hardening script generation [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Patches

1
eeef37e059fb

GHSA-pqwm-q9pv-ph8r - Fix CWE-78 [skip ci]

https://github.com/shivammathur/setup-phpShivam MathurMay 13, 2026via ghsa
8 files changed · +199 67
  • dist/index.js+1 1 modified
  • src/config.ts+5 2 modified
    @@ -16,7 +16,7 @@ export async function addINIValuesUnix(
       });
       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
       );
    @@ -37,7 +37,10 @@ export async function addINIValuesWindows(
           (await utils.addLog('$tick', line, 'Added to php.ini', 'win32')) + '\n';
       });
       return (
    -    'Add-Content "$php_dir\\php.ini" "' + ini_values.join('\n') + '"' + script
    +    'Add-Content "$php_dir\\php.ini" "' +
    +    ini_values.map(v => utils.escapeForShell(v, 'win32')).join('\n') +
    +    '"' +
    +    script
       );
     }
     
    
  • src/install.ts+5 2 modified
    @@ -18,7 +18,10 @@ export async function getScript(os: string): Promise<string> {
       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);
    @@ -28,7 +31,7 @@ export async function getScript(os: string): Promise<string> {
       const ini_file: string = await utils.parseIniFile(
         await utils.getInput('ini-file', false)
       );
    -  let script = await utils.joins('.', script_path, version, ini_file);
    +  let script = await utils.joins('.', script_path, `'${version}'`, ini_file);
       if (extension_csv) {
         script += await extensions.addExtension(extension_csv, version, os);
       }
    
  • src/tools.ts+4 7 modified
    @@ -231,7 +231,7 @@ export async function getVersion(
         case !!data.repository && major_minor_regex.test(data.version):
           return await getSemverVersion(data);
         default:
    -      return data.version.replace(/[><=^~]*/, '');
    +      return data.version.replace(/[^a-zA-Z0-9_.:@+,/-]/g, '');
       }
     }
     
    @@ -347,12 +347,9 @@ export async function addArchive(data: ToolData): Promise<string> {
     export async function addPackage(data: ToolData): Promise<string> {
       const command = await utils.getCommand(data.os, 'composer_tool');
       const parts: string[] = data.repository.split('/');
    -  const args: string = await utils.joins(
    -    parts[1],
    -    data.release,
    -    parts[0] + '/',
    -    data.scope
    -  );
    +  const args = [parts[1], data.release, parts[0] + '/', data.scope]
    +    .map(a => utils.safeArg(a, data.os))
    +    .join(' ');
       return command + args;
     }
     
    
  • src/utils.ts+75 27 modified
    @@ -62,15 +62,31 @@ export async function getManifestURLS(): Promise<string[]> {
      */
     export async function parseVersion(version: string): Promise<string> {
       switch (true) {
    +    case /^pre(-installed)?$/.test(version):
    +      return 'pre';
         case /^(latest|lowest|highest|nightly|master|\d+\.x)$/.test(version):
           for (const manifestURL of await getManifestURLS()) {
             const fetchResult = await fetch.fetch(manifestURL);
             if (fetchResult['data'] ?? false) {
    -          return JSON.parse(fetchResult['data'])[version];
    +          const resolved: string | undefined = JSON.parse(fetchResult['data'])[
    +            version
    +          ];
    +          if (resolved === undefined) {
    +            throw new Error(`Invalid PHP version: ${version.slice(0, 20)}`);
    +          }
    +          if (!/^\d+\.\d+$/.test(resolved)) {
    +            throw new Error(
    +              `Invalid PHP version in manifest: ${resolved.slice(0, 10)}`
    +            );
    +          }
    +          return resolved;
             }
           }
           throw new Error(`Could not fetch the PHP version manifest.`);
         default:
    +      if (!/^\d+(\.\d+){0,2}$/.test(version)) {
    +        throw new Error(`Invalid PHP version: ${version.slice(0, 20)}`);
    +      }
           switch (true) {
             case version.length > 1:
               return version.slice(0, 3);
    @@ -86,14 +102,11 @@ export async function parseVersion(version: string): Promise<string> {
      * @param ini_file
      */
     export async function parseIniFile(ini_file: string): Promise<string> {
    -  switch (true) {
    -    case /^(production|development|none)$/.test(ini_file):
    -      return ini_file;
    -    case /php\.ini-(production|development)$/.test(ini_file):
    -      return ini_file.split('-')[1];
    -    default:
    -      return 'production';
    +  if (/^(production|development|none)$/.test(ini_file)) {
    +    return ini_file;
       }
    +  const match = ini_file.match(/php\.ini-(production|development)$/);
    +  return match ? match[1] : 'production';
     }
     
     /**
    @@ -172,10 +185,10 @@ export async function log(
     export async function stepLog(message: string, os: string): Promise<string> {
       switch (os) {
         case 'win32':
    -      return 'Step-Log "' + message + '"';
    +      return 'Step-Log "' + escapeForShell(message, os) + '"';
         case 'linux':
         case 'darwin':
    -      return 'step_log "' + message + '"';
    +      return 'step_log "' + escapeForShell(message, os) + '"';
         default:
           return await log('Platform ' + os + ' is not supported', os, 'error');
       }
    @@ -194,17 +207,40 @@ export async function addLog(
       message: string,
       os: string
     ): Promise<string> {
    +  const sub = escapeForShell(subject, os);
    +  const msg = escapeForShell(message, os);
       switch (os) {
         case 'win32':
    -      return 'Add-Log "' + mark + '" "' + subject + '" "' + message + '"';
    +      return `Add-Log "${mark}" "${sub}" "${msg}"`;
         case 'linux':
         case 'darwin':
    -      return 'add_log "' + mark + '" "' + subject + '" "' + message + '"';
    +      return `add_log "${mark}" "${sub}" "${msg}"`;
         default:
           return await log('Platform ' + os + ' is not supported', os, 'error');
       }
     }
     
    +export function escapeForShell(value: string, os: string): string {
    +  if (os === 'win32') {
    +    return value.replace(/[`$"]/g, '`$&');
    +  }
    +  return value.replace(/[\\`$"]/g, '\\$&');
    +}
    +
    +export function safeArg(value: string, os: string): string {
    +  if (!/^[a-zA-Z0-9_./:@+,~^-]*$/.test(value)) {
    +    return '"' + escapeForShell(value, os) + '"';
    +  }
    +  return value;
    +}
    +
    +export function sanitizeShellInput(value: string, strict = false): string {
    +  const pattern = strict
    +    ? /[$`"';|&(){}[\]\\<>*?\n\r\t]/g
    +    : /[$`"';|&(){}[\]\\\n\r\t]/g;
    +  return value.replace(pattern, '');
    +}
    +
     /**
      * Function to break extension csv into an array
      *
    @@ -431,22 +467,35 @@ export async function parseExtensionSource(
       );
     }
     
    +const VERSION_INPUT_REGEX =
    +  /^(latest|lowest|highest|nightly|master|pre|pre-installed|\d+\.x|\d+(\.\d+){0,2})$/;
    +
    +function validatePHPVersionInput(version: string, source: string): string {
    +  if (!VERSION_INPUT_REGEX.test(version)) {
    +    throw new Error(
    +      `Invalid PHP version in ${source}: ${version.slice(0, 20)}`
    +    );
    +  }
    +  return version;
    +}
    +
     /**
      * Read php version from input or file
      */
     export async function readPHPVersion(): Promise<string> {
       const version = await getInput('php-version', false);
       if (version) {
    -    return version;
    +    return validatePHPVersionInput(version, 'php-version input');
       }
       const versionFile =
         (await getInput('php-version-file', false)) || '.php-version';
       if (fs.existsSync(versionFile)) {
         const contents: string = fs.readFileSync(versionFile, 'utf8');
    -    const match: RegExpMatchArray | null = contents.match(
    -      /^(?:php\s)?(\d+\.\d+\.\d+)$/m
    +    const match = contents.match(/^(?:php\s)?(\d+\.\d+\.\d+)$/m);
    +    return validatePHPVersionInput(
    +      match ? match[1] : contents.trim(),
    +      versionFile
         );
    -    return match ? match[1] : contents.trim();
       } else if (versionFile !== '.php-version') {
         throw new Error(`Could not find '${versionFile}' file.`);
       }
    @@ -456,11 +505,11 @@ export async function readPHPVersion(): Promise<string> {
       if (fs.existsSync(composerLock)) {
         const lockFileContents = JSON.parse(fs.readFileSync(composerLock, 'utf8'));
         /* istanbul ignore next */
    -    if (
    -      lockFileContents['platform-overrides'] &&
    -      lockFileContents['platform-overrides']['php']
    -    ) {
    -      return lockFileContents['platform-overrides']['php'];
    +    if (lockFileContents['platform-overrides']?.['php']) {
    +      return validatePHPVersionInput(
    +        lockFileContents['platform-overrides']['php'],
    +        'composer.lock platform-overrides.php'
    +      );
         }
       }
     
    @@ -470,12 +519,11 @@ export async function readPHPVersion(): Promise<string> {
           fs.readFileSync(composerJson, 'utf8')
         );
         /* istanbul ignore next */
    -    if (
    -      composerFileContents['config'] &&
    -      composerFileContents['config']['platform'] &&
    -      composerFileContents['config']['platform']['php']
    -    ) {
    -      return composerFileContents['config']['platform']['php'];
    +    if (composerFileContents['config']?.['platform']?.['php']) {
    +      return validatePHPVersionInput(
    +        composerFileContents['config']['platform']['php'],
    +        'composer.json config.platform.php'
    +      );
         }
       }
     
    
  • __tests__/config.test.ts+12 8 modified
    @@ -2,14 +2,18 @@ import * as config from '../src/config';
     
     describe('Config tests', () => {
       it.each`
    -    ini_values    | os           | output
    -    ${'a=b, c=d'} | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=b\nc=d"'}
    -    ${'a=b, c=d'} | ${'linux'}   | ${'echo "a=b\nc=d" | sudo tee -a "${pecl_file:-${ini_file[@]}}"'}
    -    ${'a=b, c=d'} | ${'darwin'}  | ${'echo "a=b\nc=d" | sudo tee -a "${pecl_file:-${ini_file[@]}}"'}
    -    ${'a=b & ~c'} | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=\'b & ~c\'"'}
    -    ${'a="~(b)"'} | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=\'~(b)\'"'}
    -    ${'a="b, c"'} | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=b, c"'}
    -    ${'a=b, c=d'} | ${'openbsd'} | ${'Platform openbsd is not supported'}
    +    ini_values                           | os           | output
    +    ${'a=b, c=d'}                        | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=b\nc=d"'}
    +    ${'a=b, c=d'}                        | ${'linux'}   | ${'echo "a=b\nc=d" | sudo tee -a "${pecl_file:-${ini_file[@]}}"'}
    +    ${'a=b, c=d'}                        | ${'darwin'}  | ${'echo "a=b\nc=d" | sudo tee -a "${pecl_file:-${ini_file[@]}}"'}
    +    ${'a=b & ~c'}                        | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=\'b & ~c\'"'}
    +    ${'a="~(b)"'}                        | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=\'~(b)\'"'}
    +    ${'a="b, c"'}                        | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=b, c"'}
    +    ${'disable_functions="exec,system"'} | ${'linux'}   | ${'echo "disable_functions=exec,system" | sudo tee -a'}
    +    ${'disable_functions="exec,system"'} | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "disable_functions=exec,system"'}
    +    ${'a=$(id)'}                         | ${'linux'}   | ${'echo "a=\'\\$(id)\'"'}
    +    ${'a=$(id)'}                         | ${'win32'}   | ${'Add-Content "$php_dir\\php.ini" "a=\'`$(id)\'"'}
    +    ${'a=b, c=d'}                        | ${'openbsd'} | ${'Platform openbsd is not supported'}
       `('checking addINIValues on $os', async ({ini_values, os, output}) => {
         expect(await config.addINIValues(ini_values, os)).toContain(output);
       });
    
  • __tests__/tools.test.ts+27 18 modified
    @@ -187,6 +187,7 @@ describe('Tools tests', () => {
         ${'1.2.3-dev'}     | ${'tool'}     | ${'phar'}     | ${'1.2.3-dev'}
         ${'1.2.3-alpha1'}  | ${'tool'}     | ${'phar'}     | ${'1.2.3-alpha1'}
         ${'1.2.3-alpha.1'} | ${'tool'}     | ${'phar'}     | ${'1.2.3-alpha.1'}
    +    ${'1.>=0'}         | ${'tool'}     | ${'phar'}     | ${'1.0'}
       `(
         'checking getVersion: $version, $tool, $type',
         async ({version, tool, type, expected}) => {
    @@ -304,22 +305,30 @@ describe('Tools tests', () => {
       });
     
       it.each`
    -    os           | script                                              | scope
    -    ${'linux'}   | ${'add_composer_tool tool tool:1.2.3 user/ global'} | ${'global'}
    -    ${'darwin'}  | ${'add_composer_tool tool tool:1.2.3 user/ scoped'} | ${'scoped'}
    -    ${'win32'}   | ${'Add-ComposerTool tool tool:1.2.3 user/ scoped'}  | ${'scoped'}
    -    ${'openbsd'} | ${'Platform openbsd is not supported'}              | ${'global'}
    -  `('checking addPackage: $os, $scope', async ({os, script, scope}) => {
    -    const data = getData({
    -      tool: 'tool',
    -      version: '1.2.3',
    -      repository: 'user/tool',
    -      os: os,
    -      scope: scope
    -    });
    -    data['release'] = [data['tool'], data['version']].join(':');
    -    expect(await tools.addPackage(data)).toContain(script);
    -  });
    +    os           | release           | scope       | script
    +    ${'linux'}   | ${'tool:1.2.3'}   | ${'global'} | ${'add_composer_tool tool tool:1.2.3 user/ global'}
    +    ${'darwin'}  | ${'tool:1.2.3'}   | ${'scoped'} | ${'add_composer_tool tool tool:1.2.3 user/ scoped'}
    +    ${'win32'}   | ${'tool:1.2.3'}   | ${'scoped'} | ${'Add-ComposerTool tool tool:1.2.3 user/ scoped'}
    +    ${'linux'}   | ${'tool:>=1.2'}   | ${'global'} | ${'add_composer_tool tool "tool:>=1.2" user/ global'}
    +    ${'win32'}   | ${'tool:>=1.2'}   | ${'global'} | ${'Add-ComposerTool tool "tool:>=1.2" user/ global'}
    +    ${'linux'}   | ${'tool:1.*'}     | ${'global'} | ${'add_composer_tool tool "tool:1.*" user/ global'}
    +    ${'linux'}   | ${'psalm:^5||^6'} | ${'global'} | ${'add_composer_tool tool "psalm:^5||^6" user/ global'}
    +    ${'linux'}   | ${'psalm:>=5,<6'} | ${'global'} | ${'add_composer_tool tool "psalm:>=5,<6" user/ global'}
    +    ${'openbsd'} | ${'tool:1.2.3'}   | ${'global'} | ${'Platform openbsd is not supported'}
    +  `(
    +    'checking addPackage: $os, $release',
    +    async ({os, release, scope, script}) => {
    +      const data = getData({
    +        tool: 'tool',
    +        version: '1.2.3',
    +        repository: 'user/tool',
    +        os,
    +        scope
    +      });
    +      data['release'] = release;
    +      expect(await tools.addPackage(data)).toContain(script);
    +    }
    +  );
     
       it.each`
         version     | php_version | os          | script
    @@ -651,7 +660,7 @@ describe('Tools tests', () => {
             'add_devtools phpize',
             'add_tool https://github.com/phpmd/phpmd/releases/latest/download/phpmd.phar phpmd "--version"',
             'add_tool https://github.com/phpspec/phpspec/releases/latest/download/phpspec.phar phpspec "-V"',
    -        'add_composer_tool phpunit-bridge phpunit-bridge:5.6.* symfony/ global',
    +        'add_composer_tool phpunit-bridge "phpunit-bridge:5.6.*" symfony/ global',
             'add_composer_tool phpunit-polyfills phpunit-polyfills:1.0.1 yoast/ global',
             'add_protoc 1.2.3',
             'add_tool https://github.com/vimeo/psalm/releases/latest/download/psalm.phar psalm "-v"',
    @@ -711,7 +720,7 @@ describe('Tools tests', () => {
             'Add-ComposerTool codeception codeception codeception/ global',
             'Add-ComposerTool prestissimo prestissimo hirak/ global',
             'Add-ComposerTool automatic-composer-prefetcher automatic-composer-prefetcher narrowspark/ global',
    -        'Add-ComposerTool phinx phinx:1.2.* robmorgan/ scoped',
    +        'Add-ComposerTool phinx "phinx:1.2.*" robmorgan/ scoped',
             'Add-ComposerTool phinx phinx:^1.2 robmorgan/ global',
             'Add-ComposerTool tool tool:1.2.3 user/ global',
             'Add-ComposerTool tool tool:~1.2 user/ global'
    
  • __tests__/utils.test.ts+70 2 modified
    @@ -40,7 +40,19 @@ describe('Utils tests', () => {
         expect(await utils.parseVersion('7')).toBe('7.0');
         expect(await utils.parseVersion('7.4')).toBe('7.4');
         expect(await utils.parseVersion('5.x')).toBe('5.6');
    -    expect(await utils.parseVersion('4.x')).toBe(undefined);
    +    expect(await utils.parseVersion('pre')).toBe('pre');
    +    expect(await utils.parseVersion('pre-installed')).toBe('pre');
    +    await expect(utils.parseVersion('4.x')).rejects.toThrow(
    +      'Invalid PHP version: 4.x'
    +    );
    +    await expect(utils.parseVersion('foo')).rejects.toThrow(
    +      'Invalid PHP version:'
    +    );
    +
    +    fetchSpy.mockResolvedValue({data: '{ "latest": "8.1.0" }'});
    +    await expect(utils.parseVersion('latest')).rejects.toThrow(
    +      'Invalid PHP version in manifest:'
    +    );
     
         fetchSpy.mockReset();
         fetchSpy.mockResolvedValueOnce({}).mockResolvedValueOnce({});
    @@ -56,6 +68,12 @@ describe('Utils tests', () => {
         expect(await utils.parseIniFile('none')).toBe('none');
         expect(await utils.parseIniFile('php.ini-production')).toBe('production');
         expect(await utils.parseIniFile('php.ini-development')).toBe('development');
    +    expect(await utils.parseIniFile('/etc/php.ini-production')).toBe(
    +      'production'
    +    );
    +    expect(await utils.parseIniFile('/a-b/php.ini-development')).toBe(
    +      'development'
    +    );
         expect(await utils.parseIniFile('invalid')).toBe('production');
       });
     
    @@ -91,6 +109,22 @@ describe('Utils tests', () => {
         ).toEqual(['apcu', 'mbstring', 'pdo_pgsql', 'posix', 'session']);
       });
     
    +  it('checking shell helpers', () => {
    +    expect(utils.escapeForShell('a$b`c\\d"e', 'linux')).toBe(
    +      'a\\$b\\`c\\\\d\\"e'
    +    );
    +    expect(utils.escapeForShell('a$b`c"d', 'win32')).toBe('a`$b``c`"d');
    +    expect(utils.safeArg('vendor-pkg/repo@v1.0.0', 'linux')).toBe(
    +      'vendor-pkg/repo@v1.0.0'
    +    );
    +    expect(utils.safeArg('phpcs:>=3.0', 'linux')).toBe('"phpcs:>=3.0"');
    +    expect(utils.safeArg('foo$bar', 'win32')).toBe('"foo`$bar"');
    +    expect(utils.sanitizeShellInput('foo;$(`ls`)bar')).toBe('foolsbar');
    +    expect(utils.sanitizeShellInput('vendor/foo:1.*', true)).toBe(
    +      'vendor/foo:1.'
    +    );
    +  });
    +
       it('checking INIArray', async () => {
         expect(await utils.CSVArray('a=1, b=2, c=3')).toEqual([
           'a=1',
    @@ -282,6 +316,9 @@ describe('Utils tests', () => {
         process.env['php-version'] = '8.2';
         expect(await utils.readPHPVersion()).toBe('8.2');
     
    +    process.env['php-version'] = 'pre-installed';
    +    expect(await utils.readPHPVersion()).toBe('pre-installed');
    +
         delete process.env['php-version-file'];
         delete process.env['php-version'];
     
    @@ -291,7 +328,7 @@ describe('Utils tests', () => {
     
         existsSync.mockReturnValue(true);
         readFileSync.mockReturnValue('setup-php');
    -    expect(await utils.readPHPVersion()).toBe('setup-php');
    +    await expect(utils.readPHPVersion()).rejects.toThrow('Invalid PHP version');
     
         existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true);
         readFileSync.mockReturnValue(
    @@ -312,6 +349,37 @@ describe('Utils tests', () => {
         readFileSync.mockClear();
       });
     
    +  it('readPHPVersion rejects unsupported values from each source', async () => {
    +    const existsSync = jest.spyOn(fs, 'existsSync').mockImplementation();
    +    const readFileSync = jest.spyOn(fs, 'readFileSync').mockImplementation();
    +
    +    process.env['php-version'] = 'bogus';
    +    await expect(utils.readPHPVersion()).rejects.toThrow('php-version input');
    +    delete process.env['php-version'];
    +
    +    existsSync.mockReturnValue(true);
    +    readFileSync.mockReturnValue('bogus');
    +    await expect(utils.readPHPVersion()).rejects.toThrow('.php-version');
    +
    +    existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true);
    +    readFileSync.mockReturnValue('{"platform-overrides":{"php":"bogus"}}');
    +    await expect(utils.readPHPVersion()).rejects.toThrow(
    +      'composer.lock platform-overrides.php'
    +    );
    +
    +    existsSync
    +      .mockReturnValueOnce(false)
    +      .mockReturnValueOnce(false)
    +      .mockReturnValueOnce(true);
    +    readFileSync.mockReturnValue('{"config":{"platform":{"php":"bogus"}}}');
    +    await expect(utils.readPHPVersion()).rejects.toThrow(
    +      'composer.json config.platform.php'
    +    );
    +
    +    existsSync.mockClear();
    +    readFileSync.mockClear();
    +  });
    +
       it('checking setVariable', async () => {
         let script: string = await utils.setVariable('var', 'command', 'linux');
         expect(script).toEqual('\nvar="$(command)"\n');
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

3

News mentions

0

No linked articles in our index yet.