From b7487f476b0bc2ceb534c310e0273ae04001d539 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 18 Dec 2018 09:15:13 +0100 Subject: [PATCH] [Yaml] detect circular references --- src/Symfony/Component/Yaml/Parser.php | 14 +++++++ .../Component/Yaml/Tests/ParserTest.php | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 36c9f1d91370..8b3b6992e511 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -35,6 +35,7 @@ class Parser private $refs = array(); private $skippedLineNumbers = array(); private $locallySkippedLineNumbers = array(); + private $refsBeingParsed = array(); public function __construct() { @@ -212,6 +213,7 @@ private function doParse($value, $flags) if (isset($values['value']) && self::preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; + $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } @@ -244,6 +246,7 @@ private function doParse($value, $flags) } if ($isRef) { $this->refs[$isRef] = end($data); + array_pop($this->refsBeingParsed); } } elseif ( self::preg_match('#^(?P(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P.+))?$#u', rtrim($this->currentLine), $values) @@ -287,6 +290,10 @@ private function doParse($value, $flags) if (isset($values['value'][0]) && '*' === $values['value'][0]) { $refName = substr(rtrim($values['value']), 1); if (!array_key_exists($refName, $this->refs)) { + if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { + throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); } @@ -340,6 +347,7 @@ private function doParse($value, $flags) } } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P[^ ]++) *+(?P.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; + $this->refsBeingParsed[] = $isRef; $values['value'] = $matches['value']; } @@ -395,6 +403,7 @@ private function doParse($value, $flags) } if ($isRef) { $this->refs[$isRef] = $data[$key]; + array_pop($this->refsBeingParsed); } } else { // multiple documents are not supported @@ -500,6 +509,7 @@ private function parseBlock($offset, $yaml, $flags) $parser->totalNumberOfLines = $this->totalNumberOfLines; $parser->skippedLineNumbers = $skippedLineNumbers; $parser->refs = &$this->refs; + $parser->refsBeingParsed = $this->refsBeingParsed; return $parser->doParse($yaml, $flags); } @@ -689,6 +699,10 @@ private function parseValue($value, $flags, $context) } if (!array_key_exists($value, $this->refs)) { + if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { + throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + } + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 6372fe45eef8..38ed1b340d27 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2177,6 +2177,48 @@ public function testEvalRefException() $this->parser->parse($yaml); } + /** + * @dataProvider circularReferenceProvider + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Circular reference [foo, bar, foo] detected + */ + public function testDetectCircularReferences($yaml) + { + $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS); + } + + public function circularReferenceProvider() + { + $tests = array(); + + $yaml = <<