From d17ba0e14775f6825b7df4a2d23c21036da06c11 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 19 Mar 2012 15:57:24 +0100 Subject: [PATCH] Fixed base URL detection when request URI contains encoded chars Signed-off-by: Victor Berchet --- .../Component/HttpFoundation/Request.php | 38 +++++-- .../HttpFoundation/Tests/RequestTest.php | 103 ++++++++++++++++-- 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 49126fa6dfb7..09237f6d33e7 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1301,14 +1301,14 @@ protected function prepareBaseUrl() // Does the baseUrl have anything in common with the request_uri? $requestUri = $this->getRequestUri(); - if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) { + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { // full $baseUrl matches - return $baseUrl; + return $prefix; } - if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) { + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, dirname($baseUrl))) { // directory portion of $baseUrl matches - return rtrim(dirname($baseUrl), '/'); + return rtrim($prefix, '/'); } $truncatedRequestUri = $requestUri; @@ -1317,7 +1317,7 @@ protected function prepareBaseUrl() } $basename = basename($baseUrl); - if (empty($basename) || !strpos($truncatedRequestUri, $basename)) { + if (empty($basename) || !strpos(urldecode($truncatedRequestUri), $basename)) { // no match whatsoever; set it blank return ''; } @@ -1378,14 +1378,14 @@ protected function preparePathInfo() $requestUri = substr($requestUri, 0, $pos); } - if ((null !== $baseUrl) && (false === ($pathInfo = substr(urldecode($requestUri), strlen(urldecode($baseUrl)))))) { + if ((null !== $baseUrl) && (false === ($pathInfo = substr($requestUri, strlen($baseUrl))))) { // If substr() returns false then PATH_INFO is set to an empty string return '/'; } elseif (null === $baseUrl) { return $requestUri; } - return (string) $pathInfo; + return rawurldecode((string) $pathInfo); } /** @@ -1423,4 +1423,28 @@ private function setPhpDefaultLocale($locale) } catch (\Exception $e) { } } + + /* + * Returns the prefix as encoded in the string when the string starts with + * the given prefix, false otherwise. + * + * @param string $string The urlencoded string + * @param string $prefix The prefix not encoded + * + * @return string|false The prefix as it is encoded in $string, or false + */ + private function getUrlencodedPrefix($string, $prefix) + { + if (0 !== strpos(rawurldecode($string), $prefix)) { + return false; + } + + $len = strlen($prefix); + + if (preg_match("#^(%[[:xdigit:]]{2}|.){{$len}}#", $string, $match)) { + return $match[0]; + } + + return false; + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index acf76d48a890..29f678a10875 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -751,18 +751,11 @@ public function testGetPathInfo() $this->assertEquals('/path/info', $request->getPathInfo()); - $server = array(); - $server['REQUEST_URI'] = '/path test/info'; - $request->initialize(array(), array(), array(), array(), array(), $server); - - $this->assertEquals('/path test/info', $request->getPathInfo()); - $server = array(); $server['REQUEST_URI'] = '/path%20test/info'; $request->initialize(array(), array(), array(), array(), array(), $server); - $this->assertEquals('/path test/info', $request->getPathInfo()); - + $this->assertEquals('/path%20test/info', $request->getPathInfo()); } public function testGetPreferredLanguage() @@ -946,6 +939,100 @@ private function startTrustingProxyData() Request::trustProxyData(); } + /** + * @dataProvider getBaseUrlData + */ + public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo) + { + $request = Request::create($uri, 'GET', array(), array(), array(), $server); + + $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl'); + $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo'); + } + + public function getBaseUrlData() + { + return array( + array( + '/foo%20bar', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar', + '/', + ), + array( + '/foo%20bar/home', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar', + '/home', + ), + array( + '/foo%20bar/app.php/home', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar/app.php', + '/home', + ), + array( + '/foo%20bar/app.php/home%2Fbaz', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php', + 'SCRIPT_NAME' => '/foo bar/app.php', + 'PHP_SELF' => '/foo bar/app.php', + ), + '/foo%20bar/app.php', + '/home%2Fbaz', + ), + array( + '/foo/bar+baz', + array( + 'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php', + 'SCRIPT_NAME' => '/foo/app.php', + 'PHP_SELF' => '/foo/app.php', + ), + '/foo', + '/bar+baz', + ), + ); + } + + /** + * @dataProvider urlencodedStringPrefixData + */ + public function testUrlencodedStringPrefix($string, $prefix, $expect) + { + $request = new Request; + + $me = new \ReflectionMethod($request, 'getUrlencodedPrefix'); + $me->setAccessible(true); + + $this->assertSame($expect, $me->invoke($request, $string, $prefix)); + } + + public function urlencodedStringPrefixData() + { + return array( + array('foo', 'foo', 'foo'), + array('fo%6f', 'foo', 'fo%6f'), + array('foo/bar', 'foo', 'foo'), + array('fo%6f/bar', 'foo', 'fo%6f'), + array('f%6f%6f/bar', 'foo', 'f%6f%6f'), + array('%66%6F%6F/bar', 'foo', '%66%6F%6F'), + array('fo+o/bar', 'fo+o', 'fo+o'), + array('fo%2Bo/bar', 'fo+o', 'fo%2Bo'), + ); + } + private function stopTrustingProxyData() { $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request');