Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
bug #29639 [Yaml] detect circular references (xabbuh)
This PR was merged into the 3.4 branch.

Discussion
----------

[Yaml] detect circular references

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #29462
| License       | MIT
| Doc PR        |

Commits
-------

b7487f4 [Yaml] detect circular references
  • Loading branch information
nicolas-grekas committed Dec 19, 2018
2 parents c0cf123 + b7487f4 commit 88525d7
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/Symfony/Component/Yaml/Parser.php
Expand Up @@ -35,6 +35,7 @@ class Parser
private $refs = array();
private $skippedLineNumbers = array();
private $locallySkippedLineNumbers = array();
private $refsBeingParsed = array();

public function __construct()
{
Expand Down Expand Up @@ -212,6 +213,7 @@ private function doParse($value, $flags)

if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$this->refsBeingParsed[] = $isRef;
$values['value'] = $matches['value'];
}

Expand Down Expand Up @@ -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<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -340,6 +347,7 @@ private function doParse($value, $flags)
}
} elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]++) *+(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$this->refsBeingParsed[] = $isRef;
$values['value'] = $matches['value'];
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down
42 changes: 42 additions & 0 deletions src/Symfony/Component/Yaml/Tests/ParserTest.php
Expand Up @@ -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 = <<<YAML
foo:
- &foo
- &bar
bar: foobar
baz: *foo
YAML;
$tests['sequence'] = array($yaml);

$yaml = <<<YAML
foo: &foo
bar: &bar
foobar: baz
baz: *foo
YAML;
$tests['mapping'] = array($yaml);

$yaml = <<<YAML
foo: &foo
bar: &bar
foobar: baz
<<: *foo
YAML;
$tests['mapping with merge key'] = array($yaml);

return $tests;
}

/**
* @dataProvider indentedMappingData
*/
Expand Down

0 comments on commit 88525d7

Please sign in to comment.