mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-14 05:18:34 +00:00
fix: harden allow-always shell multiplexer wrapper handling
This commit is contained in:
@@ -7,6 +7,7 @@ const WINDOWS_EXE_SUFFIX = ".exe";
|
||||
const POSIX_SHELL_WRAPPER_NAMES = ["ash", "bash", "dash", "fish", "ksh", "sh", "zsh"] as const;
|
||||
const WINDOWS_CMD_WRAPPER_NAMES = ["cmd"] as const;
|
||||
const POWERSHELL_WRAPPER_NAMES = ["powershell", "pwsh"] as const;
|
||||
const SHELL_MULTIPLEXER_WRAPPER_NAMES = ["busybox", "toybox"] as const;
|
||||
const DISPATCH_WRAPPER_NAMES = [
|
||||
"chrt",
|
||||
"doas",
|
||||
@@ -42,6 +43,7 @@ export const DISPATCH_WRAPPER_EXECUTABLES = new Set(withWindowsExeAliases(DISPAT
|
||||
const POSIX_SHELL_WRAPPER_CANONICAL = new Set<string>(POSIX_SHELL_WRAPPER_NAMES);
|
||||
const WINDOWS_CMD_WRAPPER_CANONICAL = new Set<string>(WINDOWS_CMD_WRAPPER_NAMES);
|
||||
const POWERSHELL_WRAPPER_CANONICAL = new Set<string>(POWERSHELL_WRAPPER_NAMES);
|
||||
const SHELL_MULTIPLEXER_WRAPPER_CANONICAL = new Set<string>(SHELL_MULTIPLEXER_WRAPPER_NAMES);
|
||||
const DISPATCH_WRAPPER_CANONICAL = new Set<string>(DISPATCH_WRAPPER_NAMES);
|
||||
const SHELL_WRAPPER_CANONICAL = new Set<string>([
|
||||
...POSIX_SHELL_WRAPPER_NAMES,
|
||||
@@ -133,6 +135,39 @@ function findShellWrapperSpec(baseExecutable: string): ShellWrapperSpec | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export type ShellMultiplexerUnwrapResult =
|
||||
| { kind: "not-wrapper" }
|
||||
| { kind: "blocked"; wrapper: string }
|
||||
| { kind: "unwrapped"; wrapper: string; argv: string[] };
|
||||
|
||||
export function unwrapKnownShellMultiplexerInvocation(
|
||||
argv: string[],
|
||||
): ShellMultiplexerUnwrapResult {
|
||||
const token0 = argv[0]?.trim();
|
||||
if (!token0) {
|
||||
return { kind: "not-wrapper" };
|
||||
}
|
||||
const wrapper = normalizeExecutableToken(token0);
|
||||
if (!SHELL_MULTIPLEXER_WRAPPER_CANONICAL.has(wrapper)) {
|
||||
return { kind: "not-wrapper" };
|
||||
}
|
||||
|
||||
let appletIndex = 1;
|
||||
if (argv[appletIndex]?.trim() === "--") {
|
||||
appletIndex += 1;
|
||||
}
|
||||
const applet = argv[appletIndex]?.trim();
|
||||
if (!applet || !isShellWrapperExecutable(applet)) {
|
||||
return { kind: "blocked", wrapper };
|
||||
}
|
||||
|
||||
const unwrapped = argv.slice(appletIndex);
|
||||
if (unwrapped.length === 0) {
|
||||
return { kind: "blocked", wrapper };
|
||||
}
|
||||
return { kind: "unwrapped", wrapper, argv: unwrapped };
|
||||
}
|
||||
|
||||
export function isEnvAssignment(token: string): boolean {
|
||||
return /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token);
|
||||
}
|
||||
@@ -474,6 +509,18 @@ function hasEnvManipulationBeforeShellWrapperInternal(
|
||||
);
|
||||
}
|
||||
|
||||
const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(argv);
|
||||
if (shellMultiplexerUnwrap.kind === "blocked") {
|
||||
return false;
|
||||
}
|
||||
if (shellMultiplexerUnwrap.kind === "unwrapped") {
|
||||
return hasEnvManipulationBeforeShellWrapperInternal(
|
||||
shellMultiplexerUnwrap.argv,
|
||||
depth + 1,
|
||||
envManipulationSeen,
|
||||
);
|
||||
}
|
||||
|
||||
const wrapper = findShellWrapperSpec(normalizeExecutableToken(token0));
|
||||
if (!wrapper) {
|
||||
return false;
|
||||
@@ -577,6 +624,14 @@ function extractShellWrapperCommandInternal(
|
||||
return extractShellWrapperCommandInternal(dispatchUnwrap.argv, rawCommand, depth + 1);
|
||||
}
|
||||
|
||||
const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(argv);
|
||||
if (shellMultiplexerUnwrap.kind === "blocked") {
|
||||
return { isWrapper: false, command: null };
|
||||
}
|
||||
if (shellMultiplexerUnwrap.kind === "unwrapped") {
|
||||
return extractShellWrapperCommandInternal(shellMultiplexerUnwrap.argv, rawCommand, depth + 1);
|
||||
}
|
||||
|
||||
const base0 = normalizeExecutableToken(token0);
|
||||
const wrapper = findShellWrapperSpec(base0);
|
||||
if (!wrapper) {
|
||||
|
||||
Reference in New Issue
Block a user