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

Co-Authored-By: maki <amarrec@quarkslab.com>
This commit is contained in:
Shivam Mathur
2026-05-14 03:56:37 +05:30
parent 6eb42c595e
commit 10e197b409
11 changed files with 237 additions and 32 deletions

View File

@@ -50,12 +50,37 @@ describe('Utils tests', () => {
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it('parseVersion rejects unsupported inputs', async () => {
await expect(utils.parseVersion('foo')).rejects.toThrow(
'Invalid PHP version:'
);
await expect(utils.parseVersion('8.x.1')).rejects.toThrow(
'Invalid PHP version:'
);
});
it('parseVersion rejects unexpected manifest values', async () => {
const fetchSpy = jest
.spyOn(fetchModule, 'fetch')
.mockResolvedValue({data: '{ "latest": "8.1.0" }'});
await expect(utils.parseVersion('latest')).rejects.toThrow(
'Invalid PHP version in manifest:'
);
fetchSpy.mockRestore();
});
it('checking parseIniFile', async () => {
expect(await utils.parseIniFile('production')).toBe('production');
expect(await utils.parseIniFile('development')).toBe('development');
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 +116,40 @@ describe('Utils tests', () => {
).toEqual(['apcu', 'mbstring', 'pdo_pgsql', 'posix', 'session']);
});
it('checking escapeForShell', () => {
expect(utils.escapeForShell('plain', 'linux')).toBe('plain');
expect(utils.escapeForShell('a"b', 'linux')).toBe('a\\"b');
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');
});
it('checking safeArg', () => {
expect(utils.safeArg('plain', 'linux')).toBe('plain');
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('vendor/foo:1.*', 'linux')).toBe('"vendor/foo:1.*"');
expect(utils.safeArg('foo$bar', 'linux')).toBe('"foo\\$bar"');
expect(utils.safeArg('foo$bar', 'win32')).toBe('"foo`$bar"');
expect(utils.safeArg('', 'linux')).toBe('');
});
it('checking sanitizeShellInput', () => {
expect(utils.sanitizeShellInput('mbstring, intl')).toBe('mbstring, intl');
expect(utils.sanitizeShellInput('foo;ls;bar')).toBe('foolsbar');
expect(utils.sanitizeShellInput('foo$(id)bar')).toBe('fooidbar');
expect(utils.sanitizeShellInput('a`b`c\\d"e\'f')).toBe('abcdef');
expect(utils.sanitizeShellInput('vendor/foo:1.*', true)).toBe(
'vendor/foo:1.'
);
expect(utils.sanitizeShellInput('vendor/foo@feat?ure', true)).toBe(
'vendor/foo@feature'
);
});
it('checking INIArray', async () => {
expect(await utils.CSVArray('a=1, b=2, c=3')).toEqual([
'a=1',
@@ -291,7 +350,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 +371,45 @@ 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(
'Invalid PHP version in php-version input'
);
delete process.env['php-version'];
existsSync.mockReturnValue(true);
readFileSync.mockReturnValue('bogus');
await expect(utils.readPHPVersion()).rejects.toThrow(
'Invalid PHP version in .php-version'
);
existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true);
readFileSync.mockReturnValue(
JSON.stringify({'platform-overrides': {php: 'bogus'}})
);
await expect(utils.readPHPVersion()).rejects.toThrow(
'Invalid PHP version in composer.lock platform-overrides.php'
);
existsSync
.mockReturnValueOnce(false)
.mockReturnValueOnce(false)
.mockReturnValueOnce(true);
readFileSync.mockReturnValue(
JSON.stringify({config: {platform: {php: 'bogus'}}})
);
await expect(utils.readPHPVersion()).rejects.toThrow(
'Invalid PHP version in 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');