diff --git a/CHANGELOG.md b/CHANGELOG.md index 45e8c5d52..6dec33462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.6.4 +## mm/dd/2019 + +1. [](#bugfix) + * Better logic in `Utils::normalizePath` to handle externals properly [#2216](https://github.com/getgrav/grav/issues/2216) + # v1.6.3 ## 04/12/2019 diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index c5728b56f..f233bf13f 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -20,7 +20,9 @@ abstract class Utils { protected static $nonces = []; - protected const ROOTURL_REGEX = '{^(\/*)}'; + protected const ROOTURL_REGEX = '{^((?:http[s]?:\/\/[^\/]+)|(?:\/\/[^\/]+))(.*)}'; + + // ^((?:http[s]?:)?[\/]?(?:\/)) /** * Simple helper method to make getting a Grav URL easier @@ -784,31 +786,43 @@ abstract class Utils */ public static function normalizePath($path) { - - if (Uri::isExternal($path)) { - return $path; + /** @var UniformResourceLocator $locator */ + $locator = Grav::instance()['locator']; + if ($locator->isStream($path)) { + $path = $locator->findResource($path); } $root = ''; preg_match(self::ROOTURL_REGEX, $path, $matches); if ($matches) { - $root = $matches[0]; + $root = $matches[1]; + $path = $matches[2]; } - $clean_path = static::replaceFirstOccurrence($root, '', $path); - $segments = explode('/', trim($clean_path, '/')); - $ret = []; - foreach ($segments as $segment) { - if (($segment === '.') || $segment === '') { - continue; - } - if ($segment === '..') { - array_pop($ret); - } else { - $ret[] = $segment; - } + if (Utils::startsWith($path,'/')) { + $root .= '/'; + $path = ltrim($path, '/'); } - $normalized = $root . implode('/', $ret); + +// $path = ltrim(static::replaceFirstOccurrence($root, '', $path), '/'); + + if (Utils::contains($path, '..')) { + $segments = explode('/', trim($path, '/')); + $ret = []; + foreach ($segments as $segment) { + if (($segment === '.') || $segment === '') { + continue; + } + if ($segment === '..') { + array_pop($ret); + } else { + $ret[] = $segment; + } + } + $path = implode('/', $ret); + } + + $normalized = $root . $path; return $normalized; } diff --git a/tests/unit/Grav/Common/UtilsTest.php b/tests/unit/Grav/Common/UtilsTest.php index 6c034bafd..f3b2a9707 100644 --- a/tests/unit/Grav/Common/UtilsTest.php +++ b/tests/unit/Grav/Common/UtilsTest.php @@ -224,9 +224,19 @@ class UtilsTest extends \Codeception\TestCase\Test $this->assertEquals('test', Utils::normalizePath('../test')); $this->assertEquals('/test', Utils::normalizePath('/../test')); $this->assertEquals('/test2', Utils::normalizePath('/test/../test2')); - $this->assertEquals('/test/test2', Utils::normalizePath('/test/./test2')); + $this->assertEquals('/test3', Utils::normalizePath('/test/../test2/../test3')); + + $this->assertEquals('//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css')); + $this->assertEquals('//use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('//use.fontawesome.com/releases/v5.8.1/css/all.css')); + $this->assertEquals('//use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('//use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot')); + + $this->assertEquals('http://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('http://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css')); + $this->assertEquals('http://use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('http://use.fontawesome.com/releases/v5.8.1/css/all.css')); + $this->assertEquals('http://use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('http://use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot')); + $this->assertEquals('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css', Utils::normalizePath('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css')); - $this->assertEquals('//something.com/../test/test2', Utils::normalizePath('//something.com/../test/test2')); + $this->assertEquals('https://use.fontawesome.com/releases/v5.8.1/css/all.css', Utils::normalizePath('https://use.fontawesome.com/releases/v5.8.1/css/all.css')); + $this->assertEquals('https://use.fontawesome.com/releases/v5.8.1/webfonts/fa-brands-400.eot', Utils::normalizePath('https://use.fontawesome.com/releases/v5.8.1/css/../webfonts/fa-brands-400.eot')); } public function testIsFunctionDisabled()