From a141910b3746bb3be43d4c06ba400a83c1b8b075 Mon Sep 17 00:00:00 2001 From: B <6723574+louisgv@users.noreply.github.com> Date: Sat, 28 Mar 2026 07:26:05 +0000 Subject: [PATCH] fix(security): harden env_b64 and b64_tmp expansion in provision.sh (#3078) Two defense-in-depth improvements to the manual .spawnrc fallback: 1. Validate b64_tmp (remote mktemp output) contains only safe path characters before interpolating it into subsequent cloud_exec calls. A compromised remote could return a crafted path with shell metacharacters. 2. Escape single quotes in env_b64 before interpolating into the remote command string. Base64 output never contains single quotes, but if the validation were ever bypassed, an unescaped quote could break out of the _B64='...' assignment on the remote side. Agent: ux-engineer Co-Authored-By: Claude Sonnet 4.5 --- sh/e2e/lib/provision.sh | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/sh/e2e/lib/provision.sh b/sh/e2e/lib/provision.sh index 39553fdc..cba1760a 100644 --- a/sh/e2e/lib/provision.sh +++ b/sh/e2e/lib/provision.sh @@ -324,24 +324,35 @@ CLOUD_ENV # Piping via stdin is NOT used because Sprite's exec driver replaces stdin # with the command pipe, causing piped data to be lost. - # Step 1: Create a temp file and write base64 data to it on the remote host. - # env_b64 is validated above to contain only [A-Za-z0-9+/=] (base64 alphabet), - # which cannot break out of single quotes or cause shell injection. - # The remote command re-validates the data as defense-in-depth. + # Step 1: Create a temp file on the remote host for the base64 payload. local b64_tmp b64_tmp=$(cloud_exec "${app_name}" "mktemp -t spawnrc.b64.XXXXXX" 2>/dev/null | tr -d '[:space:]') if [ -z "${b64_tmp}" ]; then log_err "Failed to create remote temp file for .spawnrc payload" return 1 fi - # Assign to remote variable and re-validate base64 on remote side before writing. - if ! cloud_exec "${app_name}" "_B64='${env_b64}'; printf '%s' \"\$_B64\" | grep -qE '^[A-Za-z0-9+/=]+$' && printf '%s' \"\$_B64\" > '${b64_tmp}' || exit 1" >/dev/null 2>&1; then + + # Validate b64_tmp contains only safe path characters (defense-in-depth). + # A compromised remote could return a crafted mktemp path with shell + # metacharacters that would be interpolated into subsequent cloud_exec calls. + if ! printf '%s' "${b64_tmp}" | grep -qE '^[A-Za-z0-9_./ -]+$'; then + log_err "SECURITY: Remote mktemp returned suspicious path — aborting" + return 1 + fi + + # Write env_b64 to the remote temp file. Defense-in-depth: escape any single + # quotes in env_b64 before interpolation (base64 cannot contain them, but this + # prevents injection if the validation above is ever bypassed or weakened). + # The remote command also re-validates the data before writing. + local env_b64_escaped + env_b64_escaped=$(printf '%s' "${env_b64}" | sed "s/'/'\\\\''/g") + if ! cloud_exec "${app_name}" "_B64='${env_b64_escaped}'; printf '%s' \"\$_B64\" | grep -qE '^[A-Za-z0-9+/=]+\$' && printf '%s' \"\$_B64\" > '${b64_tmp}' || exit 1" >/dev/null 2>&1; then log_err "Failed to write .spawnrc payload to remote temp file" return 1 fi # Step 2: Decode from the temp file and set up shell rc sourcing. - # The only interpolated variable is b64_tmp (a mktemp path, safe characters only). + # b64_tmp is validated above to contain only safe path characters. if cloud_exec "${app_name}" "base64 -d < '${b64_tmp}' > ~/.spawnrc && chmod 600 ~/.spawnrc && rm -f '${b64_tmp}' && \ for _rc in ~/.bashrc ~/.profile ~/.bash_profile; do \ grep -q 'source ~/.spawnrc' \"\$_rc\" 2>/dev/null || printf '%s\n' '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> \"\$_rc\"; done" >/dev/null 2>&1; then