Skip to content

Commit dfb3b1e

Browse files
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 ------- b7487f476b [Yaml] detect circular references
2 parents fac7c48 + 463d57b commit dfb3b1e

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

Parser.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Parser
3535
private $refs = array();
3636
private $skippedLineNumbers = array();
3737
private $locallySkippedLineNumbers = array();
38+
private $refsBeingParsed = array();
3839

3940
public function __construct()
4041
{
@@ -212,6 +213,7 @@ private function doParse($value, $flags)
212213

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

@@ -244,6 +246,7 @@ private function doParse($value, $flags)
244246
}
245247
if ($isRef) {
246248
$this->refs[$isRef] = end($data);
249+
array_pop($this->refsBeingParsed);
247250
}
248251
} elseif (
249252
self::preg_match('#^(?P<key>(?:![^\s]++\s++)?(?:'.Inline::REGEX_QUOTED_STRING.'|(?:!?!php/const:)?[^ \'"\[\{!].*?)) *\:(\s++(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
@@ -287,6 +290,10 @@ private function doParse($value, $flags)
287290
if (isset($values['value'][0]) && '*' === $values['value'][0]) {
288291
$refName = substr(rtrim($values['value']), 1);
289292
if (!array_key_exists($refName, $this->refs)) {
293+
if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) {
294+
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);
295+
}
296+
290297
throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename);
291298
}
292299

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

@@ -395,6 +403,7 @@ private function doParse($value, $flags)
395403
}
396404
if ($isRef) {
397405
$this->refs[$isRef] = $data[$key];
406+
array_pop($this->refsBeingParsed);
398407
}
399408
} else {
400409
// multiple documents are not supported
@@ -500,6 +509,7 @@ private function parseBlock($offset, $yaml, $flags)
500509
$parser->totalNumberOfLines = $this->totalNumberOfLines;
501510
$parser->skippedLineNumbers = $skippedLineNumbers;
502511
$parser->refs = &$this->refs;
512+
$parser->refsBeingParsed = $this->refsBeingParsed;
503513

504514
return $parser->doParse($yaml, $flags);
505515
}
@@ -689,6 +699,10 @@ private function parseValue($value, $flags, $context)
689699
}
690700

691701
if (!array_key_exists($value, $this->refs)) {
702+
if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) {
703+
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);
704+
}
705+
692706
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename);
693707
}
694708

Tests/ParserTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,6 +2177,48 @@ public function testEvalRefException()
21772177
$this->parser->parse($yaml);
21782178
}
21792179

2180+
/**
2181+
* @dataProvider circularReferenceProvider
2182+
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
2183+
* @expectedExceptionMessage Circular reference [foo, bar, foo] detected
2184+
*/
2185+
public function testDetectCircularReferences($yaml)
2186+
{
2187+
$this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS);
2188+
}
2189+
2190+
public function circularReferenceProvider()
2191+
{
2192+
$tests = array();
2193+
2194+
$yaml = <<<YAML
2195+
foo:
2196+
- &foo
2197+
- &bar
2198+
bar: foobar
2199+
baz: *foo
2200+
YAML;
2201+
$tests['sequence'] = array($yaml);
2202+
2203+
$yaml = <<<YAML
2204+
foo: &foo
2205+
bar: &bar
2206+
foobar: baz
2207+
baz: *foo
2208+
YAML;
2209+
$tests['mapping'] = array($yaml);
2210+
2211+
$yaml = <<<YAML
2212+
foo: &foo
2213+
bar: &bar
2214+
foobar: baz
2215+
<<: *foo
2216+
YAML;
2217+
$tests['mapping with merge key'] = array($yaml);
2218+
2219+
return $tests;
2220+
}
2221+
21802222
/**
21812223
* @dataProvider indentedMappingData
21822224
*/

0 commit comments

Comments
 (0)