Skip to content

Commit 673b8e9

Browse files
committed
fix lexing strings containing escaped quotation characters
1 parent b60bb6e commit 673b8e9

File tree

2 files changed

+91
-38
lines changed

2 files changed

+91
-38
lines changed

src/Symfony/Component/Yaml/Parser.php

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -750,54 +750,54 @@ private function parseValue(string $value, int $flags, string $context)
750750
return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
751751
}
752752

753-
$quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
754-
755-
// do not take following lines into account when the current line is a quoted single line value
756-
if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
757-
return Inline::parse($value, $flags, $this->refs);
758-
}
753+
switch ($value[0] ?? '') {
754+
case '"':
755+
case "'":
756+
$cursor = \strlen($this->currentLine) - \strlen($value);
757+
$parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs);
758+
759+
if (isset($this->currentLine[$cursor]) && preg_replace('/\s*#.*$/A', '', substr($this->currentLine, $cursor))) {
760+
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor)));
761+
}
759762

760-
$lines = [];
763+
return $parsedValue;
764+
default:
765+
$lines = [];
761766

762-
while ($this->moveToNextLine()) {
763-
// unquoted strings end before the first unindented line
764-
if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
765-
$this->moveToPreviousLine();
767+
while ($this->moveToNextLine()) {
768+
// unquoted strings end before the first unindented line
769+
if (0 === $this->getCurrentLineIndentation()) {
770+
$this->moveToPreviousLine();
766771

767-
break;
768-
}
772+
break;
773+
}
769774

770-
$lines[] = trim($this->currentLine);
775+
$lines[] = trim($this->currentLine);
776+
}
771777

772-
// quoted string values end with a line that is terminated with the quotation character
773-
$escapedLine = str_replace(['\\\\', '\\"'], '', $this->currentLine);
774-
if ('' !== $escapedLine && substr($escapedLine, -1) === $quotation) {
775-
break;
776-
}
777-
}
778+
for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
779+
if ('' === $lines[$i]) {
780+
$value .= "\n";
781+
$previousLineBlank = true;
782+
} elseif ($previousLineBlank) {
783+
$value .= $lines[$i];
784+
$previousLineBlank = false;
785+
} else {
786+
$value .= ' '.$lines[$i];
787+
$previousLineBlank = false;
788+
}
789+
}
778790

779-
for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
780-
if ('' === $lines[$i]) {
781-
$value .= "\n";
782-
$previousLineBlank = true;
783-
} elseif ($previousLineBlank) {
784-
$value .= $lines[$i];
785-
$previousLineBlank = false;
786-
} else {
787-
$value .= ' '.$lines[$i];
788-
$previousLineBlank = false;
789-
}
790-
}
791+
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
791792

792-
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
793+
$parsedValue = Inline::parse($value, $flags, $this->refs);
793794

794-
$parsedValue = Inline::parse($value, $flags, $this->refs);
795+
if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
796+
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
797+
}
795798

796-
if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
797-
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
799+
return $parsedValue;
798800
}
799-
800-
return $parsedValue;
801801
} catch (ParseException $e) {
802802
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
803803
$e->setSnippet($this->currentLine);
@@ -1154,8 +1154,13 @@ private function lexInlineQuotedString(int &$cursor = 0): string
11541154

11551155
$previousLineWasNewline = true;
11561156
$previousLineWasTerminatedWithBackslash = false;
1157+
$lineNumber = 0;
11571158

11581159
do {
1160+
if (++$lineNumber > 1) {
1161+
$cursor += strspn($this->currentLine, ' ', $cursor);
1162+
}
1163+
11591164
if ($this->isCurrentLineBlank()) {
11601165
$value .= "\n";
11611166
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {

src/Symfony/Component/Yaml/Tests/ParserTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,54 @@ public function testParseMultiLineUnquotedString()
15701570
$this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml));
15711571
}
15721572

1573+
/**
1574+
* @dataProvider escapedQuotationCharactersInQuotedStrings
1575+
*/
1576+
public function testParseQuotedStringContainingEscapedQuotationCharacters(string $yaml, array $expected)
1577+
{
1578+
$this->assertSame($expected, $this->parser->parse($yaml));
1579+
}
1580+
1581+
public function escapedQuotationCharactersInQuotedStrings()
1582+
{
1583+
return [
1584+
'single quoted string' => [
1585+
<<<YAML
1586+
entries:
1587+
- message: 'No emails received before timeout - Address: ''test@testemail.company.com''
1588+
Keyword: ''Your Order confirmation'' ttl: 50'
1589+
outcome: failed
1590+
YAML
1591+
,
1592+
[
1593+
'entries' => [
1594+
[
1595+
'message' => 'No emails received before timeout - Address: \'test@testemail.company.com\' Keyword: \'Your Order confirmation\' ttl: 50',
1596+
'outcome' => 'failed',
1597+
],
1598+
],
1599+
],
1600+
],
1601+
'double quoted string' => [
1602+
<<<YAML
1603+
entries:
1604+
- message: "No emails received before timeout - Address: \"test@testemail.company.com\"
1605+
Keyword: \"Your Order confirmation\" ttl: 50"
1606+
outcome: failed
1607+
YAML
1608+
,
1609+
[
1610+
'entries' => [
1611+
[
1612+
'message' => 'No emails received before timeout - Address: "test@testemail.company.com" Keyword: "Your Order confirmation" ttl: 50',
1613+
'outcome' => 'failed',
1614+
],
1615+
],
1616+
],
1617+
],
1618+
];
1619+
}
1620+
15731621
public function testParseMultiLineString()
15741622
{
15751623
$this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy