1<?php 2 3namespace dokuwiki\test; 4 5use dokuwiki\Input\Input; 6use dokuwiki\Ip; 7 8class IpTest extends \DokuWikiTest { 9 10 /** 11 * The data provider for ipToNumber() tests. 12 * 13 * @return mixed[][] Returns an array of test cases. 14 */ 15 public function ip_to_number_provider() : array 16 { 17 $tests = [ 18 ['127.0.0.1', 4, 0x00000000, 0x7f000001], 19 ['::127.0.0.1', 6, 0x00000000, 0x7f000001], 20 ['::1', 6, 0x00000000, 0x00000001], 21 ['38AF:3033:AA39:CDE3:1A46:094C:44ED:5300', 6, 0x38AF3033AA39CDE3, 0x1A46094C44ED5300], 22 ['193.53.125.7', 4, 0x00000000, 0xC1357D07], 23 ]; 24 25 return $tests; 26 } 27 28 /** 29 * Test ipToNumber(). 30 * 31 * @dataProvider ip_to_number_provider 32 * 33 * @param string $ip The IP address to convert. 34 * @param int $version The IP version, either 4 or 6. 35 * @param int $upper The upper 64 bits of the IP. 36 * @param int $lower The lower 64 bits of the IP. 37 * 38 * @return void 39 */ 40 public function test_ip_to_number(string $ip, int $version, int $upper, int $lower): void 41 { 42 $result = Ip::ipToNumber($ip); 43 44 $this->assertSame($version, $result['version']); 45 $this->assertSame($upper, $result['upper']); 46 $this->assertSame($lower, $result['lower']); 47 } 48 49 /** 50 * The data provider for test_ip_in_range(). 51 * 52 * @return mixed[][] Returns an array of test cases. 53 */ 54 public function ip_in_range_provider(): array 55 { 56 $tests = [ 57 ['192.168.11.2', '192.168.0.0/16', true], 58 ['192.168.11.2', '192.168.64.1/16', true], 59 ['192.168.11.2', '192.168.64.1/18', false], 60 ['192.168.11.2', '192.168.11.0/20', true], 61 ['127.0.0.1', '127.0.0.0/7', true], 62 ['127.0.0.1', '127.0.0.0/8', true], 63 ['127.0.0.1', '127.200.0.0/8', true], 64 ['127.0.0.1', '127.200.0.0/9', false], 65 ['127.0.0.1', '127.0.0.0/31', true], 66 ['127.0.0.1', '127.0.0.0/32', false], 67 ['127.0.0.1', '127.0.0.1/32', true], 68 ['1111:2222:3333:4444:5555:6666:7777:8888', '1110::/12', true], 69 ['1110:2222:3333:4444:5555:6666:7777:8888', '1110::/12', true], 70 ['1100:2222:3333:4444:5555:6666:7777:8888', '1110::/12', false], 71 ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3300::/40', true], 72 ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3200::/40', false], 73 ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3333:4444:5555:6666:7777:8889/127', true], 74 ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3333:4444:5555:6666:7777:8889/128', false], 75 ['1111:2222:3333:4444:5555:6666:7777:8889', '1111:2222:3333:4444:5555:6666:7777:8889/128', true], 76 ['abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd', 'abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd/128', true], 77 ['abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abce', 'abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd/128', false], 78 ]; 79 80 return $tests; 81 } 82 83 /** 84 * Test ipInRange(). 85 * 86 * @dataProvider ip_in_range_provider 87 * 88 * @param string $ip The IP to test. 89 * @param string $range The IP range to test against. 90 * @param bool $expected The expected result from ipInRange(). 91 * 92 * @return void 93 */ 94 public function test_ip_in_range(string $ip, string $range, bool $expected): void 95 { 96 $result = Ip::ipInRange($ip, $range); 97 98 $this->assertSame($expected, $result); 99 } 100 101 /** 102 * Data provider for test_ip_matches(). 103 * 104 * @return mixed[][] Returns an array of test cases. 105 */ 106 public function ip_matches_provider(): array 107 { 108 // Tests for a CIDR range. 109 $rangeTests = $this->ip_in_range_provider(); 110 111 // Tests for an exact IP match. 112 $exactTests = [ 113 ['127.0.0.1', '127.0.0.1', true], 114 ['127.0.0.1', '127.0.0.0', false], 115 ['aaaa:bbbb:cccc:dddd:eeee::', 'aaaa:bbbb:cccc:dddd:eeee:0000:0000:0000', true], 116 ['aaaa:bbbb:cccc:dddd:eeee:0000:0000:0000', 'aaaa:bbbb:cccc:dddd:eeee::', true], 117 ['aaaa:bbbb:0000:0000:0000:0000:0000:0001', 'aaaa:bbbb::1', true], 118 ['aaaa:bbbb::0001', 'aaaa:bbbb::1', true], 119 ['aaaa:bbbb::0001', 'aaaa:bbbb::', false], 120 ['::ffff:127.0.0.1', '127.0.0.1', false], 121 ['::ffff:127.0.0.1', '::0:ffff:127.0.0.1', true], 122 ]; 123 124 125 return array_merge($rangeTests, $exactTests); 126 } 127 128 /** 129 * Test ipMatches(). 130 * 131 * @dataProvider ip_matches_provider 132 * 133 * @param string $ip The IP to test. 134 * @param string $ipOrRange The IP or IP range to test against. 135 * @param bool $expected The expeced result from ipMatches(). 136 * 137 * @return void 138 */ 139 public function test_ip_matches(string $ip, string $ipOrRange, bool $expected): void 140 { 141 $result = Ip::ipMatches($ip, $ipOrRange); 142 143 $this->assertSame($expected, $result); 144 } 145 146 /** 147 * Data provider for proxyIsTrusted(). 148 * 149 * @return mixed[][] Returns an array of test cases. 150 */ 151 public function proxy_is_trusted_provider(): array 152 { 153 // The new default configuration value. 154 $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; 155 156 // Adding some custom trusted proxies. 157 $custom = array_merge($default, ['1.2.3.4', '1122::', '3.0.0.1/8', '1111:2222::/32']); 158 159 $tests = [ 160 // Empty configuration. 161 ['', '127.0.0.1', false], 162 163 // Configuration with an array of IPs/CIDRs. 164 [$default, '127.0.0.1', true], 165 [$default, '127.1.2.3', true], 166 [$default, '10.1.2.3', true], 167 [$default, '11.1.2.3', false], 168 [$default, '172.16.0.1', true], 169 [$default, '172.160.0.1', false], 170 [$default, '172.31.255.255', true], 171 [$default, '172.32.0.0', false], 172 [$default, '172.200.0.0', false], 173 [$default, '192.168.2.3', true], 174 [$default, '192.169.1.2', false], 175 [$default, '::1', true], 176 [$default, '0000:0000:0000:0000:0000:0000:0000:0001', true], 177 178 // With custom proxies set. 179 [$custom, '127.0.0.1', true], 180 [$custom, '1.2.3.4', true], 181 [$custom, '3.0.1.2', true], 182 [$custom, '1122::', true], 183 [$custom, '1122:0000:0000:0000:0000:0000:0000:0000', true], 184 [$custom, '1111:2223::', false], 185 [$custom, '1111:2222::', true], 186 [$custom, '1111:2222:3333::', true], 187 [$custom, '1111:2222:3333::1', true], 188 ]; 189 190 return $tests; 191 } 192 193 /** 194 * Test proxyIsTrusted(). 195 * 196 * @dataProvider proxy_is_trusted_provider 197 * 198 * @param string|string[] $config The value for $conf[trustedproxies]. 199 * @param string $ip The proxy IP to test. 200 * @param bool $expected The expected result from proxyIsTrusted(). 201 */ 202 public function test_proxy_is_trusted($config, string $ip, bool $expected): void 203 { 204 global $conf; 205 $conf['trustedproxies'] = $config; 206 207 $result = Ip::proxyIsTrusted($ip); 208 209 $this->assertSame($expected, $result); 210 } 211 212 /** 213 * Data provider for test_forwarded_for(). 214 * 215 * @return mixed[][] Returns an array of test cases. 216 */ 217 public function forwarded_for_provider(): array 218 { 219 // The new default configuration value. 220 $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; 221 222 // Adding some custom trusted proxies. 223 $custom = array_merge($default, ['1.2.3.4', '1122::', '3.0.0.1/8', '1111:2222::/32']); 224 225 $tests = [ 226 // Empty config value should always return empty array. 227 [[], '', '127.0.0.1', []], 228 [[], '127.0.0.1', '127.0.0.1', []], 229 230 // The new default configuration. 231 [$default, '', '127.0.0.1', []], 232 [$default, '1.2.3.4', '127.0.0.1', ['1.2.3.4', '127.0.0.1']], 233 [$default, '1.2.3.4', '192.168.1.1', ['1.2.3.4', '192.168.1.1']], 234 [$default, '1.2.3.4,172.16.0.1', '192.168.1.1', ['1.2.3.4', '172.16.0.1', '192.168.1.1']], 235 [$default, '1.2.3.4,172.16.0.1', '::1', ['1.2.3.4', '172.16.0.1', '::1']], 236 [$default, '1.2.3.4,172.16.0.1', '::0001', ['1.2.3.4', '172.16.0.1', '::0001']], 237 238 // Directly from an untrusted proxy. 239 [$default, '', '127.0.0.1', []], 240 [$default, '1.2.3.4', '11.22.33.44', []], 241 [$default, '::1', '11.22.33.44', []], 242 [$default, '::1', '::2', []], 243 244 // From a trusted proxy, but via an untrusted proxy. 245 [$default, '1.2.3.4,11.22.33.44,172.16.0.1', '192.168.1.1', []], 246 [$default, '1.2.3.4,::2,172.16.0.1', '::1', []], 247 248 // A custom configuration. 249 [$custom, '', '127.0.0.1', []], 250 [$custom, '1.2.3.4', '127.0.0.1', ['1.2.3.4', '127.0.0.1']], 251 [$custom, '1.2.3.4', '192.168.1.1', ['1.2.3.4', '192.168.1.1']], 252 [$custom, '1.2.3.4,172.16.0.1', '192.168.1.1', ['1.2.3.4', '172.16.0.1', '192.168.1.1']], 253 [$custom, '1.2.3.4,172.16.0.1', '::1', ['1.2.3.4', '172.16.0.1', '::1']], 254 [$custom, '1.2.3.4,172.16.0.1', '::0001', ['1.2.3.4', '172.16.0.1', '::0001']], 255 256 // Directly from an untrusted proxy. 257 [$custom, '', '127.0.0.1', []], 258 [$custom, '1.2.3.4', '11.22.33.44', []], 259 [$custom, '::1', '11.22.33.44', []], 260 [$custom, '::1', '::2', []], 261 262 // From a trusted proxy, but via an untrusted proxy. 263 [$custom, '1.2.3.4,11.22.33.44,172.16.0.1', '192.168.1.1', []], 264 [$custom, '1.2.3.4,::2,172.16.0.1', '::1', []], 265 266 // Via a custom proxy. 267 [$custom, '11.2.3.4,3.1.2.3,172.16.0.1', '192.168.1.1', ['11.2.3.4', '3.1.2.3', '172.16.0.1', '192.168.1.1']], 268 [$custom, '11.2.3.4,1122::,172.16.0.1', '3.0.0.1', ['11.2.3.4', '1122::', '172.16.0.1', '3.0.0.1']], 269 [$custom, '11.2.3.4,1122::,172.16.0.1', '1111:2222:3333::', ['11.2.3.4', '1122::', '172.16.0.1', '1111:2222:3333::']], 270 ]; 271 272 return $tests; 273 } 274 275 /** 276 * Test forwardedFor(). 277 * 278 * @dataProvider forwarded_for_provider 279 * 280 * @param string|string[] $config The trustedproxies config value. 281 * @param string $header The X-Forwarded-For header value. 282 * @param string $remoteAddr The TCP/IP peer address. 283 * @param array $expected The expected result from forwardedFor(). 284 * 285 * @return void 286 */ 287 public function test_forwarded_for($config, string $header, string $remoteAddr, array $expected): void 288 { 289 /* @var Input $INPUT */ 290 global $INPUT, $conf; 291 292 $conf['trustedproxies'] = $config; 293 $INPUT->server->set('HTTP_X_FORWARDED_FOR', $header); 294 $INPUT->server->set('REMOTE_ADDR', $remoteAddr); 295 296 $result = Ip::forwardedFor(); 297 298 $this->assertSame($expected, $result); 299 } 300 301 /** 302 * Data provider for test_is_ssl(). 303 * 304 * @return mixed[][] Returns an array of test cases. 305 */ 306 public function is_ssl_provider(): array 307 { 308 // The new default configuration value. 309 $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; 310 311 $tests = [ 312 // Running behind an SSL proxy, HTTP between server and proxy 313 // Proxy (REMOTE_ADDR) is matched by trustedproxies config 314 // HTTPS not set, HTTP_X_FORWARDED_PROTO set to https 315 [$default, '127.0.0.1', '', 'https', true], 316 317 // Running behind an SSL proxy, HTTP between server and proxy 318 // Proxy (REMOTE_ADDR) is not matched by trustedproxies config 319 // HTTPS not set, HTTP_X_FORWARDED_PROTO set to https 320 [[], '8.8.8.8', '', 'https', false], 321 322 // Running behind a plain HTTP proxy, HTTP between server and proxy 323 // HTTPS not set, HTTP_X_FORWARDED_PROTO set to http 324 [$default, '127.0.0.1', '', 'http', false], 325 326 // Running behind an SSL proxy, HTTP between server and proxy 327 // HTTPS set to off, HTTP_X_FORWARDED_PROTO set to https 328 [$default, '127.0.0.1', 'off', 'https', true], 329 330 // Not running behind a proxy, HTTPS server 331 // HTTPS set to on, HTTP_X_FORWARDED_PROTO not set 332 [[], '8.8.8.8', 'on', '', true], 333 334 // Not running behind a proxy, plain HTTP server 335 // HTTPS not set, HTTP_X_FORWARDED_PROTO not set 336 [[], '8.8.8.8', '', '', false], 337 338 // Not running behind a proxy, plain HTTP server 339 // HTTPS set to off, HTTP_X_FORWARDED_PROTO not set 340 [[], '8.8.8.8', 'off', '', false], 341 342 // Running behind an SSL proxy, SSL between proxy and HTTP server 343 // HTTPS set to on, HTTP_X_FORWARDED_PROTO set to https 344 [$default, '127.0.0.1', 'on', 'https', true], 345 ]; 346 347 return $tests; 348 } 349 350 /** 351 * Test isSsl(). 352 * 353 * @dataProvider is_ssl_provider 354 * 355 * @param string|string[] $config The trustedproxies config value. 356 * @param string $remoteAddr The REMOTE_ADDR value. 357 * @param string $https The HTTPS value. 358 * @param string $forwardedProto The HTTP_X_FORWARDED_PROTO value. 359 * @param bool $expected The expected result from isSsl(). 360 * 361 * @return void 362 */ 363 public function test_is_ssl($config, string $remoteAddr, string $https, string $forwardedProto, bool $expected): void 364 { 365 /* @var Input $INPUT */ 366 global $INPUT, $conf; 367 368 $conf['trustedproxies'] = $config; 369 $INPUT->server->set('REMOTE_ADDR', $remoteAddr); 370 $INPUT->server->set('HTTPS', $https); 371 $INPUT->server->set('HTTP_X_FORWARDED_PROTO', $forwardedProto); 372 373 $result = Ip::isSsl(); 374 375 $this->assertSame($expected, $result); 376 } 377 378 /** 379 * Data provider for test_host_name(). 380 * 381 * @return mixed[][] Returns an array of test cases. 382 */ 383 public function host_name_provider(): array 384 { 385 // The new default configuration value. 386 $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']; 387 388 $tests = [ 389 // X-Forwarded-Host with trusted proxy 390 [$default, '127.0.0.1', 'proxy.example.com', 'www.example.com', 'server.local', 'proxy.example.com'], 391 392 // X-Forwarded-Host with untrusted proxy (should fall back to HTTP_HOST) 393 [[], '8.8.8.8', 'proxy.example.com', 'www.example.com', 'server.local', 'www.example.com'], 394 395 // No X-Forwarded-Host, use HTTP_HOST 396 [$default, '127.0.0.1', '', 'www.example.com', 'server.local', 'www.example.com'], 397 398 // No X-Forwarded-Host or HTTP_HOST, use SERVER_NAME 399 [$default, '127.0.0.1', '', '', 'server.local', 'server.local'], 400 401 // No headers set, should fall back to system hostname 402 [$default, '127.0.0.1', '', '', '', php_uname('n')], 403 ]; 404 405 return $tests; 406 } 407 408 /** 409 * Test hostName(). 410 * 411 * @dataProvider host_name_provider 412 * 413 * @param string|string[] $config The trustedproxies config value. 414 * @param string $remoteAddr The REMOTE_ADDR value. 415 * @param string $forwardedHost The HTTP_X_FORWARDED_HOST value. 416 * @param string $httpHost The HTTP_HOST value. 417 * @param string $serverName The SERVER_NAME value. 418 * @param string $expected The expected result from hostName(). 419 * 420 * @return void 421 */ 422 public function test_host_name($config, string $remoteAddr, string $forwardedHost, string $httpHost, string $serverName, string $expected): void 423 { 424 /* @var Input $INPUT */ 425 global $INPUT, $conf; 426 427 $conf['trustedproxies'] = $config; 428 $INPUT->server->set('REMOTE_ADDR', $remoteAddr); 429 $INPUT->server->set('HTTP_X_FORWARDED_HOST', $forwardedHost); 430 $INPUT->server->set('HTTP_HOST', $httpHost); 431 $INPUT->server->set('SERVER_NAME', $serverName); 432 433 $result = Ip::hostName(); 434 435 $this->assertSame($expected, $result); 436 } 437} 438