diff --git a/lib/Pico.php b/lib/Pico.php index 11d3ade..5e5cf83 100644 --- a/lib/Pico.php +++ b/lib/Pico.php @@ -308,7 +308,7 @@ class Pico $this->triggerEvent('onRequestUrl', array(&$this->requestUrl)); // discover requested file - $this->discoverRequestFile(); + $this->requestFile = $this->resolveFilePath($this->requestUrl); $this->triggerEvent('onRequestFile', array(&$this->requestFile)); // load raw file content @@ -735,24 +735,29 @@ class Pico } /** - * Uses the request URL to discover the content file to serve + * Resolves a given file path to its corresponding content file + * + * This method also prevents `content_dir` breakouts using malicious + * request URLs. We don't use `realpath()`, because we neither want to + * check for file existance, nor prohibit symlinks which intentionally + * point to somewhere outside the `content_dir` folder. It is STRONGLY + * RECOMMENDED to use PHP's `open_basedir` feature - always, not just + * with Pico! * * @see Pico::getRequestFile() - * @return void + * @param string $requestUrl path name (likely from a URL) to resolve + * @return string path to the resolved content file */ - protected function discoverRequestFile() + public function resolveFilePath($requestUrl) { $contentDir = $this->getConfig('content_dir'); $contentExt = $this->getConfig('content_ext'); - if (empty($this->requestUrl)) { - $this->requestFile = $contentDir . 'index' . $contentExt; + if (empty($requestUrl)) { + return $contentDir . 'index' . $contentExt; } else { - // prevent content_dir breakouts using malicious request URLs - // we don't use realpath() here because we neither want to check for file existance - // nor prohibit symlinks which intentionally point to somewhere outside the content_dir - // it is STRONGLY RECOMMENDED to use open_basedir - always, not just with Pico! - $requestUrl = str_replace('\\', '/', $this->requestUrl); + // prevent content_dir breakouts + $requestUrl = str_replace('\\', '/', $requestUrl); $requestUrlParts = explode('/', $requestUrl); $requestFileParts = array(); @@ -768,31 +773,29 @@ class Pico } if (empty($requestFileParts)) { - $this->requestFile = $contentDir . 'index' . $contentExt; - return; + return $contentDir . 'index' . $contentExt; } // discover the content file to serve // Note: $requestFileParts neither contains a trailing nor a leading slash - $this->requestFile = $contentDir . implode('/', $requestFileParts); - if (is_dir($this->requestFile)) { + $requestFile = $contentDir . implode('/', $requestFileParts); + if (is_dir($requestFile)) { // if no index file is found, try a accordingly named file in the previous dir // if this file doesn't exist either, show the 404 page, but assume the index // file as being requested (maintains backward compatibility to Pico < 1.0) - $indexFile = $this->requestFile . '/index' . $contentExt; - if (file_exists($indexFile) || !file_exists($this->requestFile . $contentExt)) { - $this->requestFile = $indexFile; - return; + $indexFile = $requestFile . '/index' . $contentExt; + if (file_exists($indexFile) || !file_exists($requestFile . $contentExt)) { + return $indexFile; } } - $this->requestFile .= $contentExt; + return $requestFile . $contentExt; } } /** * Returns the absolute path to the content file to serve * - * @see Pico::discoverRequestFile() + * @see Pico::resolveFilePath() * @return string|null file path */ public function getRequestFile()