fix: harden exec allowlist parsing

This commit is contained in:
Peter Steinberger
2026-02-02 16:53:09 -08:00
parent fff59da962
commit d1ecb46076
3 changed files with 77 additions and 0 deletions

View File

@@ -585,6 +585,11 @@ export type ExecCommandAnalysis = {
};
const DISALLOWED_PIPELINE_TOKENS = new Set([">", "<", "`", "\n", "\r", "(", ")"]);
const DOUBLE_QUOTE_ESCAPES = new Set(["\\", '"', "$", "`", "\n", "\r"]);
function isDoubleQuoteEscape(next: string | undefined): next is string {
return Boolean(next && DOUBLE_QUOTE_ESCAPES.has(next));
}
type IteratorAction = "split" | "skip" | "include" | { reject: string };
@@ -637,6 +642,21 @@ function iterateQuoteAware(
continue;
}
if (inDouble) {
if (ch === "\\" && isDoubleQuoteEscape(next)) {
buf += ch;
buf += next;
i += 1;
continue;
}
if (ch === "$" && next === "(") {
return { ok: false, reason: "unsupported shell token: $()" };
}
if (ch === "`") {
return { ok: false, reason: "unsupported shell token: `" };
}
if (ch === "\n" || ch === "\r") {
return { ok: false, reason: "unsupported shell token: newline" };
}
if (ch === '"') {
inDouble = false;
}
@@ -749,6 +769,12 @@ function tokenizeShellSegment(segment: string): string[] | null {
continue;
}
if (inDouble) {
const next = segment[i + 1];
if (ch === "\\" && isDoubleQuoteEscape(next)) {
buf += next;
i += 1;
continue;
}
if (ch === '"') {
inDouble = false;
} else {
@@ -1067,6 +1093,7 @@ function splitCommandChain(command: string): string[] | null {
for (let i = 0; i < command.length; i += 1) {
const ch = command[i];
const next = command[i + 1];
if (escaped) {
buf += ch;
escaped = false;
@@ -1085,6 +1112,12 @@ function splitCommandChain(command: string): string[] | null {
continue;
}
if (inDouble) {
if (ch === "\\" && isDoubleQuoteEscape(next)) {
buf += ch;
buf += next;
i += 1;
continue;
}
if (ch === '"') {
inDouble = false;
}