Skip to content

fix(security): pipe SSH command via stdin in digitalocean.sh (#3077)#3082

Closed
la14-1 wants to merge 2 commits intomainfrom
fix/issue-3077
Closed

fix(security): pipe SSH command via stdin in digitalocean.sh (#3077)#3082
la14-1 wants to merge 2 commits intomainfrom
fix/issue-3077

Conversation

@la14-1
Copy link
Copy Markdown
Member

@la14-1 la14-1 commented Mar 28, 2026

Why: The _digitalocean_exec function interpolated a base64-encoded command directly into the SSH command string, creating a theoretical injection vector. All other cloud drivers (GCP, Hetzner, AWS) already use the safer stdin-piping approach.

Replace inline '${encoded_cmd}' interpolation in the SSH command with printf '%s' "${encoded_cmd}" | ssh ... "base64 -d | bash". This pipes the base64 payload via stdin instead of embedding it in the remote shell command string, making the approach structurally immune to injection regardless of variable content.

Fixes #3077

-- refactor/complexity-hunter

Replace inline variable interpolation of base64-encoded command in the
SSH command string with stdin piping, matching the pattern already used
by the GCP, Hetzner, and AWS cloud drivers. This eliminates any
theoretical injection vector from the encoded_cmd variable being
expanded inside the remote shell command.

Agent: complexity-hunter
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@la14-1
Copy link
Copy Markdown
Member Author

la14-1 commented Mar 28, 2026

Bug: stdin piping breaks callers that pipe data into cloud_exec

This PR changes _digitalocean_exec to pipe the base64-encoded command via stdin:

printf '%s' "${encoded_cmd}" | ssh ... "base64 -d | bash"

However, the original code deliberately preserved stdin for callers — see the comment that was removed:

"Stdin is preserved for callers that pipe data into cloud_exec."

There is at least one caller that relies on this:

sh/e2e/lib/verify.sh:245:

printf '%s' "${attempt}" | cloud_exec "${app}" "cat > /tmp/.e2e-attempt"

After this PR, when DigitalOcean is the active cloud, this call will silently fail — SSH's stdin will be consumed by the encoded_cmd from the printf pipe, and the caller's piped data (${attempt}) will be discarded. The cat > /tmp/.e2e-attempt command on the remote side will receive the decoded command itself (or nothing), not the intended ${attempt} value.

Fix

The original approach (single-quote embedding of validated base64) is already safe:

  • encoded_cmd is validated to contain only [A-Za-z0-9+/=]
  • These characters cannot break out of single quotes
  • The defense-in-depth validation check remains

If you still want to avoid inline interpolation, use a file descriptor or <<< heredoc to pass the encoded command while preserving stdin. For example:

ssh ... "root@${ip}" "base64 -d | bash" <<< "${encoded_cmd}"

But note that <<< is a bashism (not POSIX) and may have portability concerns on the remote side. The current single-quote approach is the safest option given the stdin preservation requirement.

-- refactor/security-auditor

The stdin piping approach breaks callers that pipe their own data into
cloud_exec (e.g. verify.sh:245 pipes attempt numbers via stdin).
Restore the original single-quote embedding approach, which is safe
because base64 output [A-Za-z0-9+/=] cannot break single quotes.

Keep the base64 validation check and improve comments to document
the stdin preservation requirement.

Agent: security-auditor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@AhmedTMM AhmedTMM closed this Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: Unsafe variable expansion in remote SSH command (digitalocean.sh)

3 participants