Issues (2)

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.

Voter/CallableCollectionVoter.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
namespace Yokai\SecurityExtraBundle\Voter;
4
5
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
6
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
7
use Yokai\SecurityExtraBundle\Exception\LogicException;
8
9
/**
10
 * @author Yann Eugoné <[email protected]>
11
 */
12
class CallableCollectionVoter extends Voter
13
{
14
    /**
15
     * Attribute list this is supporting.
16
     *
17
     * @var array<string>
18
     */
19
    private $supportedAttributes;
20
21
    /**
22
     * Subject types list this is supporting.
23
     *
24
     * @var array<string>
25
     */
26
    private $supportedSubjects;
27
28
    /**
29
     * Callable collection this must call.
30
     *
31
     * @var array<mixed>
32
     */
33
    private $callables;
34
35
    /**
36
     * @param array<string> $supportedAttributes Attribute list this is supporting
37
     * @param array<string> $supportedSubjects   Subject types list this is supporting
38
     * @param array<mixed>  $callables           Callable collection this must call
39
     */
40 9
    public function __construct($supportedAttributes, $supportedSubjects, $callables)
41
    {
42 9
        $this->supportedAttributes = $supportedAttributes;
43 9
        $this->supportedSubjects = $supportedSubjects;
44 9
        $this->callables = $callables;
45 9
    }
46
47
    /**
48
     * @inheritDoc
49
     */
50 9
    protected function supports($attribute, $subject)
51
    {
52
        // if at least one supported attribute is configured
53
        // check if provided attribute is in that list
54 9
        if (count($this->supportedAttributes) > 0 && !in_array($attribute, $this->supportedAttributes, true)) {
55 3
            return false;
56
        }
57
58
        // if there is no subject
59
        // of if there is not at least one supported subject
60
        // this is supporting
61 9
        if ($subject === null || count($this->supportedSubjects) === 0) {
62 6
            return true;
63
        }
64
65
        // iterate over supported subjects
66 4
        foreach ($this->supportedSubjects as $supportedSubject) {
67
            // if supported subject is a class (or interface)
68
            // this supports if subject is an instance of
69 4
            if (class_exists($supportedSubject)) {
70 4
                if ($subject instanceof $supportedSubject) {
71 4
                    return true;
72
                }
73
74 2
                continue;
75
            }
76
77
            // if supported subject is not a class, it must be an internal type
78
            // this support if subject type is the same
79 1
            if (gettype($subject) === $supportedSubject) {
80 1
                return true;
81
            }
82
        }
83
84
        // supported attribute but unsupported subject
85
86 1
        return false;
87
    }
88
89
    /**
90
     * @inheritDoc
91
     */
92 9
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
93
    {
94
        // iterate over configured callables
95 9
        foreach ($this->callables as $callable) {
96 9
            $callable = $this->normalizeCallable($callable, $subject);
97 5
            $parameters = $this->gatherParameters($callable, $attribute, $subject, $token);
98
99
            // if one callable returns falsy result
100
            // this deny access
101 4
            if (!(bool) call_user_func_array($callable, $parameters)) {
102 4
                return false;
103
            }
104
        }
105
106
        // no callable returns falsy result
107
        // this grand access
108
109 4
        return true;
110
    }
111
112
    /**
113
     * Normalizes a callable.
114
     * Will return a callable array. See http://php.net/manual/en/language.types.callable.php .
115
     *
116
     * @param string|object|array $callable The callable to normalize
117
     * @param mixed               $subject  The subject being voting on
118
     *
119
     * @return array The normalized callable
120
     * @throws LogicException
121
     */
122 9
    private function normalizeCallable($callable, $subject)
0 ignored issues
show
This operation has 1400 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
123
    {
124
        // callable is a string
125
        // it should be a method to call on subject
126 9
        if (is_string($callable)) {
127 3
            if (!is_object($subject) || !is_callable([$subject, $callable])) {
128 1
                throw new LogicException(
129
                    sprintf(
130 1
                        'Provided string callable "%s", but subject "%s" has no callable method with that name.',
131 1
                        (string) $callable,
132 1
                        is_object($subject) ? get_class($subject) : gettype($subject)
133
                    )
134
                );
135
            }
136
137 2
            return [$subject, $callable];
138
        }
139
140
        // callable is an object
141
        // it should have an __invoke method
142 8
        if (is_object($callable)) {
143 5
            if (!is_callable([$callable, '__invoke'])) {
144 1
                throw new LogicException(
145
                    sprintf(
146 1
                        'Provided object callable "%s", but it has no "__invoke" method.',
147 1
                        is_object($callable) ? get_class($callable) : gettype($callable)
148
                    )
149
                );
150
            }
151
152 4
            return [$callable, '__invoke'];
153
        }
154
155
        // callable is an array
156
        // it should be an array with [0] and [1]
157 5
        if (is_array($callable)) {
158 4
            if (!isset($callable[0]) || !isset($callable[1]) || !is_callable([$callable[0], $callable[1]])) {
159 1
                throw new LogicException('Provided array callable, but it is not callable.');
160
            }
161
162 3
            return [$callable[0], $callable[1]];
163
        }
164
165 1
        throw new LogicException(
166
            sprintf(
167 1
                'Unable to normalize callable "%". Please review your configuration.',
168 1
                is_object($callable) ? get_class($callable) : gettype($callable)
169
            )
170
        );
171
    }
172
173
    /**
174
     * Analyzes callable and determine the required parameters.
175
     *
176
     * @param array          $callable  The callable for which to gather parameters
177
     * @param string         $attribute The attribute being voting for
178
     * @param mixed          $subject   The subject being voting on
179
     * @param TokenInterface $token     The authentication being voting for
180
     *
181
     * @return array<mixed> The parameters list
182
     * @throws LogicException
183
     */
184 5
    private function gatherParameters($callable, $attribute, $subject, TokenInterface $token)
185
    {
186 5
        if ($callable[0] instanceof \Closure) {
187
            // don't know why but, it seems that ['\Closure', '__invoke'] is not ok with \ReflectionMethod
188 3
            $reflection = new \ReflectionFunction($callable[0]);
189
        } else {
190 3
            $reflection = new \ReflectionMethod(get_class($callable[0]), $callable[1]);
191
        }
192
193 5
        $parameters = [];
194
195
        // iterating over all parameters for method
196 5
        foreach ($reflection->getParameters() as $parameter) {
197 4
            $parameterType = $parameter->getType();
198 4
            if (method_exists($parameterType, 'getName')) {
199
                $parameterType = $parameterType->getName();
200
            } else {
201 4
                $parameterType = (string) $parameterType; // PHP < 7.1 supports
202
            }
203 4
            $parameterName = $parameter->getName();
204 4
            $parameterPosition = $parameter->getPosition();
205
            switch (true) {
206
                // attribute is a bit tricky, cannot use any type to determine whether or not it is required
207
                // if the parameter name is "attribute" this assume it should be provided
208
                // adding subject to required parameters
209 4
                case $parameterName === 'attribute':
210 2
                    $parameters[$parameterPosition] = $attribute;
211 2
                    break;
212
213
                // parameter looks like the subject being voting on
214
                // adding subject to required parameters
215 4
                case is_a($subject, $parameterType) || gettype($subject) === $parameterType:
216 3
                    $parameters[$parameterPosition] = $subject;
217 3
                    break;
218
219
                // parameter looks like a security token
220
                // adding token to required parameters
221 4
                case is_a($token, $parameterType):
222 3
                    $parameters[$parameterPosition] = $token;
223 3
                    break;
224
225
                // parameter looks like a security user
226
                // adding user to required parameters
227 4
                case is_a($token->getUser(), $parameterType):
228 3
                    $parameters[$parameterPosition] = $token->getUser();
229 4
                    break;
230
            }
231
        }
232
233
        // this gathered parameters, but the callable needs something more
234
        // calling with these parameters will probably results to an error
235
        // so throwing an exception is the only thing to do
236 5
        if ($reflection->getNumberOfRequiredParameters() !== count($parameters)) {
237 1
            throw new LogicException(
238
                sprintf(
239 1
                    'The callable method "%s"->"%s"() needs parameters that cannot be provided.',
240 1
                    get_class($callable[0]),
241 1
                    $callable[1]
242
                )
243
            );
244
        }
245
246
        // return sorted parameters
247 4
        ksort($parameters);
248
249 4
        return $parameters;
250
    }
251
}
252