Fix/command injection shell (#24170)

Co-authored-by: David Pierce <davidapierce@google.com>
This commit is contained in:
Horizon_Architect_07
2026-04-23 01:30:44 +05:30
committed by GitHub
parent 1c43deee07
commit 2a52611e71
3 changed files with 525 additions and 2 deletions
+116
View File
@@ -1020,3 +1020,119 @@ export async function* execStreaming(
prepared.cleanup?.();
}
}
export function detectCommandSubstitution(command: string): boolean {
const shell = getShellConfiguration().shell;
const isPowerShell =
typeof shell === 'string' &&
(shell.toLowerCase().includes('powershell') ||
shell.toLowerCase().includes('pwsh'));
if (isPowerShell) {
return detectPowerShellSubstitution(command);
}
return detectBashSubstitution(command);
}
function detectBashSubstitution(command: string): boolean {
let inSingleQuote = false;
let inDoubleQuote = false;
let i = 0;
while (i < command.length) {
const char = command[i];
if (char === "'" && !inDoubleQuote) {
inSingleQuote = !inSingleQuote;
i++;
continue;
}
if (char === '"' && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote;
i++;
continue;
}
if (inSingleQuote) {
i++;
continue;
}
if (char === '\\' && i + 1 < command.length) {
if (inDoubleQuote) {
const next = command[i + 1];
if (['$', '`', '"', '\\', '\n'].includes(next)) {
i += 2;
continue;
}
} else {
i += 2;
continue;
}
}
if (char === '$' && command[i + 1] === '(') {
return true;
}
if (
!inDoubleQuote &&
(char === '<' || char === '>') &&
command[i + 1] === '('
) {
return true;
}
if (char === '`') {
return true;
}
i++;
}
return false;
}
const POWERSHELL_KEYWORD_RE =
/\b(if|elseif|else|foreach|for|while|do|switch|try|catch|finally|until|trap|function|filter)(\s+[-\w]+)*\s*$/i;
function detectPowerShellSubstitution(command: string): boolean {
let inSingleQuote = false;
let inDoubleQuote = false;
let i = 0;
while (i < command.length) {
const char = command[i];
if (char === "'" && !inDoubleQuote) {
inSingleQuote = !inSingleQuote;
i++;
continue;
}
if (char === '"' && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote;
i++;
continue;
}
if (inSingleQuote) {
i++;
continue;
}
if (char === '`' && i + 1 < command.length) {
i += 2;
continue;
}
if (char === '$' && command[i + 1] === '(') {
return true;
}
if (!inDoubleQuote && char === '@' && command[i + 1] === '(') {
return true;
}
if (!inDoubleQuote && char === '(') {
const before = command.slice(0, i).trimEnd();
const prevChar = before[before.length - 1];
if (prevChar === '(') {
i++;
continue;
}
if (POWERSHELL_KEYWORD_RE.test(before)) {
i++;
continue;
}
return true;
}
i++;
}
return false;
}