Changeset View
Changeset View
Standalone View
Standalone View
src/xsprintf/__tests__/PhutilCsprintfTestCase.php
Show All 10 Lines | $inputs = array( | ||||
// For arguments which have any characters which are not safe in some | // For arguments which have any characters which are not safe in some | ||||
// context, %R should apply standard escaping. | // context, %R should apply standard escaping. | ||||
'a b' => false, | 'a b' => false, | ||||
'http://domain.com/path/' => true, | 'http://domain.com/path/' => true, | ||||
'svn+ssh://domain.com/path/' => true, | 'svn+ssh://domain.com/path/' => true, | ||||
'`rm -rf`' => false, | '`rm -rf`' => false, | ||||
'$VALUE' => false, | '$VALUE' => phutil_is_windows(), | ||||
'%VALUE%' => !phutil_is_windows(), | |||||
); | ); | ||||
foreach ($inputs as $input => $expect_same) { | foreach ($inputs as $input => $expect_same) { | ||||
$actual = (string)csprintf('%R', $input); | $actual = (string)csprintf('%R', $input); | ||||
if ($expect_same) { | if ($expect_same) { | ||||
$this->assertEqual($input, $actual); | $this->assertEqual($input, $actual); | ||||
} else { | } else { | ||||
$this->assertFalse($input === $actual); | $this->assertFalse($input === $actual); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
public function testPowershell() { | public function testPowershell() { | ||||
$cmd = csprintf('%s', "\n"); | $cmd = csprintf('%s', "\n"); | ||||
$cmd->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); | $cmd->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); | ||||
$this->assertEqual( | $this->assertEqual( | ||||
'"`n"', | '"`n"', | ||||
(string)$cmd); | (string)$cmd); | ||||
} | } | ||||
public function testNoPowershell() { | public function testNoPowershell() { | ||||
if (!phutil_is_windows()) { | $cmd = csprintf('%s', '#"'); | ||||
$cmd = csprintf('%s', '#'); | |||||
$cmd->setEscapingMode(PhutilCommandString::MODE_DEFAULT); | $cmd->setEscapingMode(PhutilCommandString::MODE_DEFAULT); | ||||
$this->assertEqual( | $this->assertEqual( | ||||
'\'#\'', | phutil_is_windows() ? '^"#\^"^"' : '\'#"\'', | ||||
BYK: Well, this is not really accurate. I should probably branch and use the expected Windows… | |||||
(string)$cmd); | (string)$cmd); | ||||
} | } | ||||
} | |||||
public function testPasswords() { | public function testPasswords() { | ||||
// Normal "%s" doesn't do anything special. | // Normal "%s" doesn't do anything special. | ||||
$command = csprintf('echo %s', 'hunter2trustno1'); | $command = csprintf('echo %s', 'hunter2trustno1'); | ||||
$this->assertTrue(strpos($command, 'hunter2trustno1') !== false); | $this->assertTrue(strpos($command, 'hunter2trustno1') !== false); | ||||
// "%P" takes a PhutilOpaqueEnvelope. | // "%P" takes a PhutilOpaqueEnvelope. | ||||
$caught = null; | $caught = null; | ||||
try { | try { | ||||
csprintf('echo %P', 'hunter2trustno1'); | csprintf('echo %P', 'hunter2trustno1')->getMaskedString(); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
$caught = $ex; | $caught = $ex; | ||||
} | } | ||||
$this->assertTrue($caught instanceof InvalidArgumentException); | $this->assertTrue($caught instanceof InvalidArgumentException); | ||||
// "%P" masks the provided value. | // "%P" masks the provided value. | ||||
$command = csprintf('echo %P', new PhutilOpaqueEnvelope('hunter2trustno1')); | $command = csprintf('echo %P', new PhutilOpaqueEnvelope('hunter2trustno1')); | ||||
$this->assertFalse(strpos($command, 'hunter2trustno1')); | $this->assertFalse(strpos($command, 'hunter2trustno1')); | ||||
// Executing the command works as expected. | // Executing the command works as expected. | ||||
list($out) = execx('%C', $command); | list($out) = execx('%C', $command); | ||||
$this->assertTrue(strpos($out, 'hunter2trustno1') !== false); | $this->assertTrue(strpos($out, 'hunter2trustno1') !== false); | ||||
} | } | ||||
public function testEscapingIsRobust() { | public function testEscapingIsRobust() { | ||||
if (phutil_is_windows()) { | if (phutil_is_windows()) { | ||||
// NOTE: The reason we can't run this test on Windows is two fold: | |||||
// 1. We need to use both `argv` escaping and `cmd` escaping | |||||
// when running commands on Windows because of the CMD proxy | |||||
// 2. After the first `argv` escaping, you only need CMD escaping | |||||
// but we need a new `%x` thing to signal this which is probably | |||||
// not worth the added complexity. | |||||
$this->assertSkipped(pht("This test doesn't work on Windows.")); | $this->assertSkipped(pht("This test doesn't work on Windows.")); | ||||
} | } | ||||
// Escaping should be robust even when used to escape commands which take | // Escaping should be robust even when used to escape commands which take | ||||
// other commands. | // other commands. | ||||
list($out) = execx( | list($out) = execx( | ||||
'sh -c %s', | 'sh -c %s', | ||||
csprintf( | csprintf( | ||||
'sh -c %s', | 'sh -c %s', | ||||
csprintf( | csprintf( | ||||
'sh -c %s', | 'sh -c %s', | ||||
csprintf( | csprintf( | ||||
'echo %P', | 'echo %P', | ||||
new PhutilOpaqueEnvelope('!@#$%^&*()'))))); | new PhutilOpaqueEnvelope('!@#$%^&*()'))))); | ||||
$this->assertTrue(strpos($out, '!@#$%^&*()') !== false); | $this->assertTrue(strpos($out, '!@#$%^&*()') !== false); | ||||
} | } | ||||
public function testEdgeCases() { | |||||
Not Done Inline ActionsThese test cases do not test what they intend to test. In PHP, '\0' is the string literal "backslash, zero", not a null byte. Likewise, '\n' and '\r' are not newline characters. When these strings are changed to use double quotes, the test cases fail. epriestley: These test cases do not test what they intend to test.
In PHP, `'\0'` is the string literal… | |||||
Not Done Inline ActionsAh, okay I see. Will fix. BYK: Ah, okay I see. Will fix. | |||||
Not Done Inline ActionsTurns out it is not possible to pass a NULL BYTE through the command line anyways so gonna drop that case. Working on the new lines which are the other two problems. BYK: Turns out it is not possible to pass a NULL BYTE through the command line anyways so gonna drop… | |||||
$edgeCases = array( | |||||
'\\', | |||||
'%', | |||||
'%%', | |||||
' ', // space | |||||
'', // empty string | |||||
'-', | |||||
'/flag', | |||||
'\\\^\%\\"\ \\', | |||||
'%PATH%', | |||||
'%XYZ%', | |||||
'%%HOMEDIR%', | |||||
'a b', | |||||
'"a b"', | |||||
'"%%$HOMEDIR%^^"', | |||||
'\'a b\'', | |||||
'^%HO ^"M\'EDIR^%^%\'', | |||||
'"\'a\0\r\nb%PATH%%`\'"\'`\'`\'', | |||||
); | |||||
foreach ($edgeCases as $edgeCase) { | |||||
list($output) = execx('php -r %s -- %s', 'echo $argv[1];', $edgeCase); | |||||
$this->assertEqual($edgeCase, $output); | |||||
} | |||||
} | |||||
Not Done Inline ActionsShould probably do list($output) = execx('php -r %s -- %s', 'echo $argv[1];', $edgeCase); instead. BYK: Should probably do
list($output) = execx('php -r %s -- %s', 'echo $argv[1];', $edgeCase)… | |||||
public function testThrowingEdgeCases() { | |||||
$edgeCases = array( | |||||
"\0", | |||||
"\n", | |||||
"\r", | |||||
"\n\r\n", | |||||
); | |||||
foreach ($edgeCases as $edgeCase) { | |||||
$caught = null; | |||||
try { | |||||
$cmd = csprintf('echo %s', $edgeCase); | |||||
$cmd->setEscapingMode(PhutilCommandString::MODE_WIN_CMD); | |||||
$cmd->getMaskedString(); | |||||
} catch (Exception $ex) { | |||||
$caught = $ex; | |||||
} | |||||
$this->assertTrue($caught instanceof UnexpectedValueException); | |||||
} | |||||
} | |||||
} | } |
Well, this is not really accurate. I should probably branch and use the expected Windows version at this point. What do you think?