From e9b91b1798fde385aafc0512865b1c11b0249069 Mon Sep 17 00:00:00 2001
From: Robin Appelman <icewind@owncloud.com>
Date: Tue, 18 Aug 2015 15:35:02 +0200
Subject: [PATCH] verify the path in the autoloader

---
 lib/autoloader.php       | 33 +++++++++++++++++-
 lib/base.php             | 19 ++++++++---
 tests/bootstrap.php      |  2 ++
 tests/lib/autoloader.php |  2 +-
 tests/lib/template.php   | 72 ++++++++++++++++++++--------------------
 5 files changed, 86 insertions(+), 42 deletions(-)

diff --git a/lib/autoloader.php b/lib/autoloader.php
index 23285f61e7..010318a65b 100644
--- a/lib/autoloader.php
+++ b/lib/autoloader.php
@@ -34,12 +34,33 @@ class Autoloader {
 
 	private $classPaths = array();
 
+	private $validRoots = [];
+
 	/**
 	 * Optional low-latency memory cache for class to path mapping.
+	 *
 	 * @var \OC\Memcache\Cache
 	 */
 	protected $memoryCache;
 
+	/**
+	 * Autoloader constructor.
+	 *
+	 * @param string[] $validRoots
+	 */
+	public function __construct(array $validRoots) {
+		$this->validRoots = $validRoots;
+	}
+
+	/**
+	 * Add a path to the list of valid php roots for auto loading
+	 *
+	 * @param string $root
+	 */
+	public function addValidRoot($root) {
+		$this->validRoots[] = $root;
+	}
+
 	/**
 	 * disable the usage of the global classpath \OC::$CLASSPATH
 	 */
@@ -102,6 +123,15 @@ class Autoloader {
 		return $paths;
 	}
 
+	protected function isValidPath($fullPath) {
+		foreach ($this->validRoots as $root) {
+			if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') {
+				return true;
+			}
+		}
+		throw new \Exception('Path not allowed');
+	}
+
 	/**
 	 * Load the specified class
 	 *
@@ -119,7 +149,7 @@ class Autoloader {
 			$pathsToRequire = array();
 			foreach ($this->findClass($class) as $path) {
 				$fullPath = stream_resolve_include_path($path);
-				if ($fullPath) {
+				if ($fullPath && $this->isValidPath($fullPath)) {
 					$pathsToRequire[] = $fullPath;
 				}
 			}
@@ -138,6 +168,7 @@ class Autoloader {
 
 	/**
 	 * Sets the optional low-latency cache for class to path mapping.
+	 *
 	 * @param \OC\Memcache\Cache $memoryCache Instance of memory cache.
 	 */
 	public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null) {
diff --git a/lib/base.php b/lib/base.php
index aceac2e53c..9cf0228bbd 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -115,9 +115,6 @@ class OC {
 	 * the app path list is empty or contains an invalid path
 	 */
 	public static function initPaths() {
-		// calculate the root directories
-		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
-
 		// ensure we can find OC_Config
 		set_include_path(
 			OC::$SERVERROOT . '/lib' . PATH_SEPARATOR .
@@ -519,10 +516,20 @@ class OC {
 	}
 
 	public static function init() {
+		// calculate the root directories
+		OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4));
+
 		// register autoloader
 		$loaderStart = microtime(true);
 		require_once __DIR__ . '/autoloader.php';
-		self::$loader = new \OC\Autoloader();
+		self::$loader = new \OC\Autoloader([
+			OC::$SERVERROOT . '/lib',
+			OC::$SERVERROOT . '/core',
+			OC::$SERVERROOT . '/settings',
+			OC::$SERVERROOT . '/ocs',
+			OC::$SERVERROOT . '/ocs-provider',
+			OC::$SERVERROOT . '/3rdparty'
+		]);
 		spl_autoload_register(array(self::$loader, 'load'));
 		$loaderEnd = microtime(true);
 
@@ -545,6 +552,10 @@ class OC {
 			exit();
 		}
 
+		foreach(OC::$APPSROOTS as $appRoot) {
+			self::$loader->addValidRoot($appRoot['path']);
+		}
+
 		// setup the basic server
 		self::$server = new \OC\Server(\OC::$WEBROOT);
 		\OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd);
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index bd94ca6775..8bca05b1a1 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -8,6 +8,8 @@ if ($configDir) {
 
 require_once __DIR__ . '/../lib/base.php';
 
+\OC::$loader->addValidRoot(OC::$SERVERROOT . '/tests');
+
 // load minimum set of apps
 OC_App::loadApps(array('authentication'));
 OC_App::loadApps(array('filesystem', 'logging'));
diff --git a/tests/lib/autoloader.php b/tests/lib/autoloader.php
index bf63094a9e..6d9d3bd8eb 100644
--- a/tests/lib/autoloader.php
+++ b/tests/lib/autoloader.php
@@ -16,7 +16,7 @@ class AutoLoader extends TestCase {
 
 	protected function setUp() {
 		parent::setUp();
-		$this->loader = new \OC\AutoLoader();
+		$this->loader = new \OC\AutoLoader([]);
 	}
 
 	public function testLeadingSlashOnClassName() {
diff --git a/tests/lib/template.php b/tests/lib/template.php
index db58238eae..6e62d3955f 100644
--- a/tests/lib/template.php
+++ b/tests/lib/template.php
@@ -1,31 +1,31 @@
 <?php
-/**
-* ownCloud
-*
-* @author Bernhard Posselt
-* @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com>
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
-*
-*/
 
+/**
+ * ownCloud
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
 class Test_TemplateFunctions extends \Test\TestCase {
 
 	protected function setUp() {
 		parent::setUp();
 
-		$loader = new \OC\Autoloader();
+		$loader = new \OC\Autoloader([OC::$SERVERROOT . '/lib']);
 		$loader->load('OC_Template');
 	}
 
@@ -60,7 +60,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 	// ---------------------------------------------------------------------------
 	// Test relative_modified_date with dates only
 	// ---------------------------------------------------------------------------
-	public function testRelativeDateToday(){
+	public function testRelativeDateToday() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -74,7 +74,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('today', $result);
 	}
 
-	public function testRelativeDateYesterday(){
+	public function testRelativeDateYesterday() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 24 * 3600;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -88,7 +88,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('yesterday', $result);
 	}
 
-	public function testRelativeDate2DaysAgo(){
+	public function testRelativeDate2DaysAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 48 * 3600;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -102,7 +102,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('2 days ago', $result);
 	}
 
-	public function testRelativeDateLastMonth(){
+	public function testRelativeDateLastMonth() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 31;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -115,7 +115,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('last month', $result);
 	}
 
-	public function testRelativeDateMonthsAgo(){
+	public function testRelativeDateMonthsAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 65;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -128,7 +128,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('4 months ago', $result);
 	}
 
-	public function testRelativeDateLastYear(){
+	public function testRelativeDateLastYear() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 365;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -141,7 +141,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('last year', $result);
 	}
 
-	public function testRelativeDateYearsAgo(){
+	public function testRelativeDateYearsAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 365.25 * 2;
 		$result = (string)relative_modified_date($elementTime, $currentTime, true);
@@ -158,7 +158,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 	// Test relative_modified_date with timestamps only (date + time value)
 	// ---------------------------------------------------------------------------
 
-	public function testRelativeTimeSecondsAgo(){
+	public function testRelativeTimeSecondsAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 5;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -166,7 +166,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('seconds ago', $result);
 	}
 
-	public function testRelativeTimeMinutesAgo(){
+	public function testRelativeTimeMinutesAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 190;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -174,7 +174,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('3 minutes ago', $result);
 	}
 
-	public function testRelativeTimeHoursAgo(){
+	public function testRelativeTimeHoursAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 7500;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -182,7 +182,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('2 hours ago', $result);
 	}
 
-	public function testRelativeTime2DaysAgo(){
+	public function testRelativeTime2DaysAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 48 * 3600;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -196,7 +196,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('2 days ago', $result);
 	}
 
-	public function testRelativeTimeLastMonth(){
+	public function testRelativeTimeLastMonth() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 31;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -209,7 +209,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('last month', $result);
 	}
 
-	public function testRelativeTimeMonthsAgo(){
+	public function testRelativeTimeMonthsAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 65;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -222,7 +222,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('4 months ago', $result);
 	}
 
-	public function testRelativeTimeLastYear(){
+	public function testRelativeTimeLastYear() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 365;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
@@ -235,7 +235,7 @@ class Test_TemplateFunctions extends \Test\TestCase {
 		$this->assertEquals('last year', $result);
 	}
 
-	public function testRelativeTimeYearsAgo(){
+	public function testRelativeTimeYearsAgo() {
 		$currentTime = 1380703592;
 		$elementTime = $currentTime - 86400 * 365.25 * 2;
 		$result = (string)relative_modified_date($elementTime, $currentTime, false);
-- 
GitLab