mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
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:
parent
c1085f076a
commit
3d274bf3d2
6 changed files with 44 additions and 13 deletions
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/"$//')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue