This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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
|
|||
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 |
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: