8000 bug #29222 [Dotenv] properly parse backslashes in unquoted env vars (… · symfony/symfony@7f310b4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7f310b4

Browse files
committed
bug #29222 [Dotenv] properly parse backslashes in unquoted env vars (xabbuh)
This PR was merged into the 3.4 branch. Discussion ---------- [Dotenv] properly parse backslashes in unquoted env vars | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #29202 | License | MIT | Doc PR | Commits ------- 785fff5 properly parse backslashes in unquoted env vars
2 parents 0f2d577 + 785fff5 commit 7f310b4

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

src/Symfony/Component/Dotenv/Dotenv.php

Lines changed: 21 additions & 13 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,24 +352,31 @@ private function resolveVariables($value)
350352
}
351353

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

361364
$value = preg_replace_callback($regex, function ($matches) {
362-
if ('\\' === $matches[1]) {
365+
// odd number of backslashes means the $ character is escaped
366+
if (1 === \strlen($matches['backslashes']) % 2) {
363367
return substr($matches[0], 1);
364368
}
365369

366-
if ('{' === $matches[2] && !isset($matches[4])) {
370+
// unescaped $ not followed by variable name
371+
if (!isset($matches['name'])) {
372+
return $matches[0];
373+
}
374+
375+
if ('{' === $matches['opening_brace'] && !isset($matches['closing_brace'])) {
367376
throw $this->createFormatException('Unclosed braces on variable expansion');
368377
}
369378

370-
$name = $matches[3];
379+
$name = $matches['name'];
371380
if (isset($this->values[$name])) {
372381
$value = $this->values[$name];
373382
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
@@ -378,15 +387,14 @@ private function resolveVariables($value)
378387
$value = (string) getenv($name);
379388
}
380389

381-
if (!$matches[2] && isset($matches[4])) {
390+
if (!$matches['opening_brace'] && isset($matches['closing_brace'])) {
382391
$value .= '}';
383392
}
384393

385-
return $value;
394+
return $matches['backslashes'].$value;
386395
}, $value);
387396

388-
// unescape 8000 $
389-
return str_replace('\\$', '$', $value);
397+
return $value;
390398
}
391399

392400
private function moveCursor($text)

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ 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+
array('FOO=\'foo\\\\\\$BAR\'', array('FOO' => 'foo\\\\\\$BAR')),
81+
array('FOO="foo\\\\\\$BAR"', array('FOO' => 'foo\\$BAR')),
82+
6983
// spaces
7084
array('FOO=bar', array('FOO' => 'bar')),
7185
array(' FOO=bar ', array('FOO' => 'bar')),

0 commit comments

Comments
 (0)
0