From 9d2fe0270bc5322441fd75e5ce9249bf8b277dfb Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 13:48:07 +0900 Subject: [PATCH 1/8] Add URL-to-path and clean path conversion --- common/framework/filters/filenamefilter.php | 20 ++++++ common/framework/url.php | 69 +++++++++++++++++-- tests/unit/framework/URLTest.php | 35 ++++++++++ .../framework/filters/FilenameFilterTest.php | 24 ++++++- 4 files changed, 140 insertions(+), 8 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index 42f5a729c..a498f2a98 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -46,4 +46,24 @@ class FilenameFilter return $filename; } + + /** + * Clean a path to remove ./, ../, trailing slashes, etc. + * + * @param string $path + * @return string + */ + public static function cleanPath($path) + { + $path = str_replace('\\', '/', $path); + $path = preg_replace('@[\?#].+$@', '', $path); + $path = preg_replace('@/{2,}@', '/', $path); + $path = preg_replace('@/\.{3,}/@', '/', $path); + $path = preg_replace('@/(\./)+@', '/', $path); + while (preg_match('@/[^/]+/\.\.(?:/|$)@', $path, $matches)) + { + $path = str_replace($matches[0], '/', $path); + } + return rtrim($path, '/'); + } } diff --git a/common/framework/url.php b/common/framework/url.php index 8fc370bdb..f810c0082 100644 --- a/common/framework/url.php +++ b/common/framework/url.php @@ -18,10 +18,7 @@ class URL */ public static function getCurrentURL(array $changes = array()) { - $proto = \RX_SSL ? 'https://' : 'http://'; - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; - $local = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; - $url = $proto . $host . $local; + $url = self::getCurrentDomainURL(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'); if (count($changes)) { return self::modifyURL($url, $changes); @@ -32,6 +29,19 @@ class URL } } + /** + * Get the current domain. + * + * @param string $path + * @return string + */ + public static function getCurrentDomainURL($path = '/') + { + $proto = \RX_SSL ? 'https://' : 'http://'; + $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; + return $proto . $host . '/' . ltrim($path, '/'); + } + /** * Convert a URL to its canonical format. * @@ -42,9 +52,7 @@ class URL { if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url)) { - $proto = \RX_SSL ? 'https://' : 'http://'; - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; - $url = $proto . $host . \RX_BASEURL . ltrim($url, './'); + $url = self::getCurrentDomainURL(\RX_BASEURL . ltrim($url, './')); } return preg_replace_callback('#^(https?:|)//([^/]+)#i', function($matches) { if ($matches[1] === '') $matches[1] = \RX_SSL ? 'https:' : 'http:'; @@ -125,6 +133,53 @@ class URL } } + /** + * Convert a server-side path to a URL. + * + * This method returns false if the path cannot be converted to a URL, + * e.g. if the path is outside of the document root. + * + * @param string $path + * @return string|false + */ + public static function fromServerPath($path) + { + $cleanpath = Filters\FilenameFilter::cleanPath($path); + if (substr($path, -1) === '/') + { + $cleanpath .= '/'; + } + $root = Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT']); + if ($cleanpath === $root) + { + return self::getCurrentDomainURL('/'); + } + if (starts_with($root . '/', $cleanpath)) + { + return self::getCurrentDomainURL(substr($cleanpath, strlen($root))); + } + return false; + } + + /** + * Convert a URL to a server-side path. + * + * This method returns false if the URL cannot be converted to a server-side path, + * e.g. if the URL belongs to an external domain. + * + * @param string $url + * @return string + */ + public static function toServerPath($url) + { + $url = self::getCanonicalURL($url); + if (!self::isInternalURL($url)) + { + return false; + } + return Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT'] . parse_url($url, \PHP_URL_PATH)); + } + /** * Encode UTF-8 domain into IDNA (punycode) * diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index d36dd5fe6..2faaac28f 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -25,6 +25,17 @@ class URLTest extends \Codeception\TestCase\Test $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); } + public function testGetCurrentDomainURL() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL()); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL('/')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('index.php?foo=bar')); + } + public function testGetCanonicalURL() { $protocol = \RX_SSL ? 'https://' : 'http://'; @@ -82,6 +93,30 @@ class URLTest extends \Codeception\TestCase\Test // This function is checked in Security::checkCSRF() } + public function testURLFromServerPath() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL, Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); + } + + public function testURLToServerPath() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . '/foo/bar?arg=baz')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('./foo/bar')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('foo/bar/../bar')); + $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('http://other.domain.com/')); + $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('//other.domain.com/')); + } + public function testEncodeIdna() { $this->assertEquals('xn--9i1bl3b186bf9e.xn--3e0b707e', Rhymix\Framework\URL::encodeIdna('퓨니코드.한국')); diff --git a/tests/unit/framework/filters/FilenameFilterTest.php b/tests/unit/framework/filters/FilenameFilterTest.php index 7fe095d35..7d1bf117a 100644 --- a/tests/unit/framework/filters/FilenameFilterTest.php +++ b/tests/unit/framework/filters/FilenameFilterTest.php @@ -1,5 +1,7 @@ $to) { - $result = Rhymix\Framework\Filters\FilenameFilter::clean($from); + $result = FilenameFilter::clean($from); $this->assertEquals($to, $result); } } + + public function testFilenameFilterCleanPath() + { + // Remove extra dots and slashes. + $this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo//./baz/../bar.jpg')); + $this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo/././baz/../../foo/bar.jpg')); + $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/..')); + $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/bar/../baz/../../')); + + // Test Windows paths. + $this->assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); + + // Do not remove .. if there is no parent directory. + $this->assertEquals('C:/../foobar', FilenameFilter::cleanPath('C:\\..\foobar\\')); + $this->assertEquals('/../foobar', FilenameFilter::cleanPath('/../foobar/')); + + // Remove query strings and URL fragments. + $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php?foo=bar')); + $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php#baz')); + } } From f983335833057b2fcdb4f3593764fd39dd88741a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 13:54:33 +0900 Subject: [PATCH 2/8] Add function aliases for URL and path conversion --- common/functions.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/common/functions.php b/common/functions.php index 154d60455..6840cdcaa 100644 --- a/common/functions.php +++ b/common/functions.php @@ -133,6 +133,19 @@ function class_basename($class) return basename(str_replace('\\', '/', is_object($class) ? get_class($class) : $class)); } +/** + * Clean a path to remove ./, ../, trailing slashes, etc. + * + * This function is an alias to Rhymix\Framework\Filters\FilenameFilter::cleanPath(). + * + * @param string $path + * @return string + */ +function clean_path($path) +{ + return Rhymix\Framework\Filters\FilenameFilter::cleanPath($path); +} + /** * This function is a shortcut to htmlspecialchars(). * @@ -337,6 +350,34 @@ function base64_decode_urlsafe($str) return @base64_decode(str_pad(strtr($str, '-_', '+/'), ceil(strlen($str) / 4) * 4, '=', STR_PAD_RIGHT)); } +/** + * Convert a server-side path to a URL. + * + * This function is an alias to Rhymix\Framework\URL::fromServerPath(). + * It returns false if the path cannot be converted. + * + * @param string $path + * @return string|false + */ +function path2url($path) +{ + return Rhymix\Framework\URL::fromServerPath($path); +} + +/** + * Convert a URL to a server-side path. + * + * This function is an alias to Rhymix\Framework\URL::toServerPath(). + * It returns false if the URL cannot be converted. + * + * @param string $url + * @return string|false + */ +function url2path($url) +{ + return Rhymix\Framework\URL::toServerPath($url); +} + /** * Convert hexadecimal color codes to an array of R, G, B values. * This function can handle both 6-digit and 3-digit notations, optionally prefixed with '#'. From 139e11df13f0beec2d9fd1ba37a7a8822278ca45 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 14:14:50 +0900 Subject: [PATCH 3/8] Also clean relative paths in cleanPath() --- common/framework/filters/filenamefilter.php | 4 ++++ common/framework/storage.php | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 common/framework/storage.php diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index a498f2a98..becd5e3f7 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -55,6 +55,10 @@ class FilenameFilter */ public static function cleanPath($path) { + if (!preg_match('@^(?:[a-z]:[\\\\/]|\\\\|/)@i', $path)) + { + $path = \RX_BASEDIR . $path; + } $path = str_replace('\\', '/', $path); $path = preg_replace('@[\?#].+$@', '', $path); $path = preg_replace('@/{2,}@', '/', $path); diff --git a/common/framework/storage.php b/common/framework/storage.php new file mode 100644 index 000000000..4fee32b08 --- /dev/null +++ b/common/framework/storage.php @@ -0,0 +1,11 @@ + Date: Tue, 15 Mar 2016 14:15:03 +0900 Subject: [PATCH 4/8] Update unit tests --- tests/unit/framework/URLTest.php | 7 ++++--- tests/unit/framework/filters/FilenameFilterTest.php | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index 2faaac28f..f3d5befb3 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -98,9 +98,10 @@ class URLTest extends \Codeception\TestCase\Test $protocol = \RX_SSL ? 'https://' : 'http://'; $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL, Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath(dirname(dirname(\RX_BASEDIR)))); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); } diff --git a/tests/unit/framework/filters/FilenameFilterTest.php b/tests/unit/framework/filters/FilenameFilterTest.php index 7d1bf117a..562bac0cc 100644 --- a/tests/unit/framework/filters/FilenameFilterTest.php +++ b/tests/unit/framework/filters/FilenameFilterTest.php @@ -50,6 +50,10 @@ class FilenameFilterTest extends \Codeception\TestCase\Test $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/..')); $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/bar/../baz/../../')); + // Test internal paths. + $this->assertEquals(\RX_BASEDIR . 'common/js/debug.js', FilenameFilter::cleanPath('common/js/debug.js')); + $this->assertEquals(\RX_BASEDIR . 'common/js/debug.js', FilenameFilter::cleanPath('./common/js/debug.js')); + // Test Windows paths. $this->assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); @@ -58,7 +62,7 @@ class FilenameFilterTest extends \Codeception\TestCase\Test $this->assertEquals('/../foobar', FilenameFilter::cleanPath('/../foobar/')); // Remove query strings and URL fragments. - $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php?foo=bar')); - $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php#baz')); + $this->assertEquals(\RX_BASEDIR . 'index.php', FilenameFilter::cleanPath('index.php?foo=bar')); + $this->assertEquals(\RX_BASEDIR . 'index.php', FilenameFilter::cleanPath('index.php#baz')); } } From ff0df0a14b1346945d89d219a7cbab92ddfc1900 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 14:41:21 +0900 Subject: [PATCH 5/8] Ensure proper handling of network shares and URLs in cleanPath() --- common/framework/filters/filenamefilter.php | 19 +++++++++++++++++-- .../framework/filters/FilenameFilterTest.php | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index becd5e3f7..dc5fee4eb 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -55,19 +55,34 @@ class FilenameFilter */ public static function cleanPath($path) { - if (!preg_match('@^(?:[a-z]:[\\\\/]|\\\\|/)@i', $path)) + // Convert relative paths to absolute paths. + if (!preg_match('@^(?:/|[a-z]:[\\\\/]|\\\\|https?:)@i', $path)) { $path = \RX_BASEDIR . $path; } + + // Convert backslashes to forward slashes. $path = str_replace('\\', '/', $path); + + // Remove querystrings and URL fragments. $path = preg_replace('@[\?#].+$@', '', $path); - $path = preg_replace('@/{2,}@', '/', $path); + + // Remove duplicate slashes, except at the beginning of a URL. + $path = preg_replace('@(?assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); + $this->assertEquals('//vboxsrv/hello/world', FilenameFilter::cleanPath('\\\\vboxsrv\\hello\\world')); + + // Test absolute URLs. + $this->assertEquals('https://www.rhymix.org/foo/bar', FilenameFilter::cleanPath('https://www.rhymix.org/foo/.//bar')); + $this->assertEquals('//www.rhymix.org/foo/bar', FilenameFilter::cleanPath('//www.rhymix.org/foo/.//bar')); // Do not remove .. if there is no parent directory. $this->assertEquals('C:/../foobar', FilenameFilter::cleanPath('C:\\..\foobar\\')); From b088348be2c0899d8d463d992e6ffe9c9ed98cc7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 18:05:48 +0900 Subject: [PATCH 6/8] More explicitly exclude a series of double dots --- common/framework/filters/filenamefilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index dc5fee4eb..a43fb8ba2 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -77,7 +77,7 @@ class FilenameFilter $path = preg_replace('@/(\./)+@', '/', $path); // Remove double dots and the preceding directory. - while (preg_match('@/[^/]+/\.\.(?:/|$)@', $path, $matches)) + while (preg_match('@/(?!\.\.)[^/]+/\.\.(?:/|$)@', $path, $matches)) { $path = str_replace($matches[0], '/', $path); } From ca7a7b64eaf98a74c12a06990116d220a98567b9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 09:13:31 +0900 Subject: [PATCH 7/8] Use a more realistic server environment in unit tests --- tests/_bootstrap.php | 15 +++- tests/unit/_bootstrap.php | 1 - tests/unit/classes/ContextTest.php | 3 - tests/unit/classes/FileHandlerTest.php | 1 - .../unit/classes/FrontEndFileHandlerTest.php | 53 +++++++-------- tests/unit/classes/OldSecurityTest.php | 1 - tests/unit/classes/TemplateHandlerTest.php | 35 +++++----- tests/unit/framework/SecurityTest.php | 2 - tests/unit/framework/URLTest.php | 68 +++++++------------ 9 files changed, 81 insertions(+), 98 deletions(-) diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php index 529a86e6c..8017ed216 100644 --- a/tests/_bootstrap.php +++ b/tests/_bootstrap.php @@ -1,6 +1,17 @@ loadFile(array('./common/js/common.js', 'body')); $handler->loadFile(array('./common/js/common.js', 'head')); $handler->loadFile(array('./common/js/xml_js_filter.js', 'body')); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -39,8 +38,8 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/xe.css')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/xe/common/css/xe.css' . $this->_filemtime('common/css/xe.css'), 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/xe.css' . $this->_filemtime('common/css/xe.css'), 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -54,10 +53,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/common.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_handler.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -67,10 +66,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/js_app.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/common.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -81,9 +80,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/xml_handler.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); $handler->unloadFile('./common/js/js_app.js', '', 'all'); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -92,9 +91,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie6')); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie7')); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie8')); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie6'); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie7'); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie8'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie6'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie7'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie8'); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -127,9 +126,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/css/common.css', null, 'ie7')); $handler->loadFile(array('./common/css/common.css', null, 'ie8')); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => 'ie6'); - $expected[] = array('file' => '/xe/common/css/common.css','media'=>'all', 'targetie' => 'ie7'); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => 'ie8'); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie6'); + $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'all', 'targetie' => 'ie7'); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie8'); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -139,9 +138,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/css/common.css', 'screen')); $handler->loadFile(array('./common/css/common.css', 'handled')); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/common.css','media'=>'screen', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'handled', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'screen', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'handled', 'targetie' => null); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -151,8 +150,8 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/xe.css')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/xe/files/cache/minify/common.css.xe.min.css', 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/xe/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.xe.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); $result[1]['file'] = preg_replace('/\?\d+$/', '', $result[1]['file']); diff --git a/tests/unit/classes/OldSecurityTest.php b/tests/unit/classes/OldSecurityTest.php index f699b8d1b..85ed5e564 100644 --- a/tests/unit/classes/OldSecurityTest.php +++ b/tests/unit/classes/OldSecurityTest.php @@ -1,5 +1,4 @@ ', - '?>' + '?>' ), // relative path2 array( '', - '?>' + '?>' ), // error case array( @@ -213,7 +210,7 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // issue 584 array( 'mobile', - PHP_EOL . 'if($__Context->oBodex->display_extra_images[\'mobile\'] && $__Context->arr_extra && $__Context->arr_extra->bodex->mobile){ ?>mobile' + PHP_EOL . 'if($__Context->oBodex->display_extra_images[\'mobile\'] && $__Context->arr_extra && $__Context->arr_extra->bodex->mobile){ ?>mobile' ), // issue 831 array( @@ -222,8 +219,8 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test ), // issue 746 array( - '', - '?>' + '', + '?>' ), // issue 696 array( @@ -233,47 +230,47 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->foo->bar){ ?>' + PHP_EOL . 'if($__Context->foo->bar){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( 'a!@#$%^&*()_-=[]{}?/', - PHP_EOL . 'if($__Context->foo->bar > 100){ ?>a!@#$%^&*()_-=[]{}?/' + PHP_EOL . 'if($__Context->foo->bar > 100){ ?>a!@#$%^&*()_-=[]{}?/' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->foo->bar){ ?>' + PHP_EOL . 'if($__Context->foo->bar){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if(!$__Context->module_info->title){ ?>' + PHP_EOL . 'if(!$__Context->module_info->title){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->mi->title){ ?>mi->use){ ?> class="tmp_class" src="/xe/tests/unit/classes/img/common/blank.gif" />' + PHP_EOL . 'if($__Context->mi->title){ ?>mi->use){ ?> class="tmp_class" src="/rhymix/tests/unit/classes/img/common/blank.gif" />' ), array( ' alt', - '?> foo->bar){ ?>alt' + '?> foo->bar){ ?>alt' ), array( '' . "\n" . ' alt', - '?>' . PHP_EOL . ' foo->bar){ ?>alt' + '?>' . PHP_EOL . ' foo->bar){ ?>alt' ), array( 'asf ', '?>asf ' ), array( - '', - '?>' + '', + '?>' ), array( - 'asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf', - '?>asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf' + 'asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf', + '?>asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf' ), array( 'asdf src="../img/img.gif" asdf', diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index b28d99ada..f0db07f0d 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -100,11 +100,9 @@ class SecurityTest extends \Codeception\TestCase\Test $_SERVER['REQUEST_METHOD'] = 'POST'; $this->assertTrue(Rhymix\Framework\Security::checkCSRF()); - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; $_SERVER['HTTP_REFERER'] = 'http://www.foobar.com/'; $this->assertFalse(Rhymix\Framework\Security::checkCSRF()); - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; $this->assertTrue(Rhymix\Framework\Security::checkCSRF('http://www.rhymix.org/')); } diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index f3d5befb3..ed60020ee 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -4,48 +4,42 @@ class URLTest extends \Codeception\TestCase\Test { public function testGetCurrentURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $_SERVER['REQUEST_URI'] = '/index.php?foo=bar&xe=sucks'; - $full_url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $old_request_uri = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = '/rhymix/index.php?foo=bar&xe=sucks'; // Getting the current URL - $this->assertEquals($full_url, Rhymix\Framework\URL::getCurrentURL()); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&xe=sucks', Rhymix\Framework\URL::getCurrentURL()); // Adding items to the query string - $this->assertEquals($full_url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::getCurrentURL(array('var' => '1', 'arr' => array(2, 3)))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&xe=sucks&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::getCurrentURL(array('var' => '1', 'arr' => array(2, 3)))); // Removing item from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); // Removing all items from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::getCurrentURL(array('foo' => null, 'xe' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::getCurrentURL(array('foo' => null, 'xe' => null))); // Adding and removing parameters at the same time - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); + + $_SERVER['REQUEST_URI'] = $old_request_uri; } public function testGetCurrentDomainURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL()); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL('/')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('index.php?foo=bar')); + $this->assertEquals('https://www.rhymix.org/', Rhymix\Framework\URL::getCurrentDomainURL()); + $this->assertEquals('https://www.rhymix.org/', Rhymix\Framework\URL::getCurrentDomainURL('/')); + $this->assertEquals('https://www.rhymix.org/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('rhymix/index.php?foo=bar')); } public function testGetCanonicalURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $tests = array( - 'foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - './foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - '/foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - '//www.example.com/foo' => $protocol . 'www.example.com/foo', + 'foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + './foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + '/foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + '//www.example.com/foo' => 'https://www.example.com/foo', 'http://xn--cg4bkiv2oina.com/' => 'http://삼성전자.com/', ); @@ -71,21 +65,17 @@ class URLTest extends \Codeception\TestCase\Test public function testModifyURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $url = $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?foo=bar'; - // Conversion to absolute - $this->assertEquals($url, Rhymix\Framework\URL::modifyURL('./index.php?foo=bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar', $url = Rhymix\Framework\URL::modifyURL('./index.php?foo=bar')); // Adding items to the query string - $this->assertEquals($url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::modifyURL($url, array('var' => '1', 'arr' => array(2, 3)))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::modifyURL($url, array('var' => '1', 'arr' => array(2, 3)))); // Removing item from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::modifyURL($url, array('foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::modifyURL($url, array('foo' => null))); // Adding and removing parameters at the same time - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?l=ko', Rhymix\Framework\URL::modifyURL($url, array('l' => 'ko', 'foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?l=ko', Rhymix\Framework\URL::modifyURL($url, array('l' => 'ko', 'foo' => null))); } public function testIsInternalURL() @@ -95,23 +85,17 @@ class URLTest extends \Codeception\TestCase\Test public function testURLFromServerPath() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals('https://www.rhymix.org/rhymix/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath(dirname(dirname(\RX_BASEDIR)))); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); } public function testURLToServerPath() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php')); - $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . '/foo/bar?arg=baz')); + $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath('http://www.rhymix.org/rhymix/index.php')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('http://www.rhymix.org/rhymix/foo/bar?arg=baz')); $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('./foo/bar')); $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('foo/bar/../bar')); $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('http://other.domain.com/')); From 94859b10460c3ceaca61219e42696dc8b53d0298 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 15:25:32 +0900 Subject: [PATCH 8/8] Improve performance of FilenameFilter::cleanPath() --- common/framework/filters/filenamefilter.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index a43fb8ba2..dd99954c9 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -65,16 +65,16 @@ class FilenameFilter $path = str_replace('\\', '/', $path); // Remove querystrings and URL fragments. - $path = preg_replace('@[\?#].+$@', '', $path); + if (($querystring = strpbrk($path, '?#')) !== false) + { + $path = substr($path, 0, -1 * strlen($querystring)); + } - // Remove duplicate slashes, except at the beginning of a URL. - $path = preg_replace('@(?