fix: secure upload_file functions against command injection in 5 clouds (#453)

Replace unsafe printf '%q'-escaped unquoted variables with validated
single-quoted embedding in upload_file() for fly, northflank, daytona,
e2b, and koyeb. The previous pattern used unquoted $escaped_content and
$escaped_path in command strings passed to bash -c or run_server, which
could allow command injection via crafted filenames.

The fix:
- Validates remote_path rejects unsafe chars (', $, `, newlines)
- Uses base64 content directly (alphanumeric + /+= is shell-safe)
- Single-quotes both content and path in the command string
- Uses printf '%s' instead of echo for safer output

Matches the pattern already used by render, modal, and railway.

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 06:28:45 -08:00 committed by GitHub
parent 55fd4022e8
commit f1e8d946df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 49 additions and 34 deletions

View file

@ -164,15 +164,18 @@ run_server() {
upload_file() {
local local_path="${1}"
local remote_path="${2}"
# Upload via base64 encoding through exec (no native CLI file upload)
# SECURITY: Validate remote_path to prevent command injection via single-quote breakout
if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then
log_error "Invalid remote path (contains unsafe characters): $remote_path"
return 1
fi
# base64 output is safe (alphanumeric + /+=) so no injection risk
local content
content=$(base64 -w0 "${local_path}" 2>/dev/null || base64 "${local_path}")
# SECURITY: Properly escape paths and content
local escaped_path
escaped_path=$(printf '%q' "${remote_path}")
local escaped_content
escaped_content=$(printf '%q' "${content}")
daytona exec "${DAYTONA_SANDBOX_ID}" -- bash -c "printf '%s' ${escaped_content} | base64 -d > ${escaped_path}"
daytona exec "${DAYTONA_SANDBOX_ID}" -- bash -c "printf '%s' '${content}' | base64 -d > '${remote_path}'"
}
# Daytona has true SSH support — much better than exec-only providers

View file

@ -115,15 +115,18 @@ run_server() {
upload_file() {
local local_path="${1}"
local remote_path="${2}"
# Upload via base64 encoding through exec
# SECURITY: Validate remote_path to prevent command injection via single-quote breakout
if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then
log_error "Invalid remote path (contains unsafe characters): $remote_path"
return 1
fi
# base64 output is safe (alphanumeric + /+=) so no injection risk
local content
content=$(base64 -w0 "${local_path}" 2>/dev/null || base64 "${local_path}")
# SECURITY: Properly escape the remote path
local escaped_path
escaped_path=$(printf '%q' "${remote_path}")
local escaped_content
escaped_content=$(printf '%q' "${content}")
e2b sandbox exec "${E2B_SANDBOX_ID}" -- bash -c "echo ${escaped_content} | base64 -d > ${escaped_path}"
e2b sandbox exec "${E2B_SANDBOX_ID}" -- bash -c "printf '%s' '${content}' | base64 -d > '${remote_path}'"
}
interactive_session() {

View file

@ -311,13 +311,18 @@ run_server() {
upload_file() {
local local_path="$1"
local remote_path="$2"
local content=$(base64 -w0 "$local_path" 2>/dev/null || base64 "$local_path")
# SECURITY: Properly escape paths and content to prevent injection
local escaped_path
escaped_path=$(printf '%q' "$remote_path")
local escaped_content
escaped_content=$(printf '%q' "$content")
run_server "echo $escaped_content | base64 -d > $escaped_path"
# SECURITY: Validate remote_path to prevent command injection via single-quote breakout
if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then
log_error "Invalid remote path (contains unsafe characters): $remote_path"
return 1
fi
# base64 output is safe (alphanumeric + /+=) so no injection risk
local content
content=$(base64 -w0 "$local_path" 2>/dev/null || base64 "$local_path")
run_server "printf '%s' '${content}' | base64 -d > '${remote_path}'"
}
# Start an interactive SSH session on the Fly.io machine

View file

@ -222,16 +222,18 @@ upload_file() {
return 1
fi
# SECURITY: Validate remote_path to prevent command injection via single-quote breakout
if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then
log_error "Invalid remote path (contains unsafe characters): $remote_path"
return 1
fi
# SECURITY: base64 -w0 produces single-line output (no newline injection)
# base64 output is safe (alphanumeric + /+=) so no injection risk
local content
content=$(base64 -w0 "$local_path" 2>/dev/null || base64 "$local_path")
# SECURITY: Properly escape remote_path to prevent injection
local escaped_path
escaped_path=$(printf '%q' "$remote_path")
# base64 output is safe (alphanumeric + /+=) so no injection risk
run_server "printf '%s' '$content' | base64 -d > $escaped_path"
run_server "printf '%s' '${content}' | base64 -d > '${remote_path}'"
}
# Wait for cloud-init or basic system readiness

View file

@ -166,16 +166,18 @@ run_server() {
upload_file() {
local local_path="${1}"
local remote_path="${2}"
# SECURITY: Validate remote_path to prevent command injection via single-quote breakout
if [[ "$remote_path" == *"'"* || "$remote_path" == *'$'* || "$remote_path" == *'`'* || "$remote_path" == *$'\n'* ]]; then
log_error "Invalid remote path (contains unsafe characters): $remote_path"
return 1
fi
# base64 output is safe (alphanumeric + /+=) so no injection risk
local content
content=$(base64 -w0 "${local_path}" 2>/dev/null || base64 "${local_path}")
# SECURITY: Properly escape paths and content to prevent injection
local escaped_path
escaped_path=$(printf '%q' "${remote_path}")
local escaped_content
escaped_content=$(printf '%q' "${content}")
run_server "echo ${escaped_content} | base64 -d > ${escaped_path}"
run_server "printf '%s' '${content}' | base64 -d > '${remote_path}'"
}
# Start an interactive shell session on the Northflank service