diff --git a/fly/lib/common.sh b/fly/lib/common.sh index 011bd644..ca281b1d 100644 --- a/fly/lib/common.sh +++ b/fly/lib/common.sh @@ -164,8 +164,16 @@ _fly_create_app() { local org org=$(get_fly_org) + # SECURITY: Validate org slug to prevent JSON injection via FLY_ORG env var + if [[ ! "$org" =~ ^[a-zA-Z0-9_-]+$ ]]; then + log_error "Invalid FLY_ORG: must be alphanumeric with hyphens/underscores only" + return 1 + fi + log_warn "Creating Fly.io app '$name'..." - local app_body="{\"app_name\":\"$name\",\"org_slug\":\"$org\"}" + # SECURITY: Use json_escape to prevent JSON injection + local app_body + app_body=$(printf '{"app_name":%s,"org_slug":%s}' "$(json_escape "$name")" "$(json_escape "$org")") local app_response app_response=$(fly_api POST "/apps" "$app_body") @@ -197,18 +205,19 @@ _fly_create_machine() { log_warn "Creating Fly.io machine (region: $region, memory: ${vm_memory}MB)..." + # SECURITY: Pass values via environment variables to prevent Python injection local machine_body - machine_body=$(python3 -c " -import json + machine_body=$(_FLY_NAME="$name" _FLY_REGION="$region" _FLY_MEM="$vm_memory" python3 -c " +import json, os body = { - 'name': '$name', - 'region': '$region', + 'name': os.environ['_FLY_NAME'], + 'region': os.environ['_FLY_REGION'], 'config': { 'image': 'ubuntu:24.04', 'guest': { 'cpu_kind': 'shared', 'cpus': 1, - 'memory_mb': $vm_memory + 'memory_mb': int(os.environ['_FLY_MEM']) }, 'init': { 'exec': ['/bin/sleep', 'inf'] diff --git a/github-codespaces/lib/common.sh b/github-codespaces/lib/common.sh index 9fe0ca53..628a98f4 100755 --- a/github-codespaces/lib/common.sh +++ b/github-codespaces/lib/common.sh @@ -195,6 +195,7 @@ upload_file() { } # Run a command on the codespace (wrapper matching other providers' interface) +# SECURITY: Uses printf %q to properly escape commands to prevent injection run_server() { local cmd="$1" @@ -203,7 +204,9 @@ run_server() { return 1 fi - gh codespace ssh --codespace "$CODESPACE_NAME" -- bash -c "$cmd" + local escaped_cmd + escaped_cmd=$(printf '%q' "$cmd") + gh codespace ssh --codespace "$CODESPACE_NAME" -- bash -c "$escaped_cmd" } # Inject environment variables into shell config diff --git a/koyeb/lib/common.sh b/koyeb/lib/common.sh index 766e0feb..2315c732 100644 --- a/koyeb/lib/common.sh +++ b/koyeb/lib/common.sh @@ -201,6 +201,7 @@ create_server() { } # Run a command on the Koyeb service instance +# SECURITY: Uses printf %q to properly escape commands to prevent injection run_server() { local cmd="$1" @@ -209,7 +210,9 @@ run_server() { return 1 fi - koyeb instances exec "$KOYEB_INSTANCE_ID" -- bash -c "$cmd" + local escaped_cmd + escaped_cmd=$(printf '%q' "$cmd") + koyeb instances exec "$KOYEB_INSTANCE_ID" -- bash -c "$escaped_cmd" } # Upload a file to the Koyeb instance via base64 encoding @@ -278,7 +281,10 @@ interactive_session() { fi log_info "Starting interactive session..." - koyeb instances exec "$KOYEB_INSTANCE_ID" -- bash -c "$launch_cmd" + # SECURITY: Properly escape command to prevent injection + local escaped_cmd + escaped_cmd=$(printf '%q' "$launch_cmd") + koyeb instances exec "$KOYEB_INSTANCE_ID" -- bash -c "$escaped_cmd" } # Cleanup: delete the service and app diff --git a/railway/lib/common.sh b/railway/lib/common.sh index 673dd872..daa7f404 100644 --- a/railway/lib/common.sh +++ b/railway/lib/common.sh @@ -164,11 +164,14 @@ create_server() { } # Run a command on the Railway service +# SECURITY: Uses printf %q to properly escape commands to prevent injection run_server() { local cmd="$1" # Railway CLI doesn't have a direct exec - we use shell command via railway run - railway run bash -c "$cmd" + local escaped_cmd + escaped_cmd=$(printf '%q' "$cmd") + railway run bash -c "$escaped_cmd" } # Upload a file to the Railway service diff --git a/render/lib/common.sh b/render/lib/common.sh index 27952435..e359a17b 100644 --- a/render/lib/common.sh +++ b/render/lib/common.sh @@ -183,6 +183,7 @@ create_server() { } # Run a command on the Render service via SSH +# SECURITY: Uses printf %q to properly escape commands to prevent injection run_server() { local cmd="$1" @@ -191,7 +192,9 @@ run_server() { return 1 fi - render ssh --service "$RENDER_SERVICE_ID" -- bash -c "$cmd" + local escaped_cmd + escaped_cmd=$(printf '%q' "$cmd") + render ssh --service "$RENDER_SERVICE_ID" -- bash -c "$escaped_cmd" } # Upload a file to the Render service via base64 encoding over SSH @@ -253,7 +256,10 @@ interactive_session() { fi log_info "Starting interactive session..." - render ssh --service "$RENDER_SERVICE_ID" -- bash -c "$launch_cmd" + # SECURITY: Properly escape command to prevent injection + local escaped_cmd + escaped_cmd=$(printf '%q' "$launch_cmd") + render ssh --service "$RENDER_SERVICE_ID" -- bash -c "$escaped_cmd" } # Cleanup: delete the service diff --git a/shared/common.sh b/shared/common.sh index 66edc705..e39e65b5 100644 --- a/shared/common.sh +++ b/shared/common.sh @@ -506,10 +506,14 @@ wait_for_oauth_code() { exchange_oauth_code() { local oauth_code="${1}" + # SECURITY: Use json_escape to prevent JSON injection via crafted OAuth codes + local escaped_code + escaped_code=$(json_escape "${oauth_code}") + local key_response key_response=$(curl -s --max-time 30 -X POST "https://openrouter.ai/api/v1/auth/keys" \ -H "Content-Type: application/json" \ - -d "{\"code\": \"${oauth_code}\"}") + -d "{\"code\": ${escaped_code}}") local api_key api_key=$(echo "${key_response}" | grep -o '"key":"[^"]*"' | sed 's/"key":"//;s/"$//')