Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#1101)
by
unknown
03:06
created

Ip::verifyNetwork()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 14
c 0
b 0
f 0
ccs 8
cts 8
cp 1
rs 10
cc 4
nc 4
nop 1
crap 4
1
<?php
2
3
/*
4
 * This file is part of Respect/Validation.
5
 *
6
 * (c) Alexandre Gomes Gaigalas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the "LICENSE.md"
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Respect\Validation\Rules;
15
16
use Respect\Validation\Exceptions\ComponentException;
17
18
use function bccomp;
19
use function explode;
20
use function filter_var;
21
use function ip2long;
22
use function is_int;
23
use function long2ip;
24
use function mb_strpos;
25
use function mb_substr_count;
26
use function sprintf;
27
use function str_replace;
28
use function strtr;
29
30
/**
31
 * Validates IP Addresses. This validator uses the native filter_var() PHP function.
32
 *
33
 * @author Alexandre Gomes Gaigalas <[email protected]>
34
 * @author Danilo Benevides <[email protected]>
35
 * @author Henrique Moody <[email protected]>
36
 * @author Luís Otávio Cobucci Oblonczyk <[email protected]>
37
 */
38
final class Ip extends AbstractRule
39
{
40
    /**
41
     * @var int|string
42
     */
43
    private $ipOptions;
44
45
    /**
46
     * @var array
47
     */
48
    private $range;
49
50
    /**
51
     * @var string
52
     */
53
    private $networkRange;
54
55
    /**
56
     * @param int|string $ipOptions
57
     */
58 1
    public function __construct($ipOptions = null)
59
    {
60 1
        if (is_int($ipOptions)) {
61
            $this->ipOptions = $ipOptions;
62
63
            return;
64
        }
65
66 1
        $this->networkRange = $this->parseRange($ipOptions);
67 1
        $this->range = $this->createRange();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createRange() can also be of type string. However, the property $range is declared as type array. 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...
68 1
    }
69
70 1
    private function createRange(): ?string
71
    {
72 1
        if (!$this->networkRange) {
73
            return null;
74
        }
75
76 1
        $range = $this->networkRange;
77 1
        $message = $range['min'];
78
79 1
        if (isset($range['max'])) {
80 1
            $message .= '-'.$range['max'];
81
        } else {
82
            $message .= '/'.long2ip((int) $range['mask']);
83
        }
84
85 1
        return $message;
86
    }
87
88 1
    private function parseRange($input)
89
    {
90 1
        if (null === $input || '*' == $input || '*.*.*.*' == $input
91 1
            || '0.0.0.0-255.255.255.255' == $input) {
92
            return;
93
        }
94
95 1
        $range = ['min' => null, 'max' => null, 'mask' => null];
96
97 1
        if (false !== mb_strpos($input, '-')) {
98
            list($range['min'], $range['max']) = explode('-', $input);
99 1
        } elseif (false !== mb_strpos($input, '*')) {
100 1
            $this->parseRangeUsingWildcards($input, $range);
101 1
        } elseif (false !== mb_strpos($input, '/')) {
102 1
            $this->parseRangeUsingCidr($input, $range);
103
        } else {
104 1
            throw new ComponentException('Invalid network range');
105
        }
106
107 1
        if (!$this->verifyAddress($range['min'])) {
108
            throw new ComponentException('Invalid network range');
109
        }
110
111 1
        if (isset($range['max']) && !$this->verifyAddress($range['max'])) {
112
            throw new ComponentException('Invalid network range');
113
        }
114
115 1
        return $range;
116
    }
117
118 1
    private function fillAddress(&$input, $char = '*'): void
119
    {
120 1
        while (mb_substr_count($input, '.') < 3) {
121
            $input .= '.'.$char;
122
        }
123 1
    }
124
125 1
    private function parseRangeUsingWildcards($input, &$range): void
126
    {
127 1
        $this->fillAddress($input);
128
129 1
        $range['min'] = strtr($input, '*', '0');
130 1
        $range['max'] = str_replace('*', '255', $input);
131 1
    }
132
133 1
    private function parseRangeUsingCidr($input, &$range): void
134
    {
135 1
        $input = explode('/', $input);
136 1
        $this->fillAddress($input[0], '0');
137
138 1
        $range['min'] = $input[0];
139 1
        $isAddressMask = false !== mb_strpos($input[1], '.');
140
141 1
        if ($isAddressMask && $this->verifyAddress($input[1])) {
142
            $range['mask'] = sprintf('%032b', ip2long($input[1]));
143
144
            return;
145
        }
146
147 1
        if ($isAddressMask || $input[1] < 8 || $input[1] > 30) {
148 1
            throw new ComponentException('Invalid network mask');
149
        }
150
151
        $range['mask'] = sprintf('%032b', ip2long(long2ip(~(2 ** (32 - $input[1]) - 1))));
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157 34
    public function validate($input): bool
158
    {
159 34
        return $this->verifyAddress($input) && $this->verifyNetwork($input);
160
    }
161
162 34
    private function verifyAddress($address)
163
    {
164 34
        return (bool) filter_var(
165 34
            $address,
166 34
            FILTER_VALIDATE_IP,
167
            [
168 34
                'flags' => $this->ipOptions,
169
            ]
170
        );
171
    }
172
173 28
    private function verifyNetwork($input)
174
    {
175 28
        if (null === $this->networkRange) {
176 3
            return true;
177
        }
178
179 25
        if (isset($this->networkRange['mask'])) {
180 6
            return $this->belongsToSubnet($input);
181
        }
182
183 19
        $input = sprintf('%u', ip2long($input));
184
185 19
        return bccomp($input, sprintf('%u', ip2long($this->networkRange['min']))) >= 0
186 19
               && bccomp($input, sprintf('%u', ip2long($this->networkRange['max']))) <= 0;
187
    }
188
189 6
    private function belongsToSubnet($input)
190
    {
191 6
        $range = $this->networkRange;
192 6
        $min = sprintf('%032b', ip2long($range['min']));
193 6
        $input = sprintf('%032b', ip2long($input));
194
195 6
        return ($input & $range['mask']) === ($min & $range['mask']);
196
    }
197
}
198