Skip to content

Commit 8d98d85

Browse files
authored
Merge pull request #45 from Kirill89/master
Update addArg() to improve escaping
2 parents 6c6f44c + c2ef7db commit 8d98d85

File tree

3 files changed

+34
-12
lines changed

3 files changed

+34
-12
lines changed

src/Command.php

+13-6
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public function getArgs()
272272
* @param string|array|null $value the optional argument value which will
273273
* get escaped if $escapeArgs is true. An array can be passed to add more
274274
* than one value for a key, e.g. `addArg('--exclude',
275-
* array('val1','val2'))` which will create the option `--exclude 'val1'
275+
* array('val1','val2'))` which will create the option `'--exclude' 'val1'
276276
* 'val2'`.
277277
* @param bool|null $escape if set, this overrides the $escapeArgs setting
278278
* and enforces escaping/no escaping
@@ -288,18 +288,25 @@ public function addArg($key, $value = null, $escape = null)
288288
setlocale(LC_CTYPE, $this->locale);
289289
}
290290
if ($value === null) {
291-
// Only escape single arguments if explicitely requested
292-
$this->_args[] = $escape ? escapeshellarg($key) : $key;
291+
$this->_args[] = $doEscape ? escapeshellarg($key) : $key;
293292
} else {
294-
$separator = substr($key, -1)==='=' ? '' : ' ';
293+
if (substr($key, -1) === '=') {
294+
$separator = '=';
295+
$argKey = substr($key, 0, -1);
296+
} else {
297+
$separator = ' ';
298+
$argKey = $key;
299+
}
300+
$argKey = $doEscape ? escapeshellarg($argKey) : $argKey;
301+
295302
if (is_array($value)) {
296303
$params = array();
297304
foreach ($value as $v) {
298305
$params[] = $doEscape ? escapeshellarg($v) : $v;
299306
}
300-
$this->_args[] = $key . $separator.implode(' ',$params);
307+
$this->_args[] = $argKey . $separator . implode(' ', $params);
301308
} else {
302-
$this->_args[] = $key . $separator .
309+
$this->_args[] = $argKey . $separator .
303310
($doEscape ? escapeshellarg($value) : $value);
304311
}
305312
}

tests/BlockingCommandTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function testCanRunCommandWithArguments()
6262
$command->nonBlockingMode = false;
6363
$command->addArg('-l');
6464
$command->addArg('-n');
65-
$this->assertEquals("ls -l -n", $command->getExecCommand());
65+
$this->assertEquals("ls '-l' '-n'", $command->getExecCommand());
6666
$this->assertFalse($command->getExecuted());
6767
$this->assertTrue($command->execute());
6868
$this->assertTrue($command->getExecuted());

tests/CommandTest.php

+20-5
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ public function testCanAddArguments()
8181
$command->addArg('-b=', array('v4','v5','v6'));
8282
$command->addArg('-c', '');
8383
$command->addArg('some name', null, true);
84-
$this->assertEquals("--arg1=x --a --a '中文字äüp' --a 'v'\''1' 'v2' 'v3' -b=v -b='v4' 'v5' 'v6' -c '' 'some name'", $command->getArgs());
85-
$this->assertEquals("test --arg1=x --a --a '中文字äüp' --a 'v'\''1' 'v2' 'v3' -b=v -b='v4' 'v5' 'v6' -c '' 'some name'", $command->getExecCommand());
84+
$this->assertEquals("--arg1=x '--a' '--a' '中文字äüp' '--a' 'v'\''1' 'v2' 'v3' -b=v '-b'='v4' 'v5' 'v6' '-c' '' 'some name'", $command->getArgs());
85+
$this->assertEquals("test --arg1=x '--a' '--a' '中文字äüp' '--a' 'v'\''1' 'v2' 'v3' -b=v '-b'='v4' 'v5' 'v6' '-c' '' 'some name'", $command->getExecCommand());
8686
}
8787
public function testCanResetArguments()
8888
{
@@ -102,14 +102,29 @@ public function testCanDisableEscaping()
102102
$command->addArg('-b=','v', true);
103103
$command->addArg('-b=', array('v4','v5','v6'));
104104
$command->addArg('some name', null, true);
105-
$this->assertEquals("--a --a v --a v1 v2 v3 -b='v' -b=v4 v5 v6 'some name'", $command->getArgs());
105+
$this->assertEquals("--a --a v --a v1 v2 v3 '-b'='v' -b=v4 v5 v6 'some name'", $command->getArgs());
106+
}
107+
public function testCanPreventCommandInjection()
108+
{
109+
$command = new Command(array(
110+
'command' => 'curl',
111+
));
112+
$command->addArg('http://example.com --wrong-argument || echo "RCE 1"');
113+
$this->assertEquals("'http://example.com --wrong-argument || echo \"RCE 1\"'", $command->getArgs());
114+
115+
$command = new Command(array(
116+
'command' => 'curl',
117+
));
118+
$command->addArg('http://example.com');
119+
$command->addArg('--header foo --wrong-argument || echo "RCE 2" ||', 'bar');
120+
$this->assertEquals("'http://example.com' '--header foo --wrong-argument || echo \"RCE 2\" ||' 'bar'", $command->getArgs());
106121
}
107122
public function testCanRunCommandWithArguments()
108123
{
109124
$command = new Command('ls');
110125
$command->addArg('-l');
111126
$command->addArg('-n');
112-
$this->assertEquals("ls -l -n", $command->getExecCommand());
127+
$this->assertEquals("ls '-l' '-n'", $command->getExecCommand());
113128
$this->assertFalse($command->getExecuted());
114129
$this->assertTrue($command->execute());
115130
$this->assertTrue($command->getExecuted());
@@ -163,7 +178,7 @@ public function testCanCastToString()
163178
$command = new Command('ls');
164179
$command->addArg('-l');
165180
$command->addArg('-n');
166-
$this->assertEquals("ls -l -n", (string)$command);
181+
$this->assertEquals("ls '-l' '-n'", (string)$command);
167182
}
168183

169184
// Exec

0 commit comments

Comments
 (0)