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 AndrewAndante\SubMuncher; |
||
4 | |||
5 | class SubMuncher |
||
6 | { |
||
7 | /** |
||
8 | * This class should not be instantiated. |
||
9 | */ |
||
10 | private function __construct() |
||
11 | { |
||
12 | } |
||
13 | |||
14 | /** |
||
15 | * @param array $ipsArray |
||
16 | * @param int $max max number of rules returned |
||
17 | * @return array |
||
18 | */ |
||
19 | public static function consolidate($ipsArray, $max = null) |
||
20 | { |
||
21 | $consolidatedSubnets = []; |
||
22 | $subnetStart = null; |
||
23 | |||
24 | $ips = array_unique($ipsArray); |
||
25 | $sortedIPs = Util::sort_addresses($ips); |
||
26 | |||
27 | foreach ($sortedIPs as $index => $ipv4) { |
||
28 | // If not last and the next IP is the next sequential one, we are at the beginning of a subnet |
||
29 | if (isset($sortedIPs[$index + 1]) && $sortedIPs[$index + 1] == Util::ip_after($ipv4)) { |
||
30 | // if we've already started, just keep going, else kick one off |
||
31 | $subnetStart = $subnetStart ?: $ipv4; |
||
32 | // if not the first IP and the previous IP is sequential, we're at the end of a subnet |
||
33 | } elseif (isset($sortedIPs[$index - 1]) && $subnetStart !== null) { |
||
34 | $result = self::ip_range_to_subnet_array($subnetStart, $ipv4); |
||
35 | $consolidatedSubnets = array_merge($consolidatedSubnets, $result); |
||
36 | $subnetStart = null; |
||
37 | // otherwise we are a lone /32, so add it straight in |
||
38 | } else { |
||
39 | $consolidatedSubnets[]= $ipv4.'/32'; |
||
40 | $subnetStart = null; |
||
41 | } |
||
42 | } |
||
43 | |||
44 | if ($max === null || count($consolidatedSubnets) <= $max) { |
||
45 | return $consolidatedSubnets; |
||
46 | } |
||
47 | |||
48 | return self::consolidate_subnets($consolidatedSubnets, $max); |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * @param string $startip an IPv4 address |
||
53 | * @param string $endip an IPv4 address |
||
54 | * |
||
55 | * @return string[] list of subnets that cover the ip range specified |
||
56 | */ |
||
57 | public static function ip_range_to_subnet_array($startip, $endip) |
||
58 | { |
||
59 | |||
60 | if (!Util::is_ipaddr($startip) || !Util::is_ipaddr($endip)) { |
||
61 | return []; |
||
62 | } |
||
63 | |||
64 | // Container for subnets within this range. |
||
65 | $rangesubnets = []; |
||
66 | |||
67 | // Figure out what the smallest subnet is that holds the number of IPs in the |
||
68 | // given range. |
||
69 | $cidr = Util::find_smallest_cidr(Util::ip_range_size($startip, $endip)); |
||
70 | |||
71 | // Loop here to reduce subnet size and retest as needed. We need to make sure |
||
72 | // that the target subnet is wholly contained between $startip and $endip. |
||
73 | for ($cidr; $cidr <= 32; $cidr++) { |
||
74 | // Find the network and broadcast addresses for the subnet being tested. |
||
75 | $targetsub_min = Util::gen_subnet($startip, $cidr); |
||
76 | $targetsub_max = Util::gen_subnet_max($startip, $cidr); |
||
77 | |||
78 | // Check best case where the range is exactly one subnet. |
||
79 | if (($targetsub_min == $startip) && ($targetsub_max == $endip)) { |
||
80 | // Hooray, the range is exactly this subnet! |
||
81 | return ["{$startip}/{$cidr}"]; |
||
82 | } |
||
83 | |||
84 | // These remaining scenarios will find a subnet that uses the largest |
||
85 | // chunk possible of the range being tested, and leave the rest to be |
||
86 | // tested recursively after the loop. |
||
87 | |||
88 | // Check if the subnet begins with $startip and ends before $endip |
||
89 | if (($targetsub_min == $startip) && Util::ip_less_than($targetsub_max, $endip)) { |
||
90 | break; |
||
91 | } |
||
92 | |||
93 | // Check if the subnet ends at $endip and starts after $startip |
||
94 | if (Util::ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) { |
||
95 | break; |
||
96 | } |
||
97 | |||
98 | // Check if the subnet is between $startip and $endip |
||
99 | if (Util::ip_greater_than($targetsub_min, $startip) && Util::ip_less_than($targetsub_max, $endip)) { |
||
100 | break; |
||
101 | } |
||
102 | } |
||
103 | |||
104 | // Some logic that will recursively search from $startip to the first IP before |
||
105 | // the start of the subnet we just found. |
||
106 | // NOTE: This may never be hit, the way the above algo turned out, but is left |
||
107 | // for completeness. |
||
108 | if ($startip != $targetsub_min) { |
||
109 | $rangesubnets = array_merge( |
||
110 | $rangesubnets, |
||
111 | self::ip_range_to_subnet_array($startip, Util::ip_before($targetsub_min)) |
||
0 ignored issues
–
show
|
|||
112 | ); |
||
113 | } |
||
114 | |||
115 | // Add in the subnet we found before, to preserve ordering |
||
116 | $rangesubnets[] = "{$targetsub_min}/{$cidr}"; |
||
117 | |||
118 | // And some more logic that will search after the subnet we found to fill in |
||
119 | // to the end of the range. |
||
120 | if ($endip != $targetsub_max) { |
||
121 | $rangesubnets = array_merge( |
||
122 | $rangesubnets, |
||
123 | self::ip_range_to_subnet_array(Util::ip_after($targetsub_max), $endip) |
||
0 ignored issues
–
show
The variable
$targetsub_max 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
![]() |
|||
124 | ); |
||
125 | } |
||
126 | |||
127 | return $rangesubnets; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Function to figure out the least problematic subnets to combine based on |
||
132 | * fewest additional IPs introduced. Then combines them as such, and runs |
||
133 | * it back through the consolidator with one less subnet - until we have |
||
134 | * reduced it down to the maximum number of rules |
||
135 | * |
||
136 | * @param array $subnetsArray array of cidrs |
||
137 | * @param int $max |
||
138 | * |
||
139 | * @return array |
||
140 | */ |
||
141 | public static function consolidate_subnets($subnetsArray, $max = null) |
||
142 | { |
||
143 | |||
144 | $subnetsArray = Util::sort_cidrs(array_unique($subnetsArray)); |
||
145 | |||
146 | do { |
||
147 | $countSubnetsArray = count($subnetsArray); |
||
148 | $newSubnetsArray = []; |
||
149 | $subnetToMaskMap = []; |
||
150 | $ipReductionBySubnet = []; |
||
151 | reset($subnetsArray); |
||
152 | do { |
||
153 | $cidr = current($subnetsArray); |
||
154 | list($currentIP, $currentMask) = explode('/', $cidr); |
||
155 | $nextIP = null; |
||
156 | $nextMask = null; |
||
157 | |||
158 | if (next($subnetsArray) !== false) { |
||
159 | list($nextIP, $nextMask) = explode('/', current($subnetsArray)); |
||
160 | prev($subnetsArray); |
||
161 | } else { |
||
162 | end($subnetsArray); |
||
163 | } |
||
164 | |||
165 | $endIP = Util::gen_subnet_max($currentIP, $currentMask); |
||
166 | while (isset($nextIP) && Util::ip_after($endIP) == $nextIP) { |
||
167 | $nextEndIP = Util::gen_subnet_max($nextIP, $nextMask); |
||
168 | $consolidated = self::ip_range_to_subnet_array($currentIP, $nextEndIP); |
||
169 | if (count($consolidated) == 1) { |
||
170 | $endIP = $nextEndIP; |
||
171 | list($currentIP, $currentMask) = explode('/', $consolidated[0]); |
||
172 | if (next($subnetsArray) !== false) { |
||
173 | list($nextIP, $nextMask) = explode('/', current($subnetsArray)); |
||
174 | } else { |
||
175 | end($subnetsArray); |
||
176 | $nextIP = null; |
||
177 | $nextMask = null; |
||
178 | } |
||
179 | } else { |
||
180 | break; |
||
181 | } |
||
182 | } |
||
183 | |||
184 | $newSubnetsArray[] = $currentIP . '/' . $currentMask; |
||
185 | |||
186 | $subnetToMaskMap[$currentIP] = [ |
||
187 | 'startIP' => $currentIP, |
||
188 | 'endIP' => $endIP, |
||
189 | 'mask' => $currentMask, |
||
190 | 'next' => isset($nextIP) ? $nextIP : 'none', |
||
191 | ]; |
||
192 | |||
193 | $toJoin = Util::get_single_subnet($currentIP, Util::gen_subnet_max($nextIP, $nextMask)); |
||
194 | if (!$toJoin) { |
||
0 ignored issues
–
show
The expression
$toJoin of type null|string is loosely compared to false ; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
195 | continue; |
||
196 | } |
||
197 | list($joinIP, $joinMask) = explode('/', $toJoin); |
||
198 | $diff = abs(Util::subnet_range_size($currentMask) - Util::subnet_range_size($joinMask)); |
||
199 | |||
200 | $ipReductionBySubnet[$joinIP] = [ |
||
201 | 'mask' => $joinMask, |
||
202 | 'diff' => $diff, |
||
203 | 'original' => $currentIP, |
||
204 | ]; |
||
205 | } while (next($subnetsArray) !== false); |
||
206 | $subnetsArray = $newSubnetsArray; |
||
207 | } while (count($subnetsArray) !== $countSubnetsArray); |
||
208 | |||
209 | // sort array by number of additional IPs introduced |
||
210 | uasort($ipReductionBySubnet, function ($a, $b) { |
||
211 | return $a['diff'] - $b['diff']; |
||
212 | }); |
||
213 | |||
214 | $returnCIDRs = []; |
||
215 | foreach ($subnetToMaskMap as $ip => $config) { |
||
216 | $returnCIDRs[] = $ip.'/'.$config['mask']; |
||
217 | } |
||
218 | |||
219 | if ($max === null || count($returnCIDRs) <= $max) { |
||
220 | return $returnCIDRs; |
||
221 | } |
||
222 | |||
223 | reset($ipReductionBySubnet); |
||
224 | do { |
||
225 | current($ipReductionBySubnet); |
||
226 | $injectedIP = key($ipReductionBySubnet); |
||
227 | |||
228 | $toUpdate = $ipReductionBySubnet[$injectedIP]['original']; |
||
229 | if (isset($subnetToMaskMap[$toUpdate])) { |
||
230 | $next = $subnetToMaskMap[$toUpdate]['next']; |
||
231 | |||
232 | // remove the two subnets we've just mushed |
||
233 | unset($subnetToMaskMap[$toUpdate]); |
||
234 | unset($subnetToMaskMap[$next]); |
||
235 | |||
236 | // chuck in the new one |
||
237 | $subnetToMaskMap[$injectedIP] = [ |
||
238 | 'mask' => $ipReductionBySubnet[$injectedIP]['mask'], |
||
239 | ]; |
||
240 | |||
241 | $returnCIDRs = []; |
||
242 | foreach ($subnetToMaskMap as $ip => $config) { |
||
243 | $returnCIDRs[] = $ip . '/' . $config['mask']; |
||
244 | } |
||
245 | |||
246 | $returnCIDRs = Util::sort_cidrs($returnCIDRs); |
||
247 | } |
||
248 | } while (count($returnCIDRs) > $max && next($ipReductionBySubnet) !== false); |
||
249 | |||
250 | if (count($returnCIDRs > $max)) { |
||
251 | return self::consolidate_subnets($returnCIDRs, $max); |
||
252 | } |
||
253 | |||
254 | return $returnCIDRs; |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * @param string[] $ipsArray |
||
259 | * @param int|null $max |
||
260 | * @return array |
||
261 | */ |
||
262 | public static function consolidate_verbose($ipsArray, $max = null) |
||
263 | { |
||
264 | $consolidateResults = self::consolidate($ipsArray, $max); |
||
265 | $totalIPs = []; |
||
266 | foreach ($consolidateResults as $cidr) { |
||
267 | $totalIPs = array_merge($totalIPs, Util::cidr_to_ips_array($cidr)); |
||
268 | } |
||
269 | |||
270 | return [ |
||
271 | 'consolidated_subnets' => $consolidateResults, |
||
272 | 'initial_IPs' => Util::sort_addresses($ipsArray), |
||
273 | 'total_IPs' => $totalIPs |
||
274 | ]; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * @param string[] $subnetsArray |
||
279 | * @param int|null $max |
||
280 | * @return array |
||
281 | */ |
||
282 | public static function consolidate_subnets_verbose($subnetsArray, $max = null) |
||
283 | { |
||
284 | $ips = []; |
||
285 | foreach ($subnetsArray as $subnet) { |
||
286 | $ips = array_merge($ips, Util::cidr_to_ips_array($subnet)); |
||
287 | } |
||
288 | |||
289 | return self::consolidate_verbose($ips, $max); |
||
290 | } |
||
291 | } |
||
292 |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: