Issues (8)

Security Analysis    no request data  

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/Rauth.php (8 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
namespace SitePoint;
4
5
use SitePoint\Rauth\ArrayCache;
6
use SitePoint\Rauth\Cache;
7
use SitePoint\Rauth\Exception\AuthException;
8
use SitePoint\Rauth\Exception\Reason;
9
10
class Rauth implements RauthInterface
11
{
12
    const REGEX = '/@auth-([\w-]+)\s?\s(.+)/';
13
14
    const MODE_OR = 'or';
15
    const MODE_AND = 'and';
16
    const MODE_NONE = 'none';
17
18
    const MODES = [
19
        self::MODE_AND,
20
        self::MODE_NONE,
21
        self::MODE_OR,
22
    ];
23
24
    /** @var string */
25
    private $defaultMode = self::MODE_OR;
26
27
    /** @var Cache */
28
    private $cache;
29
30 78
    public function __construct(Cache $c = null)
31
    {
32 78
        if ($c) {
33 1
            $this->cache = $c;
34
        }
35 78
    }
36
37
    /**
38
     * Set a default mode for auth blocks without one defined.
39
     *
40
     * Default is MODE_OR
41
     *
42
     * @param string $mode
43
     * @return RauthInterface
44
     */
45 2
    public function setDefaultMode(string $mode = null) : RauthInterface
46
    {
47 2
        if (!in_array($mode, self::MODES)) {
48 1
            throw new \InvalidArgumentException(
49 1
                'Mode ' . $mode . ' not accepted!'
50
            );
51
        }
52 1
        $this->defaultMode = $mode;
53
54 1
        return $this;
55
    }
56
57
    /**
58
     * Inject Cache instance
59
     *
60
     * @param Cache $c
61
     * @return RauthInterface
62
     */
63 74
    public function setCache(Cache $c) : RauthInterface
64
    {
65 74
        $this->cache = $c;
66
67 74
        return $this;
68
    }
69
70
    /**
71
     * Only used by the class.
72
     *
73
     * Could have user property directly, but having default cache is convenient
74
     *
75
     * @internal
76
     * @return null|Cache
77
     */
78 74
    private function getCache()
79
    {
80 74
        if ($this->cache === null) {
81 74
            $this->setCache(new ArrayCache());
82
        }
83
84 74
        return $this->cache;
85
    }
86
87
    /**
88
     * Used to extract the @auth blocks from a class or method
89
     *
90
     * The auth prefix is stripped, and the remaining values are saved as
91
     * key => value pairs.
92
     *
93
     * @param $class
94
     * @param string|null $method
95
     * @return array
96
     */
97 75
    public function extractAuth($class, string $method = null) : array
98
    {
99 75
        if (!is_string($class) && !is_object($class)) {
100 1
            throw new \InvalidArgumentException(
101 1
                'Class must be string or object!'
102
            );
103
        }
104
105 74
        $className = (is_string($class)) ? $class : get_class($class);
106 74
        $sig = ($method) ? $className . '::' . $method : $className;
107
108
        // Class auths haven't been cached yet
109 74 View Code Duplication
        if (!$this->getCache()->has($className)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
110 74
            $r = new \ReflectionClass($className);
111 74
            preg_match_all(self::REGEX, $r->getDocComment(), $matchC);
112 74
            $this->getCache()->set($className, $this->normalize((array)$matchC));
113
        }
114
115
        // Method auths haven't been cached yet
116 74 View Code Duplication
        if (!$this->getCache()->has($sig)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117 64
            $r = new \ReflectionMethod($className, $method);
118 64
            preg_match_all(self::REGEX, $r->getDocComment(), $matchC);
119 64
            $this->getCache()->set($sig, $this->normalize((array)$matchC));
120
        }
121
122 74
        return ($this->getCache()->get($sig) == [])
123 18
            ? $this->getCache()->get($className)
124 74
            : $this->getCache()->get($sig);
125
    }
126
127
    /**
128
     * Turns a pregexed array of auth blocks into a decent array
129
     *
130
     * Internal use only - @see Rauth::extractAuth
131
     *
132
     * @internal
133
     * @param array $matches
134
     * @return array
135
     */
136 74
    private function normalize(array $matches) : array
137
    {
138 74
        $keys = $matches[1];
139 74
        $values = $matches[2];
140
141 74
        $return = [];
142
143 74
        foreach ($keys as $i => $key) {
144 72
            $key = strtolower(trim($key));
145
146 72
            if ($key == 'mode') {
147 48
                $value = strtolower($values[$i]);
148
            } else {
149 72
                $value = array_map(
150 72
                    function ($el) {
151 72
                        return trim($el, ', ');
152 72
                    },
153 72
                    explode(',', $values[$i])
154
                );
155
            }
156 72
            $return[$key] = $value;
157
        }
158
159 74
        return $return;
160
    }
161
162
163
    /**
164
     * Either passes or fails an authorization attempt.
165
     *
166
     * The first two arguments are the class/method pair to inspect for @auth
167
     * tags, and `$attr` are attributes to compare the @auths against.
168
     *
169
     * Depending on the currently selected mode (either default - for that
170
     * you should @see Rauth::setDefaultMode, or defined in the @auths), it will
171
     * evaluate the arrays against one another and come to a conclusion.
172
     *
173
     * @param $class
174
     * @param string|null $method
175
     * @param array $attr
176
     * @return bool
177
     * @throws \InvalidArgumentException
178
     * @throws AuthException
179
     */
180 68
    public function authorize(
181
        $class,
182
        string $method = null,
183
        array $attr = []
184
    ) : bool {
185
    
186 68
        $auth = $this->extractAuth($class, $method);
187
188
        // Class / method has no rules - allow all
189 67
        if (empty($auth)) {
190 2
            return true;
191
        }
192
193
        // Store mode, remove from auth array
194 65
        $mode = $auth['mode'] ?? $this->defaultMode;
195 65
        $e = new AuthException($mode);
196 65
        unset($auth['mode']);
197
198
        // Handle bans, remove them from auth
199 65
        $this->handleBans($auth, $attr);
200
201
        switch ($mode) {
202 63
            case self::MODE_AND:
0 ignored issues
show
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
203
                // All values in all arrays must match
204 17
                foreach ($auth as $set => $values) {
205 17
                    if (!isset($attr[$set])) {
206 15
                        $e->addReason(new Reason(
207
                            $set,
208 15
                            [],
209
                            $values
210
                        ));
211
                    } else {
212 5
                        $attr[$set] = (array)$attr[$set];
213 5
                        sort($values);
214 5
                        sort($attr[$set]);
215 5
                        if ($values != $attr[$set]) {
216 1
                            $e->addReason(new Reason(
217
                                $set,
218 17
                                $attr[$set],
219
                                $values
220
                            ));
221
                        }
222
                    }
223
                }
224
225 17
                if ($e->hasReasons()) {
226 15
                    throw $e;
227
                }
228 2
                return true;
229 46 View Code Duplication
            case self::MODE_NONE:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
230
                // There must be no overlap between any of the array values
231
232 13
                foreach ($auth as $set => $values) {
233 13
                    if (isset($attr[$set]) && count(
234
                        array_intersect(
235 13
                            (array)$attr[$set],
236
                            $values
237
                        )
238
                    )
239
                    ) {
240 4
                        $e->addReason(new Reason(
241
                            $set,
242 13
                            (array)($attr[$set] ?? []),
243
                            $values
244
                        ));
245
                    }
246
                }
247
248 13
                if ($e->hasReasons()) {
249 4
                    throw $e;
250
                }
251 9
                return true;
252 33 View Code Duplication
            case self::MODE_OR:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
253
                // At least one match must be present
254 32
                foreach ($auth as $set => $values) {
255 32
                    if (isset($attr[$set]) && count(
256
                        array_intersect(
257 32
                            (array)$attr[$set],
258
                            $values
259
                        )
260
                    )
261
                    ) {
262 19
                        return true;
263
                    }
264 22
                    $e->addReason(new Reason(
265
                        $set,
266 22
                        (array)($attr[$set] ?? []),
267
                        $values
268
                    ));
269
                }
270
271 13
                throw $e;
272
            default:
0 ignored issues
show
DEFAULT statements must be defined using a colon

As per the PSR-2 coding standard, default statements should not be wrapped in curly braces.

switch ($expr) {
    default: { //wrong
        doSomething();
        break;
    }
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
273 1
                throw new \InvalidArgumentException('Durrrr');
274
        }
275
276
    }
277
278 65
    private function handleBans(&$auth, $attr)
279
    {
280 65
        foreach ($auth as $set => $values) {
281 64
            if (strpos($set, 'ban-') === 0) {
282 19
                $key = str_replace('ban-', '', $set);
283 19
                if (isset($attr[$key]) && array_intersect(
284 19
                    (array)$attr[$key],
285
                    $values
286
                )
287
                ) {
288 2
                    $exception = new AuthException('ban');
289 2
                    throw $exception->addReason(new Reason(
290
                        $key,
291 2
                        (array)$attr[$key],
292
                        $values
293
                    ));
294
                }
295 64
                unset($auth[$set]);
296
            }
297
        }
298
299 63
    }
300
}
301