From 0f1a938a3eecb6d033f6d75aff23370aa7a501cc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 13:33:46 +0100 Subject: [PATCH] refactor: share shell wrapper traversal --- src/infra/shell-wrapper-resolution.ts | 126 +++++++++++--------------- 1 file changed, 54 insertions(+), 72 deletions(-) diff --git a/src/infra/shell-wrapper-resolution.ts b/src/infra/shell-wrapper-resolution.ts index 59e433a6ebb..18cd81748f8 100644 --- a/src/infra/shell-wrapper-resolution.ts +++ b/src/infra/shell-wrapper-resolution.ts @@ -57,46 +57,75 @@ export type ShellWrapperCommand = { command: string | null; }; -function resolveShellWrapperSpecAndArgvInternal( - argv: string[], - depth: number, -): { argv: string[]; wrapper: ShellWrapperSpec; payload: string } | null { - if (!isWithinDispatchClassificationDepth(depth)) { +type ShellWrapperCandidate = { + argv: string[]; + token0: string; + state: TState; +}; + +function resolveShellWrapperCandidate(params: { + argv: string[]; + depth: number; + state: TState; + onDispatchUnwrap?: (state: TState, wrappedArgv: string[]) => TState; +}): ShellWrapperCandidate | null { + if (!isWithinDispatchClassificationDepth(params.depth)) { return null; } - const token0 = argv[0]?.trim(); + const token0 = params.argv[0]?.trim(); if (!token0) { return null; } - const dispatchUnwrap = unwrapKnownDispatchWrapperInvocation(argv); + const dispatchUnwrap = unwrapKnownDispatchWrapperInvocation(params.argv); if (dispatchUnwrap.kind === "blocked") { return null; } if (dispatchUnwrap.kind === "unwrapped") { - return resolveShellWrapperSpecAndArgvInternal(dispatchUnwrap.argv, depth + 1); + return resolveShellWrapperCandidate({ + ...params, + argv: dispatchUnwrap.argv, + depth: params.depth + 1, + state: params.onDispatchUnwrap?.(params.state, params.argv) ?? params.state, + }); } - const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(argv); + const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(params.argv); if (shellMultiplexerUnwrap.kind === "blocked") { return null; } if (shellMultiplexerUnwrap.kind === "unwrapped") { - return resolveShellWrapperSpecAndArgvInternal(shellMultiplexerUnwrap.argv, depth + 1); + return resolveShellWrapperCandidate({ + ...params, + argv: shellMultiplexerUnwrap.argv, + depth: params.depth + 1, + }); } - const wrapper = findShellWrapperSpec(normalizeExecutableToken(token0)); + return { argv: params.argv, token0, state: params.state }; +} + +function resolveShellWrapperSpecAndArgvInternal( + argv: string[], + depth: number, +): { argv: string[]; wrapper: ShellWrapperSpec; payload: string } | null { + const candidate = resolveShellWrapperCandidate({ argv, depth, state: null }); + if (!candidate) { + return null; + } + + const wrapper = findShellWrapperSpec(normalizeExecutableToken(candidate.token0)); if (!wrapper) { return null; } - const payload = extractShellWrapperPayload(argv, wrapper); + const payload = extractShellWrapperPayload(candidate.argv, wrapper); if (!payload) { return null; } - return { argv, wrapper, payload }; + return { argv: candidate.argv, wrapper, payload }; } function isWithinDispatchClassificationDepth(depth: number): boolean { @@ -108,31 +137,8 @@ export function isShellWrapperExecutable(token: string): boolean { } function isShellWrapperInvocationInternal(argv: string[], depth: number): boolean { - if (!isWithinDispatchClassificationDepth(depth)) { - return false; - } - const token0 = argv[0]?.trim(); - if (!token0) { - return false; - } - - const dispatchUnwrap = unwrapKnownDispatchWrapperInvocation(argv); - if (dispatchUnwrap.kind === "blocked") { - return false; - } - if (dispatchUnwrap.kind === "unwrapped") { - return isShellWrapperInvocationInternal(dispatchUnwrap.argv, depth + 1); - } - - const shellMultiplexerUnwrap = unwrapKnownShellMultiplexerInvocation(argv); - if (shellMultiplexerUnwrap.kind === "blocked") { - return false; - } - if (shellMultiplexerUnwrap.kind === "unwrapped") { - return isShellWrapperInvocationInternal(shellMultiplexerUnwrap.argv, depth + 1); - } - - return isShellWrapperExecutable(token0); + const candidate = resolveShellWrapperCandidate({ argv, depth, state: null }); + return candidate ? isShellWrapperExecutable(candidate.token0) : false; } export function isShellWrapperInvocation(argv: string[]): boolean { @@ -235,49 +241,25 @@ function hasEnvManipulationBeforeShellWrapperInternal( depth: number, envManipulationSeen: boolean, ): boolean { - if (!isWithinDispatchClassificationDepth(depth)) { + const candidate = resolveShellWrapperCandidate({ + argv, + depth, + state: envManipulationSeen, + onDispatchUnwrap: (state, wrappedArgv) => state || hasDispatchEnvManipulation(wrappedArgv), + }); + if (!candidate) { return false; } - const token0 = argv[0]?.trim(); - if (!token0) { - return false; - } - - const dispatchUnwrap = unwrapKnownDispatchWrapperInvocation(argv); - if (dispatchUnwrap.kind === "blocked") { - return false; - } - if (dispatchUnwrap.kind === "unwrapped") { - const nextEnvManipulationSeen = envManipulationSeen || hasDispatchEnvManipulation(argv); - return hasEnvManipulationBeforeShellWrapperInternal( - dispatchUnwrap.argv, - depth + 1, - nextEnvManipulationSeen, - ); - } - - 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)); + const wrapper = findShellWrapperSpec(normalizeExecutableToken(candidate.token0)); if (!wrapper) { return false; } - const payload = extractShellWrapperPayload(argv, wrapper); + const payload = extractShellWrapperPayload(candidate.argv, wrapper); if (!payload) { return false; } - return envManipulationSeen; + return candidate.state; } export function hasEnvManipulationBeforeShellWrapper(argv: string[]): boolean {