8000 properly parse backslashes in unquoted env vars · symfony/symfony@cd30eb9 · GitHub
[go: up one dir, main page]

Skip to content

Commit cd30eb9

Browse files
committed
properly parse backslashes in unquoted env vars
1 parent b6f9f8d commit cd30eb9

File tree

2 files changed

+27
-8
lines changed

2 files changed

+27
-8
lines changed

src/Symfony/Component/Dotenv/Dotenv.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,11 @@ private function lexValue()
224224
throw $this->createFormatException('Missing quote to end the value');
225225
}
226226
++$this->cursor;
227-
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
227+
$value = str_replace(array('\\"', '\r', '\n'), array('"', "\r", "\n"), $value);
228228
$resolvedValue = $value;
229229
$resolvedValue = $this->resolveVariables($resolvedValue);
230230
$resolvedValue = $this->resolveCommands($resolvedValue);
231+
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
231232
$v .= $resolvedValue;
232233
} else {
233234
$value = '';
@@ -250,6 +251,7 @@ private function lexValue()
250251
$resolvedValue = $value;
251252
$resolvedValue = $this->resolveVariables($resolvedValue);
252253
$resolvedValue = $this->resolveCommands($resolvedValue);
254+
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
253255

254256
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
255257
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
@@ -350,17 +352,23 @@ private function resolveVariables($value)
350352
}
351353

352354
$regex = '/
353-
(\\\\)? # escaped with a backslash?
355+
(?<!\\\\)
356+
((?:\\\\)*) # escaped with a backslash?
354357
\$
355358
(?!\() # no opening parenthesis
356359
(\{)? # optional brace
357-
('.self::VARNAME_REGEX.') # var name
360+
('.self::VARNAME_REGEX.')? # var name
358361
(\})? # optional closing brace
359362
/x';
360363

361364
$value = preg_replace_callback($regex, function ($matches) {
362-
if ('\\' === $matches[1]) {
363-
return substr($matches[0], 1);
365+
if (strlen($matches[1]) % 2 === 1) {
366+
return substr($matches[1], 0, - 1).substr($matches[0], strlen($matches[1]));
367+
}
368+
369+
// $ not followed by variable name
370+
if (!isset($matches[3])) {
371+
return $matches[0];
364372
}
365373

366374
if ('{' === $matches[2] && !isset($matches[4])) {
@@ -382,11 +390,10 @@ private function resolveVariables($value)
382390
$value .= '}';
383391
}
384392

385-
return $value;
393+
return $matches[1].$value;
386394
}, $value);
387395

388-
// unescape $
389-
return str_replace('\\$', '$', $value);
396+
return $value;
390397
}
391398

392399
private function moveCursor($text)

src/Symfony/Component/Dotenv/Tests/DotenvTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ public function getEnvData()
6666
$_ENV['REMOTE'] = 'remote';
6767

6868
$tests = array(
69+
// backslashes
70+
array('FOO=foo\\\\bar', array('FOO' => 'foo\\bar')),
71+
array("FOO='foo\\\\bar'", array('FOO' => 'foo\\\\bar')),
72+
array('FOO="foo\\\\bar"', array('FOO' => 'foo\\bar')),
73+
74+
// escaped backslash in front of variable
75+
array("BAR=bar\nFOO=foo\\\\\$BAR", array('BAR' => 'bar', 'FOO' => 'foo\\bar')),
76+
array("BAR=bar\nFOO='foo\\\\\$BAR'", array('BAR' => 'bar', 'FOO' => 'foo\\\\$BAR')),
77+
array("BAR=bar\nFOO=\"foo\\\\\$BAR\"", array('BAR' => 'bar', 'FOO' => 'foo\\bar')),
78+
79+
array('FOO=foo\\\\\\$BAR', array('FOO' => 'foo\\$BAR')),
80+
6981
// spaces
7082
array('FOO=bar', array('FOO' => 'bar')),
7183
array(' FOO=bar ', array('FOO' => 'bar')),

0 commit comments

Comments
 (0)
0