You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adapter.Port.open_cli_port/4 spawns the CLI binary by invoking /bin/sh -c with a concatenated command string. This requires shell escaping every component — environment variable values, the executable path, all CLI arguments, and the working directory — via a hand-rolled shell_escape/1 function.
Unnecessary indirection. Erlang's Port.open/2 with :spawn_executable natively supports :args (list of strings), :env (list of {charlist, charlist} tuples), and :cd (charlist) — all without touching a shell. The arguments are passed directly to execvp, bypassing shell interpretation entirely.
Extra process layer. The sh -c "exec ..." pattern spawns a shell process that then execs the CLI. Direct :spawn_executable avoids this.
Solution
Replace the sh -c invocation with direct Port.open({:spawn_executable, exe_path}, [{:args, args}, {:env, env_list}, {:cd, cwd}, ...]). This eliminates build_shell_command/4, shell_escape/1, and @shell_safe_pattern entirely.
Problem
Adapter.Port.open_cli_port/4spawns the CLI binary by invoking/bin/sh -cwith a concatenated command string. This requires shell escaping every component — environment variable values, the executable path, all CLI arguments, and the working directory — via a hand-rolledshell_escape/1function.This approach has several drawbacks:
Shell escaping is inherently fragile. The original implementation used an allowlist of 12 dangerous characters and missed
!,#,<,>,?,[,],{,},*,~, tab (fixed in shell_escape/1 fails to quote strings with certain shell-special characters guess/claude_code#38/PR Fix shell_escape to quote all non-safe characters guess/claude_code#39 by inverting to a safelist). Even with the safelist fix, any escaping logic is a potential source of bugs.Unnecessary indirection. Erlang's
Port.open/2with:spawn_executablenatively supports:args(list of strings),:env(list of{charlist, charlist}tuples), and:cd(charlist) — all without touching a shell. The arguments are passed directly toexecvp, bypassing shell interpretation entirely.Extra process layer. The
sh -c "exec ..."pattern spawns a shell process that thenexecs the CLI. Direct:spawn_executableavoids this.Solution
Replace the
sh -cinvocation with directPort.open({:spawn_executable, exe_path}, [{:args, args}, {:env, env_list}, {:cd, cwd}, ...]). This eliminatesbuild_shell_command/4,shell_escape/1, and@shell_safe_patternentirely.Branch
refactor/direct-spawn-executable