From 54ef5e451a1e9fdc481eabade0432fa9bd678f34 Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:55:16 -0800 Subject: [PATCH] fix: Prevent command injection via env var values in Koyeb and Hyperstack scripts (#196) Koyeb's inject_env_vars used sed escaping that didn't handle single quotes, allowing API key values containing ' to break out of the shell command string passed to `koyeb instances exec`. Replace with file-based injection using generate_env_config + upload_file, matching the safe pattern in shared/common.sh. Hyperstack goose/gemini/interpreter/codex scripts embedded $OPENROUTER_API_KEY directly in double-quoted command strings passed to run_server (SSH). Values containing double quotes, backticks, or $() could execute arbitrary commands on the remote VM. Replace with inject_env_vars_ssh which writes env vars to a temp file, uploads via SCP, and appends to shell config without interpolation. Also hardens Koyeb upload_file to reject remote paths containing shell metacharacters (', $, `, newline). Agent: security-auditor Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- hyperstack/codex.sh | 11 +++++------ hyperstack/gemini.sh | 13 ++++++------- hyperstack/goose.sh | 6 ++++-- hyperstack/interpreter.sh | 11 +++++------ koyeb/lib/common.sh | 32 +++++++++++++++++++++----------- 5 files changed, 41 insertions(+), 32 deletions(-) diff --git a/hyperstack/codex.sh b/hyperstack/codex.sh index a2f71f81..e955e337 100644 --- a/hyperstack/codex.sh +++ b/hyperstack/codex.sh @@ -34,11 +34,10 @@ else fi log_warn "Setting up environment variables..." -run_server "$HYPERSTACK_VM_IP" "cat >> ~/.bashrc << 'EOF' -export OPENROUTER_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_BASE_URL=https://openrouter.ai/api/v1 -EOF" +inject_env_vars_ssh "$HYPERSTACK_VM_IP" upload_file run_server \ + "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_BASE_URL=https://openrouter.ai/api/v1" echo "" log_info "Hyperstack VM setup completed successfully!" @@ -47,4 +46,4 @@ echo "" log_warn "Starting Codex..." sleep 1 clear -interactive_session "$HYPERSTACK_VM_IP" "bash -c 'source ~/.bashrc && codex'" +interactive_session "$HYPERSTACK_VM_IP" "source ~/.zshrc && codex" diff --git a/hyperstack/gemini.sh b/hyperstack/gemini.sh index afc088ad..49da466a 100644 --- a/hyperstack/gemini.sh +++ b/hyperstack/gemini.sh @@ -32,12 +32,11 @@ else fi log_warn "Setting up environment variables..." -run_server "$HYPERSTACK_VM_IP" "cat >> ~/.bashrc << 'EOF' -export OPENROUTER_API_KEY=${OPENROUTER_API_KEY} -export GEMINI_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_BASE_URL=https://openrouter.ai/api/v1 -EOF" +inject_env_vars_ssh "$HYPERSTACK_VM_IP" upload_file run_server \ + "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" \ + "GEMINI_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_BASE_URL=https://openrouter.ai/api/v1" echo "" log_info "Hyperstack setup completed successfully!" @@ -46,4 +45,4 @@ echo "" log_warn "Starting Gemini..." sleep 1 clear -interactive_session "$HYPERSTACK_VM_IP" "bash -c 'source ~/.bashrc && gemini'" +interactive_session "$HYPERSTACK_VM_IP" "source ~/.zshrc && gemini" diff --git a/hyperstack/goose.sh b/hyperstack/goose.sh index 65291407..a51837bd 100644 --- a/hyperstack/goose.sh +++ b/hyperstack/goose.sh @@ -47,7 +47,9 @@ else fi log_warn "Setting up environment variables..." -run_server "$HYPERSTACK_VM_IP" "printf '\nexport GOOSE_PROVIDER=openrouter\nexport OPENROUTER_API_KEY=%s\n' '$OPENROUTER_API_KEY' >> ~/.bashrc" +inject_env_vars_ssh "$HYPERSTACK_VM_IP" upload_file run_server \ + "GOOSE_PROVIDER=openrouter" \ + "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" echo "" log_info "Hyperstack VM setup completed successfully!" @@ -57,4 +59,4 @@ echo "" log_warn "Starting Goose..." sleep 1 clear -interactive_session "$HYPERSTACK_VM_IP" "bash -c 'source ~/.bashrc && goose'" +interactive_session "$HYPERSTACK_VM_IP" "source ~/.zshrc && goose" diff --git a/hyperstack/interpreter.sh b/hyperstack/interpreter.sh index fba8c79b..e600bd09 100644 --- a/hyperstack/interpreter.sh +++ b/hyperstack/interpreter.sh @@ -34,11 +34,10 @@ else fi log_warn "Setting up environment variables..." -run_server "$HYPERSTACK_VM_IP" "cat >> ~/.bashrc << 'ENVEOF' -export OPENROUTER_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_API_KEY=${OPENROUTER_API_KEY} -export OPENAI_BASE_URL=https://openrouter.ai/api/v1 -ENVEOF" +inject_env_vars_ssh "$HYPERSTACK_VM_IP" upload_file run_server \ + "OPENROUTER_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_API_KEY=${OPENROUTER_API_KEY}" \ + "OPENAI_BASE_URL=https://openrouter.ai/api/v1" echo "" log_info "Hyperstack VM setup completed successfully!" @@ -47,4 +46,4 @@ echo "" log_warn "Starting Open Interpreter..." sleep 1 clear -interactive_session "$HYPERSTACK_VM_IP" "bash -c 'source ~/.bashrc && interpreter'" +interactive_session "$HYPERSTACK_VM_IP" "source ~/.zshrc && interpreter" diff --git a/koyeb/lib/common.sh b/koyeb/lib/common.sh index 237b7773..43eaecd2 100644 --- a/koyeb/lib/common.sh +++ b/koyeb/lib/common.sh @@ -254,7 +254,7 @@ run_server() { koyeb instances exec "$KOYEB_INSTANCE_ID" -- bash -c "$cmd" } -# Upload a file to the Koyeb instance +# Upload a file to the Koyeb instance via base64 encoding upload_file() { local local_path="$1" local remote_path="$2" @@ -264,12 +264,18 @@ upload_file() { return 1 fi - # Read file content and encode + # Validate remote_path to prevent command injection + if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then + log_error "Invalid remote path (contains unsafe characters): $remote_path" + return 1 + fi + + # Read file content and encode (base64 output is safe for shell embedding) local content - content=$(cat "$local_path" | base64) + content=$(base64 < "$local_path") # Write file on remote instance - run_server "echo '$content' | base64 -d > '$remote_path'" + run_server "printf '%s' '$content' | base64 -d > '$remote_path'" } # Wait for cloud-init or basic system readiness @@ -286,16 +292,20 @@ wait_for_cloud_init() { } # Inject environment variables into shell config +# Writes to a temp file and uploads to avoid shell interpolation of values inject_env_vars() { - local shell_rc="/root/.bashrc" - log_warn "Injecting environment variables..." - for env_var in "$@"; do - # Escape special characters for sed - local escaped_var=$(echo "$env_var" | sed 's/[&/\]/\\&/g') - run_server "echo 'export $escaped_var' >> $shell_rc" - done + local env_temp + env_temp=$(mktemp) + chmod 600 "${env_temp}" + track_temp_file "${env_temp}" + + generate_env_config "$@" > "${env_temp}" + + # Upload and append to .bashrc (Koyeb containers use bash, not zsh) + upload_file "${env_temp}" "/tmp/env_config" + run_server "cat /tmp/env_config >> /root/.bashrc && rm /tmp/env_config" log_info "Environment variables configured" }