Completed
Push — master ( db3c82...df4949 )
by Joschi
03:02
created

HmacValidator   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 441
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 41.67%

Importance

Changes 0
Metric Value
wmc 62
lcom 2
cbo 7
dl 0
loc 441
ccs 65
cts 156
cp 0.4167
rs 3.44
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A setMethodVector() 0 11 2
A validateRequestMethod() 0 12 3
A setSubmissionTimes() 0 15 3
A validate() 0 16 3
A armor() 0 22 2
C validateHmac() 0 74 17
A probeTimedHmac() 0 17 2
C _decryptHmac() 0 46 15
A _probeTimedHMAC() 0 12 2
B _hmac() 0 31 7
B calculateHmac() 0 32 6

How to fix   Complexity   

Complex Class

Complex classes like HmacValidator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HmacValidator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * antibot
5
 *
6
 * @category   Jkphl
7
 * @package    Jkphl\Antibot
8
 * @subpackage Jkphl\Antibot\Ports\Validators
9
 * @author     Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright  Copyright © 2018 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license    http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2018 Joschi Kuphal <[email protected]>
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Antibot\Ports\Validators;
38
39
use Jkphl\Antibot\Domain\Antibot;
40
use Jkphl\Antibot\Domain\Exceptions\InvalidRequestMethodOrderException;
41
use Jkphl\Antibot\Infrastructure\Exceptions\HmacValidationException;
42
use Jkphl\Antibot\Infrastructure\Factory\HmacFactory;
43
use Jkphl\Antibot\Infrastructure\Model\AbstractValidator;
44
use Jkphl\Antibot\Infrastructure\Model\InputElement;
45
use Jkphl\Antibot\Ports\Exceptions\InvalidArgumentException;
46
use Psr\Http\Message\ServerRequestInterface;
47
48
/**
49
 * HMAC Validator
50
 *
51
 * @package    Jkphl\Antibot
52
 * @subpackage Jkphl\Antibot\Ports\Validators
53
 */
54
class HmacValidator extends AbstractValidator
55
{
56
    /**
57
     * Request method vector
58
     *
59
     * @var string[]
60
     */
61
    protected $methodVector = null;
62
    /**
63
     * Request submission times
64
     *
65
     * @var float[]
66
     */
67
    protected $submissionTimes = null;
68
    /**
69
     * Validation order position
70
     *
71
     * @var int
72
     */
73
    const POSITION = 100;
74
    /**
75
     * GET request
76
     *
77
     * @var string
78
     */
79
    const METHOD_GET = 'GET';
80
    /**
81
     * POST request
82
     *
83
     * @var string
84
     */
85
    const METHOD_POST = 'POST';
86
    /**
87
     * Minimum submission time
88
     *
89
     * @var float
90
     */
91
    const MINIMUM_SUBMISSION = 10;
92
    /**
93
     * Minimum submission time for follow-up submissions
94
     *
95
     * @var float
96
     */
97
    const MINIMUM_FOLLOWUP_SUBMISSION = 3;
98
    /**
99
     * Maximum submission time
100
     *
101
     * @var float
102
     */
103
    const MAXIMUM_SUBMISSION = 3600;
104
    /**
105
     * Block access
106
     *
107
     * @var string
108
     */
109
    const BLOCK = 'BLOCK';
110
111
    /**
112
     * Set the request method vector
113
     *
114
     * @param string $previous Previous request
115
     * @param string $current  Current request
116
     */
117 2
    public function setMethodVector(string $previous = null, string $current = null): void
118
    {
119
        // If the request method vector should be unset
120 2
        if ($previous === null) {
121
            $this->methodVector = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,string> of property $methodVector.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
122
123
            return;
124
        }
125
126 2
        $this->methodVector = [$this->validateRequestMethod($previous), $this->validateRequestMethod($current)];
127 2
    }
128
129
    /**
130
     * Sanitize and validate a request method
131
     *
132
     * @param string $method Request method
133
     *
134
     * @return string Validated request method
135
     * @throws InvalidArgumentException If the request method is invalid
136
     */
137 2
    protected function validateRequestMethod(string $method): string
138
    {
139 2
        $method = strtoupper($method);
140 2
        if ($method !== static::METHOD_GET && $method !== static::METHOD_POST) {
141
            throw new InvalidArgumentException(
142
                sprintf(InvalidArgumentException::INVALID_REQUEST_METHOD_STR, $method),
143
                InvalidArgumentException::INVALID_REQUEST_METHOD
144
            );
145
        }
146
147 2
        return $method;
148
    }
149
150
    /**
151
     * Sanitize and set the submission times
152
     *
153
     * @param float $max              Maximum submission time
154
     * @param float $min              Minimum submission time
155
     * @param float|null $minFollowUp Minimum submission time for follow-up submissions
156
     */
157 1
    public function setSubmissionTimes(float $max = null, float $min = null, float $minFollowUp = null): void
158
    {
159
        // If the submission times should be unset
160 1
        if ($max === null) {
161
            $this->submissionTimes = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,double> of property $submissionTimes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
162
163
            return;
164
        }
165
166 1
        $max                   = min(floatval($max), static::MAXIMUM_SUBMISSION);
167 1
        $min                   = max(floatval($min), static::MINIMUM_SUBMISSION);
168 1
        $minFollowUp           = ($minFollowUp === null)
169 1
            ? $min : max(floatval($minFollowUp), static::MINIMUM_FOLLOWUP_SUBMISSION);
170 1
        $this->submissionTimes = [$min, $minFollowUp, $max];
0 ignored issues
show
Documentation Bug introduced by
It seems like array($min, $minFollowUp, $max) of type array<integer,double|int...,"2":"double|integer"}> is incompatible with the declared type array<integer,double> of property $submissionTimes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
171 1
    }
172
173
    /**
174
     * Validate a request
175
     *
176
     * @param ServerRequestInterface $request Request
177
     * @param Antibot $antibot                Antibot instance
178
     *
179
     * @return bool
180
     */
181 2
    public function validate(ServerRequestInterface $request, Antibot $antibot): bool
182
    {
183 2
        $data = $antibot->getData();
184
185
        // If Antibot data has been submitted
186 2
        if ($data !== null) {
187
            // If no HMAC was submitted
188 2
            if (empty($data['hmac'])) {
189
                return false;
190
            }
191
192 2
            return $this->validateHmac($data['hmac'], $request, $antibot);
193
        }
194
195
        return true;
196
    }
197
198
    /**
199
     * Create protective form HTML
200
     *
201
     * @param ServerRequestInterface $request Request
202
     * @param Antibot $antibot                Antibot instance
203
     *
204
     * @return InputElement[] HMTL input elements
205
     */
206 3
    public function armor(ServerRequestInterface $request, Antibot $antibot): array
207
    {
208 3
        $now   = null;
209 3
        $hmac  = $this->calculateHmac($request, $antibot, $now);
210
        $armor = [
211 3
            new InputElement([
212 3
                'type'  => 'hidden',
213 3
                'name'  => $antibot->getParameterPrefix().'[hmac]',
214 3
                'value' => $hmac
215
            ])
216
        ];
217
        // Add the timestamp field
218 3
        if ($now !== null) {
219 1
            $armor[] = new InputElement([
220 1
                'type'  => 'hidden',
221 1
                'name'  => $antibot->getParameterPrefix().'[ts]',
222 1
                'value' => intval($now)
223
            ]);
224
        }
225
226 3
        return $armor;
227
    }
228
229
    /**
230
     * Decrypt and validate an HMAC
231
     *
232
     * @param string $hmac                    HMAC
233
     * @param ServerRequestInterface $request Request
234
     * @param Antibot $antibot                Antibot instance
235
     *
236
     * @return bool HMAC is valid
237
     * @throws HmacValidationException If the request method order is invalid
238
     * @throws HmacValidationException If the request timing is invalid
239
     */
240 2
    protected function validateHmac(string $hmac, ServerRequestInterface $request, Antibot $antibot): bool
241
    {
242 2
        $decrypted      = false;
0 ignored issues
show
Unused Code introduced by
$decrypted is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
243 2
        $previousMethod = null;
0 ignored issues
show
Unused Code introduced by
$previousMethod is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
244 2
        $hmacParams     = [$antibot->getUnique()];
245
246
        // Short-circuit blocked HMAC
247 2
        $hmacBlock   = $hmacParams;
248 2
        $hmacBlock[] = self::BLOCK;
249 2
        if (HmacFactory::createFromString(serialize($hmacBlock), $antibot->getUnique()) === $hmac) {
250
            return false;
251
        }
252
253
        // If the request method vector should be used
254 2
        if (!empty($this->methodVector)) {
255 1
            $serverParams  = $request->getServerParams();
256 1
            $requestMethod = empty($serverParams['REQUEST_METHOD']) ? 'EMPTY' : $serverParams['REQUEST_METHOD'];
257 1
            if ($requestMethod !== $this->methodVector[1]) {
258 1
                throw new HmacValidationException(
259 1
                    HmacValidationException::INVALID_REQUEST_METHOD_ORDER_STR,
260 1
                    HmacValidationException::INVALID_REQUEST_METHOD_ORDER
261
                );
262
            }
263
264 1
            $hmacParams[] = $this->methodVector[0];
265
        }
266
267
        // If submission time checks are enabled
268 2
        if (!empty($this->submissionTimes)) {
269
            list($first, $min, $max) = $this->submissionTimes;
270
            $now       = time();
271
            $initial   = $now - $first;
272
            $delay     = null;
0 ignored issues
show
Unused Code introduced by
$delay is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
273
            $data      = $antibot->getData();
274
            $timestamp = empty($data['ts']) ? null : $data['ts'];
275
276
            // If a timestamp has been submitted
277
            if ($timestamp
278
                && (($timestamp + $min) <= $now)
279
                && (($timestamp + $max) >= $now)
280
                && (
281
                    $this->probeTimedHmac($hmac, $antibot, $hmacParams, $timestamp, $timestamp > $initial)
282
                    || (($timestamp <= $initial) ?
283
                        $this->probeTimedHmac($hmac, $antibot, $hmacParams, $timestamp, true) : false
284
                    )
285
                )
286
            ) {
287
//                $delay     = $now - $timestamp;
288
                return true;
289
            } else {
290
                // Run through the valid seconds range
291
                for ($time = $now - $min; $time >= $now - $max; --$time) {
292
                    // Probe the current timestamp
293
                    if ($this->probeTimedHmac($hmac, $antibot, $hmacParams, $time, $time > $initial)
294
                        || (($time <= $initial)
295
                            && $this->probeTimedHMAC($hmac, $antibot, $hmacParams, $time, true)
296
                        )
297
                    ) {
298
//                        $delay     = $now - $time;
299
                        return true;
300
                    }
301
                }
302
            }
303
304
            throw new HmacValidationException(
305
                HmacValidationException::INVALID_REQUEST_TIMING_STR,
306
                HmacValidationException::INVALID_REQUEST_TIMING
307
            );
308
        }
309
310 2
        $currentHMAC = HmacFactory::createFromString(serialize($hmacParams), $antibot->getUnique());
311
312 2
        return $hmac === $currentHMAC;
313
    }
314
315
    /**
316
     * Probe a timed HMAC
317
     *
318
     * @param string $hmac      HMAC
319
     * @param Antibot $antibot  Antibot instance
320
     * @param array $hmacParams HMAC params
321
     * @param int $timestamp    Timestamp
322
     * @param bool $followUp    Is a follow-up request
323
     *
324
     * @return bool HMAC is valid
325
     */
326
    protected function probeTimedHmac(
327
        string $hmac,
328
        Antibot $antibot,
329
        array $hmacParams,
330
        int $timestamp,
331
        bool $followUp = false
332
    ): bool {
333
        if ($followUp) {
334
            $hmacParams[] = true;
335
        }
336
        $hmacParams[] = $timestamp;
337
        $currentHMAC  = HmacFactory::createFromString(serialize($hmacParams), $antibot->getUnique());
338
339
        echo 'Current HMAC: '.$currentHMAC;
340
341
        return $currentHMAC == $hmac;
342
    }
343
344
    public function _decryptHmac($hmac)
345
    {
346
        // If submission time checks are enabled
347
        if ($this->_submissionTimeEnabled()) {
0 ignored issues
show
Bug introduced by
The method _submissionTimeEnabled() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
348
            $minimum  = intval($this->_settings['time']['minimum']);
0 ignored issues
show
Bug introduced by
The property _settings 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...
349
            $maximium = intval($this->_settings['time']['maximum']);
350
            $first    = max($minimum, intval($this->_settings['time']['first']));
351
            $now      = time();
352
            $initial  = $now - $first;
353
            // If a timestamp hint has been submitted: Probe this first
354
            if ($this->_timestamp && (($this->_timestamp + $minimum) <= $now) && (($this->_timestamp + $maximium) >= $now) && $this->_info('Probing timestamp hint first') && (
0 ignored issues
show
Bug introduced by
The method _info() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
355
                    $this->_probeTimedHMAC($hmac, $hmacParams, $this->_timestamp, $this->_timestamp > $initial) ||
0 ignored issues
show
Documentation introduced by
$this->_timestamp is of type integer, but the function expects a object<int>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->_timestamp > $initial is of type boolean, but the function expects a false|object<boolean>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
356
                    (($this->_timestamp <= $initial) ? $this->_probeTimedHMAC($hmac, $hmacParams, $this->_timestamp,
0 ignored issues
show
Documentation introduced by
$this->_timestamp is of type integer, but the function expects a object<int>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
357
                        true) : false))
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a false|object<boolean>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
358
            ) {
359
                $this->_delay = $now - $this->_timestamp;
0 ignored issues
show
Bug introduced by
The property _delay 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...
Bug introduced by
The property _timestamp 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...
360
                $decrypted    = true;
361
                // Else (or if decryption failed for some reason: Probe the valid time range
362
            } else {
363
                // Run through the valid seconds range
364
                for ($time = $now - $minimum; $time >= $now - $maximium; --$time) {
365
                    // Probe the current timestamp
366
                    if ($this->_probeTimedHMAC($hmac, $hmacParams, $time,
0 ignored issues
show
Bug introduced by
The variable $hmacParams does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Documentation introduced by
$time is of type integer, but the function expects a object<int>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
367
                            $time > $initial) || (($time <= $initial) && $this->_probeTimedHMAC($hmac, $hmacParams,
0 ignored issues
show
Documentation introduced by
$time > $initial is of type boolean, but the function expects a false|object<boolean>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
368
                                $time, true))
0 ignored issues
show
Documentation introduced by
$time is of type integer, but the function expects a object<int>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
true is of type boolean, but the function expects a false|object<boolean>.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
369
                    ) {
370
                        $this->_delay = $now - $time;
371
                        $decrypted    = true;
372
                        break;
373
                    }
374
                }
375
            }
376
            // Else: Check for HMAC match
377
        } else {
378
            $currentHMAC = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac(serialize($hmacParams));
379
            $decrypted   = $hmac == $currentHMAC;
380
            $this->_debug('Probing HMAC with parameters', $hmacParams);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
381
            $this->_debug('Current HMAC:', $currentHMAC);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
382
        }
383
        // Register the initial HTTP method in case decryption was successfull
384
        if ($decrypted && $previousMethod) {
0 ignored issues
show
Bug introduced by
The variable $decrypted does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
385
            $this->_method = $previousMethod;
0 ignored issues
show
Bug introduced by
The property _method 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...
Bug introduced by
The variable $previousMethod does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
386
        }
387
388
        return $decrypted;
389
    }
390
391
    /**
392
     * Probe a set of HMAC parameters with timestamp (for both initial or follow-up requests)
393
     *
394
     * @param \string $hmac      HMAC
395
     * @param \array $hmacParams HMAC parameters
396
     * @param \int $timestamp    Timestamp
397
     * @param \boolean $followUp Follow-up request
398
     *
399
     * @return \boolean                HMAC matches
400
     */
401
    protected function _probeTimedHMAC($hmac, array $hmacParams, $timestamp, $followUp = false)
402
    {
403
        if ($followUp) {
404
            $hmacParams[] = true;
405
        }
406
        $hmacParams[] = $timestamp;
407
        $currentHMAC  = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac(serialize($hmacParams));
408
        $this->_debug('Probing HMAC with parameters', $hmacParams);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
409
        $this->_debug('Current HMAC:', $currentHMAC);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
410
411
        return $currentHMAC == $hmac;
412
    }
413
414
    /**
415
     * Create and return the submission HMAC
416
     *
417
     * @param \int $now Current timestamp
418
     *
419
     * @return \string                    Submission HMAC
420
     */
421
    protected function _hmac(&$now = null)
422
    {
423
        $hmacParams = array($this->_token);
0 ignored issues
show
Bug introduced by
The property _token 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...
424
        // If session token checks are enabled
425
        if ($this->_sessionTokenEnabled()) {
0 ignored issues
show
Bug introduced by
The method _sessionTokenEnabled() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
426
            $hmacParams[] = session_id();
427
        }
428
        // If there is an invalid current HMAC
429
        if ($this->_valid === false) {
0 ignored issues
show
Bug introduced by
The property _valid 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...
430
            $hmacParams[] = self::BLOCK;
431
            // Else
432
        } else {
433
            // If submission time checks are enabled
434
            if ($this->_submissionMethodOrderEnabled()) {
0 ignored issues
show
Bug introduced by
The method _submissionMethodOrderEnabled() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
435
                $hmacParams[] = $this->_method ?: strtoupper($_SERVER['REQUEST_METHOD']);
436
            }
437
            // If submission time checks are enabled
438
            if ($this->_submissionTimeEnabled()) {
0 ignored issues
show
Bug introduced by
The method _submissionTimeEnabled() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
439
                if ($this->_data) {
0 ignored issues
show
Bug introduced by
The property _data 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...
440
                    $hmacParams[] = true;
441
                }
442
                $hmacParams[] =
443
                $now = time();
444
            }
445
        }
446
        $hmac = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac(serialize($hmacParams));
447
        $this->_debug('Creating HMAC for parameters', $hmacParams);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
448
        $this->_debug('HMAC:', $hmac);
0 ignored issues
show
Bug introduced by
The method _debug() does not seem to exist on object<Jkphl\Antibot\Por...lidators\HmacValidator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
449
450
        return $hmac;
451
    }
452
453
    /**
454
     * Calculate the HMAC
455
     *
456
     * @param ServerRequestInterface $request Request
457
     * @param Antibot $antibot                Antibot instance
458
     * @param int|null $now                   Current timestamp
459
     *
460
     * @return string HMAC
461
     */
462 3
    protected function calculateHmac(ServerRequestInterface $request, Antibot $antibot, int &$now = null): string
463
    {
464 3
        $hmacParams = [$antibot->getUnique()];
465 3
        $now        = null;
466
467
        // Invalidate the HMAC if there's a current, invalid one
468 3
        if (false) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
469
470
        } else {
471 3
            $serverParams = $request->getServerParams();
472
473
            // If the request method vector should be used
474 3
            if (!empty($this->methodVector)) {
475 2
                $requestMethod = empty($serverParams['REQUEST_METHOD']) ? '' : $serverParams['REQUEST_METHOD'];
476 2
                $hmacParams[]  = $this->validateRequestMethod($requestMethod);
477
            }
478
479
            // If submission time checks are enabled
480 3
            if (!empty($this->submissionTimes)) {
481 1
                if (!empty($antibot->getData())) {
482
                    $hmacParams[] = true;
483
                }
484 1
                $hmacParams[] = $now = time();
485
            }
486
        }
487
488
//        print_r($hmacParams);
489
490 3
        $hmac = HmacFactory::createFromString(serialize($hmacParams), $antibot->getUnique());
491
492 3
        return $hmac;
493
    }
494
}
495