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 | namespace Sokil\FraudDetector; |
||
4 | |||
5 | use Sokil\DataType\PriorityList; |
||
6 | use Symfony\Component\EventDispatcher\EventDispatcher; |
||
7 | use Sokil\FraudDetector\Processor\ProcessorInterface; |
||
8 | use Sokil\FraudDetector\Collector\CollectorInterface; |
||
9 | |||
10 | class Detector |
||
11 | { |
||
12 | const STATE_UNCHECKED = 'unckecked'; |
||
13 | const STATE_PASSED = 'checkPassed'; |
||
14 | const STATE_FAILED = 'checkFailed'; |
||
15 | |||
16 | private $state = self::STATE_UNCHECKED; |
||
17 | |||
18 | /** |
||
19 | * |
||
20 | * @var mixed key to identify unique user |
||
21 | */ |
||
22 | private $key; |
||
23 | |||
24 | /** |
||
25 | * |
||
26 | * @var \Sokil\DataType\PriorityList |
||
27 | */ |
||
28 | private $processorDeclarationList; |
||
29 | |||
30 | private $processorList = array(); |
||
31 | |||
32 | private $processorNamespaces = array( |
||
33 | '\Sokil\FraudDetector\Processor', |
||
34 | ); |
||
35 | |||
36 | private $collectorNamespaces = array( |
||
37 | '\Sokil\FraudDetector\Collector', |
||
38 | ); |
||
39 | |||
40 | private $storageNamespaces = array( |
||
41 | '\Sokil\FraudDetector\Storage', |
||
42 | ); |
||
43 | |||
44 | /** |
||
45 | * |
||
46 | * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface |
||
47 | */ |
||
48 | private $eventDispatcher; |
||
49 | |||
50 | public function __construct() |
||
51 | { |
||
52 | $this->processorDeclarationList = new PriorityList(); |
||
53 | $this->eventDispatcher = new EventDispatcher(); |
||
54 | } |
||
55 | |||
56 | /** |
||
57 | * Key that uniquely identify user |
||
58 | * @param type $key |
||
59 | * @return \Sokil\FraudDetector\Detector |
||
60 | */ |
||
61 | public function setKey($key) |
||
62 | { |
||
63 | $this->key = $key; |
||
64 | return $this; |
||
65 | } |
||
66 | |||
67 | public function getKey() |
||
68 | { |
||
69 | if(!$this->key) { |
||
70 | $this->key = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; |
||
71 | } |
||
72 | |||
73 | return $this->key; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Check if request is not fraud |
||
78 | */ |
||
79 | public function check() |
||
80 | { |
||
81 | $this->state = self::STATE_UNCHECKED; |
||
82 | |||
83 | // check all conditions |
||
84 | /* @var $processor \Sokil\FraudDetector\ProcessorInterface */ |
||
85 | foreach($this->processorDeclarationList->getKeys() as $processorName) { |
||
86 | $processor = $this->getProcessor($processorName); |
||
87 | |||
88 | if($processor->isPassed()) { |
||
89 | $processor->afterCheckPassed(); |
||
90 | $this->trigger(self::STATE_PASSED . ':' . $processorName); |
||
91 | |||
92 | // check passed if all processors passes their cheks |
||
93 | if ($this->state === self::STATE_UNCHECKED) { |
||
94 | $this->state = self::STATE_PASSED; |
||
95 | } |
||
96 | } else { |
||
97 | $processor->afterCheckFailed(); |
||
98 | $this->trigger(self::STATE_FAILED . ':' . $processorName); |
||
99 | // if any processor failed - all check failed |
||
100 | $this->state = self::STATE_FAILED; |
||
101 | } |
||
102 | } |
||
103 | |||
104 | $this->trigger($this->state); |
||
105 | } |
||
106 | |||
107 | public function registerProcessorNamespace($namespace) |
||
108 | { |
||
109 | $this->processorNamespaces[] = rtrim($namespace, '\\'); |
||
110 | return $this; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Add processor identified by its name. |
||
115 | * If processor already added, it will be replaced by new instance. |
||
116 | * |
||
117 | * @param string $name name of processor |
||
118 | * @param callable $callable configurator callable |
||
119 | * @return \Sokil\FraudDetector\Detector |
||
120 | */ |
||
121 | public function declareProcessor($name, $callable = null, $priority = 0) |
||
122 | { |
||
123 | $this->processorDeclarationList->set($name, $callable, $priority); |
||
0 ignored issues
–
show
|
|||
124 | return $this; |
||
125 | } |
||
126 | |||
127 | public function addProcssor($name, ProcessorInterface $processor, $priority = 0) |
||
128 | { |
||
129 | $this->declareProcessor($name, null, $priority); |
||
130 | $this->processorList[$name] = $processor; |
||
131 | |||
132 | return $this; |
||
133 | } |
||
134 | |||
135 | public function isProcessorDeclared($name) |
||
136 | { |
||
137 | return $this->processorDeclarationList->has($name); |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Factory method to create new check condition |
||
142 | * |
||
143 | * @param string $name name of check condition |
||
144 | * @return \Sokil\FraudDetector\ProcessorInterface |
||
145 | * @throws \Exception |
||
146 | */ |
||
147 | private function getProcessorClassName($name) |
||
148 | { |
||
149 | $className = ucfirst($name) . 'Processor'; |
||
150 | |||
151 | foreach($this->processorNamespaces as $namespace) { |
||
152 | $fullyQualifiedClassName = $namespace . '\\' . $className; |
||
153 | if(class_exists($fullyQualifiedClassName)) { |
||
154 | return $fullyQualifiedClassName; |
||
155 | } |
||
156 | } |
||
157 | |||
158 | throw new \Exception('Class ' . $fullyQualifiedClassName . ' not found'); |
||
159 | } |
||
160 | |||
161 | public function getProcessor($processorName) |
||
162 | { |
||
163 | if(isset($this->processorList[$processorName])) { |
||
164 | return $this->processorList[$processorName]; |
||
165 | } |
||
166 | |||
167 | // create processor |
||
168 | $processorClassName = $this->getProcessorClassName($processorName); |
||
169 | $processor = new $processorClassName($this); |
||
170 | |||
171 | if (!($processor instanceof ProcessorInterface)) { |
||
172 | throw new \Exception('Processor must inherit ProcessorInterface'); |
||
173 | } |
||
174 | |||
175 | // configure processor |
||
176 | $configuratorCallable = $this->processorDeclarationList->get($processorName); |
||
177 | if($configuratorCallable && is_callable($configuratorCallable)) { |
||
178 | call_user_func($configuratorCallable, $processor, $this); |
||
179 | } |
||
180 | |||
181 | $this->processorList[$processorName] = $processor; |
||
182 | |||
183 | return $processor; |
||
184 | } |
||
185 | |||
186 | public function registerCollectorNamespace($namespace) |
||
187 | { |
||
188 | $this->collectorNamespaces[] = rtrim($namespace, '\\'); |
||
189 | return $this; |
||
190 | } |
||
191 | |||
192 | View Code Duplication | private function getCollectorClassName($type) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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...
|
|||
193 | { |
||
194 | if(false == strpos($type, '_')) { |
||
0 ignored issues
–
show
|
|||
195 | $className = ucfirst($type); |
||
196 | } else { |
||
197 | $className = implode('', array_map('ucfirst', explode('_', $type))); |
||
198 | } |
||
199 | |||
200 | $className .= 'Collector'; |
||
201 | |||
202 | foreach($this->collectorNamespaces as $namespace) { |
||
203 | $fullyQualifiedClassName = $namespace . '\\' . $className; |
||
204 | if(class_exists($fullyQualifiedClassName)) { |
||
205 | return $fullyQualifiedClassName; |
||
206 | } |
||
207 | } |
||
208 | |||
209 | throw new \Exception('Class ' . $fullyQualifiedClassName . ' not found'); |
||
210 | } |
||
211 | |||
212 | /* |
||
213 | * @param int $requestNumber maximum number of allowed requests |
||
214 | * @param int $timeInterval time interval in seconds |
||
215 | */ |
||
216 | public function createCollector( |
||
217 | $type, |
||
218 | $namespace, |
||
219 | $requestNumber, |
||
220 | $timeInterval, |
||
221 | $configuratorCallable = null |
||
222 | ) { |
||
223 | $className = $this->getCollectorClassName($type); |
||
224 | |||
225 | $collector = new $className( |
||
226 | $this->getKey() . ':' . $namespace, |
||
227 | $requestNumber, |
||
228 | $timeInterval |
||
229 | ); |
||
230 | |||
231 | if (!($collector instanceof CollectorInterface)) { |
||
232 | throw new \Exception('Collector must inherit CollectorInterface'); |
||
233 | } |
||
234 | |||
235 | // configure |
||
236 | if(is_callable($configuratorCallable)) { |
||
237 | call_user_func($configuratorCallable, $collector, $this); |
||
238 | } |
||
239 | |||
240 | return $collector; |
||
241 | |||
242 | } |
||
243 | |||
244 | View Code Duplication | private function getStorageClassName($type) |
|
0 ignored issues
–
show
This method seems to be duplicated in 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...
|
|||
245 | { |
||
246 | if(false == strpos($type, '_')) { |
||
0 ignored issues
–
show
|
|||
247 | $className = ucfirst($type); |
||
248 | } else { |
||
249 | $className = implode('', array_map('ucfirst', explode('_', $type))); |
||
250 | } |
||
251 | |||
252 | $className .= 'Storage'; |
||
253 | |||
254 | foreach($this->storageNamespaces as $namespace) { |
||
255 | $fullyQualifiedClassName = $namespace . '\\' . $className; |
||
256 | if(class_exists($fullyQualifiedClassName)) { |
||
257 | return $fullyQualifiedClassName; |
||
258 | } |
||
259 | } |
||
260 | |||
261 | throw new \Exception('Class ' . $fullyQualifiedClassName . ' not found'); |
||
262 | } |
||
263 | |||
264 | public function createStorage($type, $configuratorCallable = null) |
||
265 | { |
||
266 | $className = $this->getStorageClassName($type); |
||
267 | |||
268 | $storage = new $className(); |
||
269 | |||
270 | // configure |
||
271 | if(is_callable($configuratorCallable)) { |
||
272 | call_user_func($configuratorCallable, $storage); |
||
273 | } |
||
274 | |||
275 | return $storage; |
||
276 | |||
277 | } |
||
278 | |||
279 | public function registerStorageNamespace($namespace) |
||
280 | { |
||
281 | $this->storageNamespaces[] = rtrim($namespace, '\\'); |
||
282 | return $this; |
||
283 | } |
||
284 | |||
285 | private function on($stateName, $callable) |
||
286 | { |
||
287 | if($this->hasState(self::STATE_UNCHECKED)) { |
||
288 | $this->subscribe($stateName, $callable); |
||
289 | } elseif($this->hasState($stateName)) { |
||
290 | call_user_func($callable); |
||
291 | } |
||
292 | |||
293 | return $this; |
||
294 | } |
||
295 | |||
296 | public function onCheckPassed($callable) |
||
297 | { |
||
298 | $this->on(self::STATE_PASSED, $callable); |
||
299 | |||
300 | return $this; |
||
301 | } |
||
302 | |||
303 | public function onCheckFailed($callable) |
||
304 | { |
||
305 | $this->on(self::STATE_FAILED, $callable); |
||
306 | |||
307 | return $this; |
||
308 | } |
||
309 | |||
310 | public function isUnchecked() |
||
311 | { |
||
312 | return $this->hasState(self::STATE_UNCHECKED); |
||
313 | } |
||
314 | |||
315 | public function isPassed() |
||
316 | { |
||
317 | return $this->hasState(self::STATE_PASSED); |
||
318 | } |
||
319 | |||
320 | public function isFailed() |
||
321 | { |
||
322 | return $this->hasState(self::STATE_FAILED); |
||
323 | } |
||
324 | |||
325 | private function hasState($state) |
||
326 | { |
||
327 | return $this->state === $state; |
||
328 | } |
||
329 | |||
330 | public function subscribe($eventName, $callable, $priority = 0) |
||
331 | { |
||
332 | $this->eventDispatcher->addListener($eventName, $callable, $priority); |
||
333 | return $this; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * |
||
338 | * @param string $eventName |
||
339 | * @param mixed $target |
||
340 | * @return \Sokil\FraudDetector\Event |
||
341 | */ |
||
342 | public function trigger($eventName, $target = null) |
||
343 | { |
||
344 | $event = new Event(); |
||
345 | |||
346 | if($target) { |
||
347 | $event->setTarget($target); |
||
348 | } |
||
349 | |||
350 | return $this->eventDispatcher->dispatch($eventName, $event); |
||
351 | } |
||
352 | } |
||
353 |
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: