Issues (1798)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

lib/private/Route/Router.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Bernhard Posselt <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @copyright Copyright (c) 2018, ownCloud GmbH
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OC\Route;
33
34
use OCP\ILogger;
35
use OCP\Route\IRouter;
36
use OCP\AppFramework\App;
37
use OCP\Util;
38
use Symfony\Component\Routing\Exception\RouteNotFoundException;
39
use Symfony\Component\Routing\Matcher\UrlMatcher;
40
use Symfony\Component\Routing\Generator\UrlGenerator;
41
use Symfony\Component\Routing\RequestContext;
42
use Symfony\Component\Routing\RouteCollection;
43
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
44
45
class Router implements IRouter {
46
	/** @var RouteCollection[] */
47
	protected $collections = [];
48
	/** @var null|RouteCollection */
49
	protected $collection = null;
50
	/** @var null|string */
51
	protected $collectionName = null;
52
	/** @var null|RouteCollection */
53
	protected $root = null;
54
	/** @var null|UrlGenerator */
55
	protected $generator = null;
56
	/** @var string[] */
57
	protected $routingFiles;
58
	/** @var bool */
59
	protected $loaded = false;
60
	/** @var array */
61
	protected $loadedApps = [];
62
	/** @var ILogger */
63
	protected $logger;
64
	/** @var RequestContext */
65
	protected $context;
66
67
	/**
68
	 * @param ILogger $logger
69
	 */
70
	public function __construct(ILogger $logger, $baseUrl = null) {
71
		$this->logger = $logger;
72
		if ($baseUrl === null) {
73
			$baseUrl = \OC::$WEBROOT;
74
		}
75
		if (!(\getenv('front_controller_active') === 'true')) {
76
			$baseUrl = \rtrim($baseUrl, '/') . '/index.php';
77
		}
78
		if (!\OC::$CLI) {
79
			$method = $_SERVER['REQUEST_METHOD'];
80
		} else {
81
			$method = 'GET';
82
		}
83
		$request = \OC::$server->getRequest();
84
		$host = $request->getServerHost();
85
		$schema = $request->getServerProtocol();
86
		$this->context = new RequestContext($baseUrl, $method, $host, $schema);
87
		// TODO cache
88
		$this->root = $this->getCollection('root');
89
	}
90
91
	/**
92
	 * Get the files to load the routes from
93
	 *
94
	 * @return string[]
95
	 */
96
	public function getRoutingFiles() {
97
		if (!isset($this->routingFiles)) {
98
			$this->routingFiles = [];
99 View Code Duplication
			foreach (\OC_App::getEnabledApps() as $app) {
100
				$appPath = \OC_App::getAppPath($app);
101
				if ($appPath !== false) {
102
					$file = $appPath . '/appinfo/routes.php';
103
					if (\file_exists($file)) {
104
						$this->routingFiles[$app] = $file;
105
					}
106
				}
107
			}
108
		}
109
		return $this->routingFiles;
110
	}
111
112
	/**
113
	 * Loads the routes
114
	 *
115
	 * @param null|string $app
116
	 */
117
	public function loadRoutes($app = null) {
118
		if (\is_string($app)) {
119
			$app = \OC_App::cleanAppId($app);
120
		}
121
122
		$requestedApp = $app;
123
		if ($this->loaded) {
124
			return;
125
		}
126
		if ($app === null) {
127
			$this->loaded = true;
128
			$routingFiles = $this->getRoutingFiles();
129
		} else {
130
			if (isset($this->loadedApps[$app])) {
131
				return;
132
			}
133
			$file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
134
			if ($file !== false && \file_exists($file)) {
135
				$routingFiles = [$app => $file];
136
			} else {
137
				$routingFiles = [];
138
			}
139
		}
140
		\OC::$server->getEventLogger()->start('loadroutes' . $requestedApp, 'Loading Routes');
141
		foreach ($routingFiles as $app => $file) {
142
			if (!isset($this->loadedApps[$app])) {
143
				if (!\OC_App::isAppLoaded($app)) {
144
					// app MUST be loaded before app routes
145
					// try again next time loadRoutes() is called
146
					$this->loaded = false;
147
					continue;
148
				}
149
				$this->loadedApps[$app] = true;
150
				$this->useCollection($app);
151
				$this->requireRouteFile($file, $app);
152
				$collection = $this->getCollection($app);
153
				$collection->addPrefix('/apps/' . $app);
154
				$this->root->addCollection($collection);
155
156
				// Also add the OCS collection
157
				$collection = $this->getCollection($app.'.ocs');
158
				$collection->addPrefix('/ocsapp');
159
				$this->root->addCollection($collection);
0 ignored issues
show
$collection is of type object<Symfony\Component\Routing\RouteCollection>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
160
			}
161
		}
162
		if (!isset($this->loadedApps['core'])) {
163
			$this->loadedApps['core'] = true;
164
			$this->useCollection('root');
165
			require_once __DIR__ . '/../../../settings/routes.php';
166
			require_once __DIR__ . '/../../../core/routes.php';
167
168
			// Also add the OCS collection
169
			$collection = $this->getCollection('root.ocs');
170
			$collection->addPrefix('/ocsapp');
171
			$this->root->addCollection($collection);
172
		}
173
		if ($this->loaded) {
174
			// include ocs routes, must be loaded last for /ocs prefix
175
			require_once __DIR__ . '/../../../ocs/routes.php';
176
			$collection = $this->getCollection('ocs');
177
			$collection->addPrefix('/ocs');
178
			$this->root->addCollection($collection);
179
		}
180
		\OC::$server->getEventLogger()->end('loadroutes' . $requestedApp);
181
	}
182
183
	/**
184
	 * @return string
185
	 * @deprecated
186
	 */
187
	public function getCacheKey() {
188
		return '';
189
	}
190
191
	/**
192
	 * @param string $name
193
	 * @return \Symfony\Component\Routing\RouteCollection
194
	 */
195
	protected function getCollection($name) {
196
		if (!isset($this->collections[$name])) {
197
			$this->collections[$name] = new RouteCollection();
198
		}
199
		return $this->collections[$name];
200
	}
201
202
	/**
203
	 * Sets the collection to use for adding routes
204
	 *
205
	 * @param string $name Name of the collection to use.
206
	 * @return void
207
	 */
208
	public function useCollection($name) {
209
		$this->collection = $this->getCollection($name);
210
		$this->collectionName = $name;
211
	}
212
213
	/**
214
	 * returns the current collection name in use for adding routes
215
	 *
216
	 * @return string the collection name
217
	 */
218
	public function getCurrentCollection() {
219
		return $this->collectionName;
220
	}
221
222
	/**
223
	 * returns the current collections
224
	 *
225
	 * @return RouteCollection[] collections
226
	 */
227
	public function getCollections() {
228
		return $this->collections;
229
	}
230
231
	/**
232
	 * Create a \OC\Route\Route.
233
	 *
234
	 * @param string $name Name of the route to create.
235
	 * @param string $pattern The pattern to match
236
	 * @param array $defaults An array of default parameter values
237
	 * @param array $requirements An array of requirements for parameters (regexes)
238
	 * @return \OC\Route\Route
239
	 */
240
	public function create($name,
241
						   $pattern,
242
						   array $defaults = [],
243
						   array $requirements = []) {
244
		$route = new Route($pattern, $defaults, $requirements);
245
		$this->collection->add($name, $route);
246
		return $route;
247
	}
248
249
	/**
250
	 * Find the route matching $url
251
	 *
252
	 * @param string $url The url to find
253
	 * @throws \Exception
254
	 * @return void
255
	 */
256
	public function match($url) {
257
		if (\substr($url, 0, 6) === '/apps/') {
258
			// empty string / 'apps' / $app / rest of the route
259
			list(, , $app, ) = \explode('/', $url, 4);
260
261
			$app = \OC_App::cleanAppId($app);
262
			\OC::$REQUESTEDAPP = $app;
263
			$this->loadRoutes($app);
264
		} elseif (\substr($url, 0, 13) === '/ocsapp/apps/') {
265
			// empty string / 'ocsapp' / 'apps' / $app / rest of the route
266
			list(, , , $app, ) = \explode('/', $url, 5);
267
268
			$app = \OC_App::cleanAppId($app);
269
			\OC::$REQUESTEDAPP = $app;
270
			$this->loadRoutes($app);
271
		} elseif (\substr($url, 0, 6) === '/core/' or \substr($url, 0, 10) === '/settings/') {
272
			\OC::$REQUESTEDAPP = $url;
273
			if (!\OC::$server->getConfig()->getSystemValue('maintenance', false) && !Util::needUpgrade()) {
274
				\OC_App::loadApps();
275
			}
276
			$this->loadRoutes('core');
277
		} else {
278
			$this->loadRoutes();
279
		}
280
281
		$matcher = new UrlMatcher($this->root, $this->context);
282
283
		if (\OC::$server->getRequest()->getMethod() === "OPTIONS") {
284
			try {
285
				// Checking whether the actual request (one which OPTIONS is pre-flight for)
286
				// Is actually valid
287
				$requestingMethod = \OC::$server->getRequest()->getHeader('Access-Control-Request-Method');
288
				$tempContext = $this->context;
289
				$tempContext->setMethod($requestingMethod);
290
				$tempMatcher = new UrlMatcher($this->root, $tempContext);
291
				$parameters = $tempMatcher->match($url);
292
293
				// Reach here if it's valid
294
				$response = new \OC\OCS\Result(null, 100, 'OPTIONS request successful');
295
				$response = \OC_Response::setOptionsRequestHeaders($response);
296
				\OC_API::respond($response, \OC_API::requestedFormat());
297
298
				// Return since no more processing for an OPTIONS request is required
299
				return;
300
			} catch (ResourceNotFoundException $e) {
301 View Code Duplication
				if (\substr($url, -1) !== '/') {
302
					// We allow links to apps/files? for backwards compatibility reasons
303
					// However, since Symfony does not allow empty route names, the route
304
					// we need to match is '/', so we need to append the '/' here.
305
					try {
306
						$parameters = $matcher->match($url . '/');
307
					} catch (ResourceNotFoundException $newException) {
308
						// If we still didn't match a route, we throw the original exception
309
						throw $e;
310
					}
311
				} else {
312
					throw $e;
313
				}
314
			}
315
		}
316
317
		try {
318
			$parameters = $matcher->match($url);
319
		} catch (ResourceNotFoundException $e) {
320 View Code Duplication
			if (\substr($url, -1) !== '/') {
321
				// We allow links to apps/files? for backwards compatibility reasons
322
				// However, since Symfony does not allow empty route names, the route
323
				// we need to match is '/', so we need to append the '/' here.
324
				try {
325
					$parameters = $matcher->match($url . '/');
326
				} catch (ResourceNotFoundException $newException) {
327
					// If we still didn't match a route, we throw the original exception
328
					throw $e;
329
				}
330
			} else {
331
				throw $e;
332
			}
333
		}
334
335
		\OC::$server->getEventLogger()->start('run_route', 'Run route');
336
		if (isset($parameters['action'])) {
337
			$action = $parameters['action'];
338
			if (!\is_callable($action)) {
339
				throw new \Exception('not a callable action');
340
			}
341
			unset($parameters['action']);
342
			\call_user_func($action, $parameters);
343
		} elseif (isset($parameters['file'])) {
344
			include $parameters['file'];
345
		} else {
346
			throw new \Exception('no action available');
347
		}
348
		\OC::$server->getEventLogger()->end('run_route');
349
	}
350
351
	/**
352
	 * Get the url generator
353
	 *
354
	 * @return \Symfony\Component\Routing\Generator\UrlGenerator
355
	 *
356
	 */
357
	public function getGenerator() {
358
		if ($this->generator !== null) {
359
			return $this->generator;
360
		}
361
362
		return $this->generator = new UrlGenerator($this->root, $this->context);
363
	}
364
365
	/**
366
	 * Generate url based on $name and $parameters
367
	 *
368
	 * @param string $name Name of the route to use.
369
	 * @param array $parameters Parameters for the route
370
	 * @param bool $absolute
371
	 * @return string
372
	 */
373
	public function generate($name,
374
							 $parameters = [],
375
							 $absolute = false) {
376
		$this->loadRoutes();
377
		try {
378
			$referenceType = UrlGenerator::ABSOLUTE_URL;
379
			if ($absolute === false) {
380
				$referenceType = UrlGenerator::ABSOLUTE_PATH;
381
			}
382
			return $this->getGenerator()->generate($name, $parameters, $referenceType);
383
		} catch (RouteNotFoundException $e) {
384
			$this->logger->logException($e);
385
			return '';
386
		}
387
	}
388
389
	/**
390
	 * To isolate the variable scope used inside the $file it is required in it's own method
391
	 *
392
	 * @param string $file the route file location to include
393
	 * @param string $appName
394
	 */
395
	private function requireRouteFile($file, $appName) {
396
		$this->setupRoutes(include_once $file, $appName);
397
	}
398
399
	/**
400
	 * If a routes.php file returns an array, try to set up the application and
401
	 * register the routes for the app. The application class will be chosen by
402
	 * camelcasing the appname, e.g.: my_app will be turned into
403
	 * \OCA\MyApp\AppInfo\Application. If that class does not exist, a default
404
	 * App will be intialized. This makes it optional to ship an
405
	 * appinfo/application.php by using the built in query resolver
406
	 *
407
	 * @param array $routes the application routes
408
	 * @param string $appName the name of the app.
409
	 */
410
	private function setupRoutes($routes, $appName) {
411
		if (\is_array($routes)) {
412
			$appNameSpace = App::buildAppNamespace($appName);
413
414
			$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
415
416
			if (\class_exists($applicationClassName)) {
417
				$application = new $applicationClassName();
418
			} else {
419
				$application = new App($appName);
420
			}
421
422
			$application->registerRoutes($this, $routes);
423
		}
424
	}
425
}
426