[Routing] fix too much greediness in host-matching regex

This commit is contained in:
Nicolas Grekas 2018-06-27 11:41:32 +02:00
parent cd94ef22bf
commit e16b3023c0
4 changed files with 101 additions and 91 deletions

View File

@ -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}(?");

View File

@ -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));

View File

@ -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));

View File

@ -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());