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

This commit is contained in:
Shivam Mathur
2026-05-14 03:56:37 +05:30
parent 0dc33069a3
commit eeef37e059
8 changed files with 199 additions and 67 deletions
+75 -27
View File
@@ -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'
);
}
}