-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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.