bug #27736 [Routing] fix too much greediness in host-matching regex (nicolas-grekas)
This PR was merged into the 4.1 branch.
Discussion
----------
[Routing] fix too much greediness in host-matching regex
| Q | A
| ------------- | ---
| Branch? | 4.1
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #27721
| License | MIT
| Doc PR | -
Commits
-------
e16b3023c0
[Routing] fix too much greediness in host-matching regex
This commit is contained in:
commit
3f4644b2f1
@ -379,7 +379,7 @@ EOF;
|
|||||||
$hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.';
|
$hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.';
|
||||||
$state->hostVars = $state->vars;
|
$state->hostVars = $state->vars;
|
||||||
} else {
|
} else {
|
||||||
$hostRegex = '(?:(?:[^.]*+\.)++)';
|
$hostRegex = '(?:(?:[^./]*+\.)++)';
|
||||||
$state->hostVars = array();
|
$state->hostVars = array();
|
||||||
}
|
}
|
||||||
$state->mark += strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?");
|
$state->mark += strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?");
|
||||||
|
@ -82,47 +82,47 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
$matchedPathinfo = $host.'.'.$pathinfo;
|
$matchedPathinfo = $host.'.'.$pathinfo;
|
||||||
$regexList = array(
|
$regexList = array(
|
||||||
0 => '{^(?'
|
0 => '{^(?'
|
||||||
.'|(?:(?:[^.]*+\\.)++)(?'
|
.'|(?:(?:[^./]*+\\.)++)(?'
|
||||||
.'|/foo/(baz|symfony)(*:46)'
|
.'|/foo/(baz|symfony)(*:47)'
|
||||||
.'|/bar(?'
|
.'|/bar(?'
|
||||||
.'|/([^/]++)(*:69)'
|
.'|/([^/]++)(*:70)'
|
||||||
.'|head/([^/]++)(*:89)'
|
.'|head/([^/]++)(*:90)'
|
||||||
.')'
|
.')'
|
||||||
.'|/test/([^/]++)/(?'
|
.'|/test/([^/]++)/(?'
|
||||||
.'|(*:115)'
|
.'|(*:116)'
|
||||||
.')'
|
.')'
|
||||||
.'|/([\']+)(*:131)'
|
.'|/([\']+)(*:132)'
|
||||||
.'|/a/(?'
|
.'|/a/(?'
|
||||||
.'|b\'b/([^/]++)(?'
|
.'|b\'b/([^/]++)(?'
|
||||||
.'|(*:160)'
|
.'|(*:161)'
|
||||||
.'|(*:168)'
|
.'|(*:169)'
|
||||||
.')'
|
.')'
|
||||||
.'|(.*)(*:181)'
|
.'|(.*)(*:182)'
|
||||||
.'|b\'b/([^/]++)(?'
|
.'|b\'b/([^/]++)(?'
|
||||||
.'|(*:204)'
|
.'|(*:205)'
|
||||||
.'|(*:212)'
|
.'|(*:213)'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
.'|/multi/hello(?:/([^/]++))?(*:248)'
|
.'|/multi/hello(?:/([^/]++))?(*:249)'
|
||||||
.'|/([^/]++)/b/([^/]++)(?'
|
.'|/([^/]++)/b/([^/]++)(?'
|
||||||
.'|(*:279)'
|
.'|(*:280)'
|
||||||
.'|(*:287)'
|
.'|(*:288)'
|
||||||
.')'
|
.')'
|
||||||
.'|/aba/([^/]++)(*:309)'
|
.'|/aba/([^/]++)(*:310)'
|
||||||
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
||||||
.'|/route1(?'
|
.'|/route1(?'
|
||||||
.'|3/([^/]++)(*:371)'
|
.'|3/([^/]++)(*:372)'
|
||||||
.'|4/([^/]++)(*:389)'
|
.'|4/([^/]++)(*:390)'
|
||||||
.')'
|
.')'
|
||||||
.')|(?i:c\\.example\\.com)\\.(?'
|
.')|(?i:c\\.example\\.com)\\.(?'
|
||||||
.'|/route15/([^/]++)(*:441)'
|
.'|/route15/([^/]++)(*:442)'
|
||||||
.')|(?:(?:[^.]*+\\.)++)(?'
|
.')|(?:(?:[^./]*+\\.)++)(?'
|
||||||
.'|/route16/([^/]++)(*:488)'
|
.'|/route16/([^/]++)(*:490)'
|
||||||
.'|/a/(?'
|
.'|/a/(?'
|
||||||
.'|a\\.\\.\\.(*:509)'
|
.'|a\\.\\.\\.(*:511)'
|
||||||
.'|b/(?'
|
.'|b/(?'
|
||||||
.'|([^/]++)(*:530)'
|
.'|([^/]++)(*:532)'
|
||||||
.'|c/([^/]++)(*:548)'
|
.'|c/([^/]++)(*:550)'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
@ -132,7 +132,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
foreach ($regexList as $offset => $regex) {
|
foreach ($regexList as $offset => $regex) {
|
||||||
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
||||||
switch ($m = (int) $matches['MARK']) {
|
switch ($m = (int) $matches['MARK']) {
|
||||||
case 115:
|
case 116:
|
||||||
$matches = array('foo' => $matches[1] ?? null);
|
$matches = array('foo' => $matches[1] ?? null);
|
||||||
|
|
||||||
// baz4
|
// baz4
|
||||||
@ -159,7 +159,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
not_bazbaz6:
|
not_bazbaz6:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 160:
|
case 161:
|
||||||
$matches = array('foo' => $matches[1] ?? null);
|
$matches = array('foo' => $matches[1] ?? null);
|
||||||
|
|
||||||
// foo1
|
// foo1
|
||||||
@ -173,14 +173,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
not_foo1:
|
not_foo1:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 204:
|
case 205:
|
||||||
$matches = array('foo1' => $matches[1] ?? null);
|
$matches = array('foo1' => $matches[1] ?? null);
|
||||||
|
|
||||||
// foo2
|
// foo2
|
||||||
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 279:
|
case 280:
|
||||||
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
||||||
|
|
||||||
// foo3
|
// foo3
|
||||||
@ -189,23 +189,23 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$routes = array(
|
$routes = array(
|
||||||
46 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null),
|
47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null),
|
||||||
69 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null),
|
70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null),
|
||||||
89 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
||||||
131 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
132 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
||||||
168 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
169 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
||||||
181 => array(array('_route' => 'overridden'), array('var'), null, null),
|
182 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||||
212 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
213 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||||
248 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||||
287 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||||
309 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
310 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||||
371 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||||
389 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||||
441 => array(array('_route' => 'route15'), array('name'), null, null),
|
442 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||||
488 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||||
509 => array(array('_route' => 'a'), array(), null, null),
|
511 => array(array('_route' => 'a'), array(), null, null),
|
||||||
530 => array(array('_route' => 'b'), array('var'), null, null),
|
532 => array(array('_route' => 'b'), array('var'), null, null),
|
||||||
548 => array(array('_route' => 'c'), array('var'), null, null),
|
550 => array(array('_route' => 'c'), array('var'), null, null),
|
||||||
);
|
);
|
||||||
|
|
||||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||||
@ -231,7 +231,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (548 === $m) {
|
if (550 === $m) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||||
|
@ -119,47 +119,47 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
$matchedPathinfo = $host.'.'.$pathinfo;
|
$matchedPathinfo = $host.'.'.$pathinfo;
|
||||||
$regexList = array(
|
$regexList = array(
|
||||||
0 => '{^(?'
|
0 => '{^(?'
|
||||||
.'|(?:(?:[^.]*+\\.)++)(?'
|
.'|(?:(?:[^./]*+\\.)++)(?'
|
||||||
.'|/foo/(baz|symfony)(*:46)'
|
.'|/foo/(baz|symfony)(*:47)'
|
||||||
.'|/bar(?'
|
.'|/bar(?'
|
||||||
.'|/([^/]++)(*:69)'
|
.'|/([^/]++)(*:70)'
|
||||||
.'|head/([^/]++)(*:89)'
|
.'|head/([^/]++)(*:90)'
|
||||||
.')'
|
.')'
|
||||||
.'|/test/([^/]++)/(?'
|
.'|/test/([^/]++)/(?'
|
||||||
.'|(*:115)'
|
.'|(*:116)'
|
||||||
.')'
|
.')'
|
||||||
.'|/([\']+)(*:131)'
|
.'|/([\']+)(*:132)'
|
||||||
.'|/a/(?'
|
.'|/a/(?'
|
||||||
.'|b\'b/([^/]++)(?'
|
.'|b\'b/([^/]++)(?'
|
||||||
.'|(*:160)'
|
.'|(*:161)'
|
||||||
.'|(*:168)'
|
.'|(*:169)'
|
||||||
.')'
|
.')'
|
||||||
.'|(.*)(*:181)'
|
.'|(.*)(*:182)'
|
||||||
.'|b\'b/([^/]++)(?'
|
.'|b\'b/([^/]++)(?'
|
||||||
.'|(*:204)'
|
.'|(*:205)'
|
||||||
.'|(*:212)'
|
.'|(*:213)'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
.'|/multi/hello(?:/([^/]++))?(*:248)'
|
.'|/multi/hello(?:/([^/]++))?(*:249)'
|
||||||
.'|/([^/]++)/b/([^/]++)(?'
|
.'|/([^/]++)/b/([^/]++)(?'
|
||||||
.'|(*:279)'
|
.'|(*:280)'
|
||||||
.'|(*:287)'
|
.'|(*:288)'
|
||||||
.')'
|
.')'
|
||||||
.'|/aba/([^/]++)(*:309)'
|
.'|/aba/([^/]++)(*:310)'
|
||||||
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
||||||
.'|/route1(?'
|
.'|/route1(?'
|
||||||
.'|3/([^/]++)(*:371)'
|
.'|3/([^/]++)(*:372)'
|
||||||
.'|4/([^/]++)(*:389)'
|
.'|4/([^/]++)(*:390)'
|
||||||
.')'
|
.')'
|
||||||
.')|(?i:c\\.example\\.com)\\.(?'
|
.')|(?i:c\\.example\\.com)\\.(?'
|
||||||
.'|/route15/([^/]++)(*:441)'
|
.'|/route15/([^/]++)(*:442)'
|
||||||
.')|(?:(?:[^.]*+\\.)++)(?'
|
.')|(?:(?:[^./]*+\\.)++)(?'
|
||||||
.'|/route16/([^/]++)(*:488)'
|
.'|/route16/([^/]++)(*:490)'
|
||||||
.'|/a/(?'
|
.'|/a/(?'
|
||||||
.'|a\\.\\.\\.(*:509)'
|
.'|a\\.\\.\\.(*:511)'
|
||||||
.'|b/(?'
|
.'|b/(?'
|
||||||
.'|([^/]++)(*:530)'
|
.'|([^/]++)(*:532)'
|
||||||
.'|c/([^/]++)(*:548)'
|
.'|c/([^/]++)(*:550)'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
.')'
|
.')'
|
||||||
@ -169,7 +169,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
foreach ($regexList as $offset => $regex) {
|
foreach ($regexList as $offset => $regex) {
|
||||||
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
||||||
switch ($m = (int) $matches['MARK']) {
|
switch ($m = (int) $matches['MARK']) {
|
||||||
case 115:
|
case 116:
|
||||||
$matches = array('foo' => $matches[1] ?? null);
|
$matches = array('foo' => $matches[1] ?? null);
|
||||||
|
|
||||||
// baz4
|
// baz4
|
||||||
@ -196,7 +196,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
not_bazbaz6:
|
not_bazbaz6:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 160:
|
case 161:
|
||||||
$matches = array('foo' => $matches[1] ?? null);
|
$matches = array('foo' => $matches[1] ?? null);
|
||||||
|
|
||||||
// foo1
|
// foo1
|
||||||
@ -210,14 +210,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
not_foo1:
|
not_foo1:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 204:
|
case 205:
|
||||||
$matches = array('foo1' => $matches[1] ?? null);
|
$matches = array('foo1' => $matches[1] ?? null);
|
||||||
|
|
||||||
// foo2
|
// foo2
|
||||||
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 279:
|
case 280:
|
||||||
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
$matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null);
|
||||||
|
|
||||||
// foo3
|
// foo3
|
||||||
@ -226,23 +226,23 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$routes = array(
|
$routes = array(
|
||||||
46 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null),
|
47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null),
|
||||||
69 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null),
|
70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null),
|
||||||
89 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null),
|
||||||
131 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
132 => array(array('_route' => 'quoter'), array('quoter'), null, null),
|
||||||
168 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
169 => array(array('_route' => 'bar1'), array('bar'), null, null),
|
||||||
181 => array(array('_route' => 'overridden'), array('var'), null, null),
|
182 => array(array('_route' => 'overridden'), array('var'), null, null),
|
||||||
212 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
213 => array(array('_route' => 'bar2'), array('bar1'), null, null),
|
||||||
248 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null),
|
||||||
287 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null),
|
||||||
309 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
310 => array(array('_route' => 'foo4'), array('foo'), null, null),
|
||||||
371 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null),
|
||||||
389 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null),
|
||||||
441 => array(array('_route' => 'route15'), array('name'), null, null),
|
442 => array(array('_route' => 'route15'), array('name'), null, null),
|
||||||
488 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null),
|
||||||
509 => array(array('_route' => 'a'), array(), null, null),
|
511 => array(array('_route' => 'a'), array(), null, null),
|
||||||
530 => array(array('_route' => 'b'), array('var'), null, null),
|
532 => array(array('_route' => 'b'), array('var'), null, null),
|
||||||
548 => array(array('_route' => 'c'), array('var'), null, null),
|
550 => array(array('_route' => 'c'), array('var'), null, null),
|
||||||
);
|
);
|
||||||
|
|
||||||
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m];
|
||||||
@ -268,7 +268,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (548 === $m) {
|
if (550 === $m) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
$regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m));
|
||||||
|
@ -670,6 +670,16 @@ class UrlMatcherTest extends TestCase
|
|||||||
$this->assertEquals('c', $matcher->match('/admin/api/package.json')['_route']);
|
$this->assertEquals('c', $matcher->match('/admin/api/package.json')['_route']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHostWithDot()
|
||||||
|
{
|
||||||
|
$coll = new RouteCollection();
|
||||||
|
$coll->add('a', new Route('/foo', array(), array(), array(), 'foo.example.com'));
|
||||||
|
$coll->add('b', new Route('/bar/{baz}'));
|
||||||
|
|
||||||
|
$matcher = $this->getUrlMatcher($coll);
|
||||||
|
$this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||||
{
|
{
|
||||||
return new UrlMatcher($routes, $context ?: new RequestContext());
|
return new UrlMatcher($routes, $context ?: new RequestContext());
|
||||||
|
Reference in New Issue
Block a user