fix: escape shell commands and sanitize JSON to prevent injection (#463)

- Add printf %q command escaping to run_server/interactive_session in
  Koyeb, Render, Railway, and GitHub Codespaces (matching pattern used
  by E2B, Daytona, Northflank, Fly, and other providers)
- Use json_escape in exchange_oauth_code to prevent JSON injection via
  crafted OAuth codes in shared/common.sh
- Use json_escape in Fly.io _fly_create_app to prevent JSON injection
  via FLY_ORG env var, plus add validation for org slug format
- Pass Fly.io _fly_create_machine values via env vars instead of Python
  string interpolation to prevent code injection

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-02-11 07:20:41 -08:00 committed by GitHub
parent c1085f076a
commit 3d274bf3d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 44 additions and 13 deletions

View file

@ -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']

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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/"$//')