8000 Fix double-escaped SSH commands by Copilot · Pull Request #6240 · wp-cli/wp-cli · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions features/aliases.feature
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,34 @@ Feature: Create shortcuts to specific WordPress installs
Running SSH command: ssh -i 'identityfile.key' -T -vvv
"""

Scenario: SSH commands should not be double-escaped
Given a WP installation in 'foo'
And a wp-cli.yml file:
"""
@foo:
ssh: user@host:/path/to/wordpress
"""

When I try `wp @foo plugin list --debug`
Then STDERR should contain:
"""
Running SSH command: ssh -T -vvv 'user@host' 'cd '\''/path/to/wordpress'\''; wp plugin list --debug'
"""

Scenario: SSH commands correctly escape arguments with spaces
Given a WP installation in 'foo'
And a wp-cli.yml file:
"""
@foo:
ssh: user@host:/path/to/wordpress
"""

When I try `wp @foo post create --post_title=My Title --debug`
Then STDERR should contain:
"""
Running SSH command: ssh -T -vvv 'user@host' 'cd '\''/path/to/wordpress'\''; wp post create --post_title=My Title
"""

Scenario: Uses env command for runtime alias with separate path line
Given a WP installation in 'foo'
And a wp-cli.yml file:
Expand Down
34 changes: 23 additions & 11 deletions php/WP_CLI/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -712,18 +712,30 @@ private function run_ssh_command( string $connection_string ): void {
}
}

$wp_command = $pre_cmd . $env_vars . $wp_binary . ' ' . implode(
' ',
array_map(
static function ( $arg ): string {
return escapeshellarg( (string) $arg ); },
$wp_args
)
);

if ( isset( $bits['scheme'] ) && 'docker-compose-run' === $bits['scheme'] ) {
$wp_command = implode( ' ', $wp_args );
// Build command with minimal quoting to improve readability in debug output.
// Arguments are only quoted if they contain characters outside the safe set.
// This avoids double-escaping appearance while maintaining security:
// 1. Here: Quote args with special chars for the remote shell
// 2. generate_ssh_command(): Wrap entire command for local shell
//
// Safe characters: alphanumeric, hyphen, underscore, equals, dot, forward slash, colon
// - Hyphens (including at start like --debug) are safe because they're part of the
// wp-cli command string that's passed to the remote shell, not SSH options
// - Forward slash and colon are included because they're common in paths and URLs
// (e.g., --url=https://example.com/path) and are not shell metacharacters
// - All other characters (spaces, quotes, $, &, |, etc.) trigger quoting via escapeshellarg()
$escaped_args = [];
foreach ( $wp_args as $arg ) {
$arg_str = (string) $arg;
// Quote empty strings and arguments with any characters outside the safe set.
// The empty string check is explicit for clarity, though regex would also catch it.
if ( '' !== $arg_str && preg_match( '/^[a-zA-Z0-9_=.\/:-]+$/', $arg_str ) ) {
$escaped_args[] = $arg_str;
} else {
$escaped_args[] = escapeshellarg( $arg_str );
}
}
$wp_command = $pre_cmd . $env_vars . $wp_binary . ' ' . implode( ' ', $escaped_args );

$escaped_command = $this->generate_ssh_command( $bits, $wp_command );

Expand Down
Loading
0