Skip to content

Commit 847a2c1

Browse files
authored
Anchor SSH argument safe-set check with \A..\z to reject trailing newlines (#6384)
1 parent 43ed1f1 commit 847a2c1

2 files changed

Lines changed: 16 additions & 1 deletion

File tree

‎features/flags.feature‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,18 @@ Feature: Global flags
436436
Running SSH command: docker exec '--env MY_VAR=value' 'wordpress' sh -c
437437
"""
438438

439+
@skip-windows @skip-macos
440+
Scenario: SSH arguments with a trailing newline should be escaped
441+
# A trailing newline used to satisfy the "safe characters" check because
442+
# PCRE's $ anchor also matches just before a final newline, letting the
443+
# argument reach the remote shell unquoted.
444+
When I try `newline_arg="$(printf 'foo\nx')"; wp --debug --ssh=wordpress option get "${newline_arg%x}"`
445+
Then STDERR should contain:
446+
"""
447+
Running SSH command: ssh -T -vvv 'wordpress' 'wp --debug option get '\''foo
448+
'\'''
449+
"""
450+
439451
Scenario: SSH connection string with leading hyphen in host should error
440452
# The payload must not contain a slash, as that would be parsed as the path
441453
# and thus separated from the host.

‎php/WP_CLI/Runner.php‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,10 @@ private function run_ssh_command( string $connection_string ): void {
10441044
foreach ( $wp_args as $arg ) {
10451045
// Quote empty strings and arguments with any characters outside the safe set.
10461046
// The empty string check is explicit for clarity, though regex would also catch it.
1047-
if ( '' !== $arg && preg_match( '/^[a-zA-Z0-9_=.\/:-]+$/', $arg ) ) {
1047+
// Anchor with \A and \z rather than ^ and $: PCRE's $ also matches just before a
1048+
// trailing newline, which would let a value ending in "\n" skip escaping and smuggle
1049+
// a command separator into the remote shell command.
1050+
if ( '' !== $arg && preg_match( '/\A[a-zA-Z0-9_=.\/:-]+\z/', $arg ) ) {
10481051
$escaped_args[] = $arg;
10491052
} else {
10501053
$escaped_args[] = escapeshellarg( $arg );

0 commit comments

Comments
 (0)