Issues (320)

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.

src/php/Apix/Router.php (6 issues)

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
/**
4
 *
5
 * This file is part of the Apix Project.
6
 *
7
 * (c) Franck Cassedanne <franck at ouarz.net>
8
 *
9
 * @license     http://opensource.org/licenses/BSD-3-Clause  New BSD License
10
 *
11
 */
12
13
namespace Apix;
14
15
/**
16
 * Apix Router class
17
 *
18
 * @example     $rules = array('/resource/:keyname/:id' => array('controller'=>'someController', 'action'=>'someAction'));
19
 *              $router = Router($rules);
20
 *              $router->init( $_SERVER['REQUEST_URI'] ); // execute router
21
 */
22
class Router
23
{
24
    /**
25
     * Holds the controller string.
26
     * @var	string
27
     */
28
    protected $controller = null;
29
30
    /**
31
     * Holds the current method.
32
     * @var string
33
     */
34
    protected $method = null;
35
36
    /**
37
     * Holds the current action.
38
     * @var	string
39
     */
40
    protected $action = null;
41
42
    /**
43
     * Holds the current action.
44
     * @var string
45
     */
46
    protected $name = null;
47
48
    /**
49
     * Holds all the actions (HTTP methods to CRUD verbs).
50
     * TODO: refactor this!!
51
     * @var array
52
     */
53
    public static $actions = array(
54
        'POST'      => 'onCreate',
55
        'GET'       => 'onRead',
56
        'PUT'       => 'onUpdate',
57
        'DELETE'    => 'onDelete',
58
        'PATCH'     => 'onModify',
59
        'OPTIONS'   => 'onHelp',
60
        'HEAD'      => 'onTest',
61
        'TRACE'     => 'onTrace'
62
    );
63
64
    /**
65
     * Holds an array of router params
66
     * @var	array
67
     */
68
    protected $params = array();
69
70
    private $_rules = array();
71
    private $_defaults = array();
72
73
    /**
74
     * The constructor, set the default routing rules.
75
     *
76
     * @param  array                     $rules
77
     * @param  array                     $defaults
78
     * @throws \InvalidArgumentException
79
     */
80
    public function __construct(array $rules=array(), array $defaults=array())
81
    {
82
        foreach ($rules as $key => $rule) {
83
            if ( is_int($key) ) {
84
                throw new \InvalidArgumentException(
85
                        'Invalid rules array specified (not associative)'
86
                    );
87
            }
88
            $this->_rules[$key] = $rule;
89
        }
90
91
        // merges defaults with required props
92
        $this->_defaults = $defaults+array('controller'=>null, 'action'=>null);
93
94
        // set default properties
95
        foreach ($this->_defaults as $prop => $value) {
96
            $this->$prop = $value;
97
        }
98
    }
99
100
    /**
101
     * Sets the public properties e.g. controller, action, method and params
102
     * in order as per the following:
103
     *      - router rules,
104
     *      - router params,
105
     *      - then router defauls.
106
     *
107
     * @param  string $route
0 ignored issues
show
There is no parameter named $route. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
108
     * @param  array  $rules
109
     * @param  array  $params
110
     * @return void
111
     */
112
    public function setAsProperties(array $rules, array $params)
113
    {
114
        foreach (array_keys($this->_defaults) as $k) {
115
            $value = isset($rules[$k]) ? $rules[$k]    // rules
116
                : (isset($params[$k]) ? $params[$k]    // params
117
                : $this->_defaults[$k]);            // defaults
118
119
            if (property_exists($this, $k)) {
120
                $this->$k = $value;
121
            }
122
            // else {
123
            //     echo 'TEMP';
124
            //     $this->temp->$k = $value;
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
125
            // }
126
        }
127
        $this->params = $params;
128
    }
129
130
    /**
131
     * Maps an URI against the routing table.
132
     *
133
     * @param  string $uri
134
     * @param  array  $params Additional params to merge with the current set (optional)
135
     * @return void
136
     */
137
    public function map($uri, array $params=null)
138
    {
139
        if (!is_null($params)) {
140
            // merge with existing, precedence!
141
            $this->setParams( $this->params+$params );
142
        }
143
144
        foreach ($this->_rules as $route => $rules) {
145
            $params = $this->routeToParamsMatcher($route, $uri);
146
            if ($uri == $route || $params) {
147
                $this->name = $route;
0 ignored issues
show
Documentation Bug introduced by
It seems like $route can also be of type integer. However, the property $name is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
148
149
                if(is_object($rules)) $rules = $rules->toArray();
150
151
                return $this->setAsProperties($rules, $params);
0 ignored issues
show
It seems like $params defined by $this->routeToParamsMatcher($route, $uri) on line 145 can also be of type false; however, Apix\Router::setAsProperties() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
152
            }
153
        }
154
    }
155
156
    /**
157
     * Tries to match a route to an URI params (version with REGEX).
158
     *
159
     * @param  string $route
160
     * @param  string $uri
161
     * @return array
162
     */
163
    public function routeToParamsMatcher($route, $uri)
164
    {
165
        $bits = explode('/', $route);
166
        $paths = explode('/', $uri);
167
        $result = array();
168
169
        // match 1st URI element not a param
170
        if (count($paths) == 2 && count($bits) >2 ) {
171
            if ($paths[1] == $bits[1]) {
172
                return array($paths[1]);
173
            }
174
        }
175
176
        // params
177
        foreach ($bits as $key => $value) {
178
179
            if (
180
                preg_match('/^:[\w]+$/', $value)) {
181
                if (isset($paths[$key])) {
182
                    $value = substr($value, 1); // rm ':'
183
                    $result[$value] = $paths[$key];
184
                }
185
            //} elseif (!isset($paths[$key]) || strcmp($value, $paths[$key]) != 0) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
186
            } elseif ( //Regex based!
187
                isset($paths[$key])
188
                && preg_match('/^:(?P<key>[\w]+)(?:<(?P<regex>.+)>)?/', $value, $m)
189
                && isset($m['regex'])
190
                && preg_match('/^'.$m['regex'].'$/', $paths[$key], $n)
191
            ) {
192
                if(count($n)>1) array_shift($n);
193
                $result[$m['key']] = implode('', $n);
194
            } elseif (strcmp($value, $paths[$key]) != 0) {
195
                return false;
196
            }
197
        }
198
199
        return $result;
200
    }
201
202
    /**
203
     * Sets the current action from a specified method
204
     * or use the method in the current scope.
205
     *
206
     * @param  string $method The method to set the action (optional)
207
     * @return void
208
     */
209
    public function setAction($method=null)
210
    {
211
        if (!is_null($method)) {
212
            $this->setMethod($method);
213
        }
214
        $this->action = isset(self::$actions[$this->method])
215
            ? self::$actions[$this->method]
216
            : null;
217
    }
218
219
    /**
220
     * Returns the current action, or as specified.
221
     *
222
     * @param  string $method A method key (optional)
223
     * @return string
224
     */
225
    public function getAction($method=null)
226
    {
227
        if (isset($method)) {
228
            return isset(self::$actions[$method])
229
                   ? self::$actions[$method]
230
                   : null;
231
        }
232
        if (null === $this->action) {
233
            $this->setAction();
234
        }
235
236
        return $this->action;
237
    }
238
239
    /**
240
     * Returns all the actions.
241
     *
242
     * @return array
243
     */
244
    public function getActions()
245
    {
246
        return self::$actions;
247
    }
248
249
    /**
250
     * Sets the router's params.
251
     *
252
     * @param  array $params
253
     * @return void
254
     */
255
    public function setParams(array $params)
256
    {
257
        $this->params = $params;
258
    }
259
260
    /**
261
     * Returns all the router's params.
262
     *
263
     * @return array
264
     */
265
    public function getParams()
266
    {
267
        return $this->params;
268
    }
269
270
    /**
271
     * Sets the specified router param.
272
     *
273
     * @param  string $key
274
     * @param  string $value
275
     * @return void
276
     */
277
    public function setParam($key, $value)
278
    {
279
        $this->params[$key] = $value;
280
    }
281
282
    /**
283
     * Returns the specified router param.
284
     *
285
     * @param  string                    $key
286
     * @return array
287
     * @throws \InvalidArgumentException
288
     */
289
    public function getParam($key)
290
    {
291
        if (isset($this->params[$key])) {
292
            return $this->params[$key];
293
        }
294
295
        throw new \InvalidArgumentException(
296
                    sprintf('Invalid parameter "%s" requested.', $key)
297
                );
298
    }
299
300
    /**
301
     * Checks a specified router param exists.
302
     *
303
     * @param  string $key A key to check
304
     * @return bolean
305
     */
306
    public function hasParam($key)
307
    {
308
        return isset($this->params[$key]);
309
    }
310
311
    /**
312
     * Sets the controller.
313
     *
314
     * @param  string $controller
315
     * @return void
316
     */
317
    public function setController($controller)
318
    {
319
        $this->controller = $controller;
320
    }
321
322
    /**
323
     * Returns the current controller.
324
     *
325
     * @return string
326
     */
327
    public function getController()
328
    {
329
        return $this->controller;
330
    }
331
332
    /**
333
     * Sets the method.
334
     *
335
     * @param  string $method
336
     * @return void
337
     */
338
    public function setMethod($method)
339
    {
340
        $this->method = strtoupper($method);
341
    }
342
343
    /**
344
     * Returns the current method.
345
     *
346
     * @return string
347
     */
348
    public function getMethod()
349
    {
350
        return $this->method;
351
    }
352
353
    /**
354
     * Returns, if set, the current route name.
355
     * Alternatively, return the current path in scope.
356
     *
357
     * @return string|null
358
     */
359
    public function getName()
360
    {
361
        return isset($this->name) ? $this->name : $this->getPath();
362
    }
363
364
    /**
365
     * Returns, if set, the current route path.
366
     *
367
     * @return string|null
368
     */
369
    public function getPath()
370
    {
371
        return isset($this->path) ? $this->path : null;
0 ignored issues
show
The property path does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
372
    }
373
374
    /**
375
     * Sets the route name.
376
     *
377
     * @param  $name
378
     * @return string|null
379
     */
380
    public function setName($name)
381
    {
382
        $this->name = $name;
383
    }
384
385
}
386