Page MenuHomePhabricator

D10249.id.diff
No OneTemporary

D10249.id.diff

diff --git a/src/xsprintf/PhutilCommandString.php b/src/xsprintf/PhutilCommandString.php
--- a/src/xsprintf/PhutilCommandString.php
+++ b/src/xsprintf/PhutilCommandString.php
@@ -3,10 +3,16 @@
final class PhutilCommandString extends Phobject {
private $argv;
+ private $escapingMode = false;
+
+ const MODE_DEFAULT = 'default';
+ const MODE_POWERSHELL = 'powershell';
public function __construct(array $argv) {
$this->argv = $argv;
+ $this->escapingMode = self::MODE_DEFAULT;
+
// This makes sure we throw immediately if there are errors in the
// parameters.
$this->getMaskedString();
@@ -24,13 +30,56 @@
return $this->renderString(false);
}
+ public function setEscapingMode($escaping_mode) {
+ $this->escapingMode = $escaping_mode;
+ return $this;
+ }
+
private function renderString($unmasked) {
return xsprintf(
'xsprintf_command',
array(
'unmasked' => $unmasked,
+ 'mode' => $this->escapingMode
),
$this->argv);
}
+ public static function escapeArgument($value, $mode) {
+ switch ($mode) {
+ case self::MODE_DEFAULT:
+ return escapeshellarg($value);
+ case self::MODE_POWERSHELL:
+ return self::escapePowershell($value);
+ default:
+ throw new Exception('Unknown escaping mode!');
+ }
+ }
+
+ private static function escapePowershell($value) {
+ // These escape sequences are from http://ss64.com/ps/syntax-esc.html
+
+ // Replace backticks first.
+ $value = str_replace('`', '``', $value);
+
+ // Now replace other required notations.
+ $value = str_replace("\0", '`0', $value);
+ $value = str_replace(chr(7), '`a', $value);
+ $value = str_replace(chr(8), '`b', $value);
+ $value = str_replace("\f", '`f', $value);
+ $value = str_replace("\n", '`n', $value);
+ $value = str_replace("\r", '`r', $value);
+ $value = str_replace("\t", '`t', $value);
+ $value = str_replace("\v", '`v', $value);
+ $value = str_replace('#', '`#', $value);
+ $value = str_replace("'", '`\'', $value);
+ $value = str_replace('"', '`"', $value);
+
+ // The rule on dollar signs is mentioned further down the page, and
+ // they only need to be escaped when using double quotes (which we are).
+ $value = str_replace('$', '`$', $value);
+
+ return '"'.$value.'"';
+ }
+
}
diff --git a/src/xsprintf/__tests__/PhutilCsprintfTestCase.php b/src/xsprintf/__tests__/PhutilCsprintfTestCase.php
--- a/src/xsprintf/__tests__/PhutilCsprintfTestCase.php
+++ b/src/xsprintf/__tests__/PhutilCsprintfTestCase.php
@@ -12,6 +12,26 @@
$this->assertFalse('a b' === (string)csprintf('%R', 'a b'));
}
+ public function testPowershell() {
+ $cmd = csprintf('%s', "\n");
+ $cmd->setEscapingMode(PhutilCommandString::MODE_POWERSHELL);
+
+ $this->assertEqual(
+ '"`n"',
+ (string)$cmd);
+ }
+
+ public function testNoPowershell() {
+ if (!phutil_is_windows()) {
+ $cmd = csprintf('%s', '#');
+ $cmd->setEscapingMode(PhutilCommandString::MODE_DEFAULT);
+
+ $this->assertEqual(
+ '\'#\'',
+ (string)$cmd);
+ }
+ }
+
public function testPasswords() {
// Normal "%s" doesn't do anything special.
$command = csprintf('echo %s', 'hunter2trustno1');
diff --git a/src/xsprintf/csprintf.php b/src/xsprintf/csprintf.php
--- a/src/xsprintf/csprintf.php
+++ b/src/xsprintf/csprintf.php
@@ -57,6 +57,12 @@
$is_unmasked = !empty($userdata['unmasked']);
+ if (empty($userdata['mode'])) {
+ $mode = PhutilCommandString::MODE_DEFAULT;
+ } else {
+ $mode = $userdata['mode'];
+ }
+
if ($value instanceof PhutilCommandString) {
if ($is_unmasked) {
$value = $value->getUnmaskedString();
@@ -102,12 +108,12 @@
case 'R':
if (!preg_match('(^[a-zA-Z0-9:/@._-]+$)', $value)) {
- $value = escapeshellarg($value);
+ $value = PhutilCommandString::escapeArgument($value, $mode);
}
$type = 's';
break;
case 's':
- $value = escapeshellarg($value);
+ $value = PhutilCommandString::escapeArgument($value, $mode);
$type = 's';
break;
case 'P':
@@ -120,7 +126,7 @@
} else {
$value = 'xxxxx';
}
- $value = escapeshellarg($value);
+ $value = PhutilCommandString::escapeArgument($value, $mode);
$type = 's';
break;
case 'C':

File Metadata

Mime Type
text/plain
Expires
Sat, Feb 8, 10:30 PM (6 h, 15 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7100574
Default Alt Text
D10249.id.diff (4 KB)

Event Timeline