Description
It is quite easy to break ProcessUtils::escapeArgument
on Windows, because it does not follow all the various argument-parsing rules.
I discovered this while working on Composer's XdebugHandler (which restarts the process and needs a robust way to escape its passed-in arguments) and after much research came up with a single function Winbox\Args::escape to handle this.
To illustrate the problems, we can pass some contrived arguments to both escape functions and get php to print out its $argv
:
Given this composer.json
:
{
"require": {
"symfony/process": "^2.1",
"winbox/args": "^1.0"
}
}
and this script:
<?php
require __DIR__ . '/vendor/autoload.php';
$params = [
PHP_BINARY,
'-r',
'print_r($argv);',
'--',
'path=%PATH%',
'quote="',
'colors=red & blue',
];
run($params, ['Symfony\Component\Process\ProcessUtils', 'escapeArgument']);
run($params, ['Winbox\Args', 'escape']);
function run($params, $escaper)
{
$command = implode(' ', array_map($escaper, $params));
printf("%s command line:\n%s\n\n", $escaper[0], $command);
passthru($command);
echo PHP_EOL;
}
the expected output from print_r($argv);
is:
Array
(
[0] => -
[1] => path=%PATH%
[2] => quote="
[3] => colors=red & blue
)
The actual output using Winbox\Args
is as above, whereas the output using ProcessUtils
is:
Array
(
[0] => -
[1] => path=C:\WINDOWS\system32 ... and the rest of the PATH variable
[2] => quote="
[3] => colors=red
)
'blue"' is not recognized as an internal or external command,
operable program or batch file.
The unexpected path-expansion is a simple logic error, but the argument splitting (colors=red
followed by cmd.exe trying to run a program called blue
) highlights a more serious problem:
- the escaped arguments are not totally self-contained.
What's happening here is that quote="
is escaped as "quote=\""
and while this will work fine on its own, the odd number of double-quotes may corrupt subsequent arguments. In this case the escaped "colors=red & blue"
is interpreted by cmd.exe as an argument ("colors=red
) followed by the special character &
which signifies a separate command (blue"
). See How cmd.exe parses a command for more information.
The wiki at winbox-args details the various hoops you have to go through to try and make this stuff work.