testSetSourceArrayOverridesServerSuperglobal()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 1
eloc 6
c 3
b 0
f 1
nc 1
nop 0
dl 0
loc 9
rs 10
1
<?php
2
/*
3
The MIT License (MIT)
4
5
Copyright (c) 2015 Vectorface, Inc.
6
7
Permission is hereby granted, free of charge, to any person obtaining a copy
8
of this software and associated documentation files (the "Software"), to deal
9
in the Software without restriction, including without limitation the rights
10
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
copies of the Software, and to permit persons to whom the Software is
12
furnished to do so, subject to the following conditions:
13
14
The above copyright notice and this permission notice shall be included in
15
all copies or substantial portions of the Software.
16
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
THE SOFTWARE.
24
*/
25
namespace Vectorface\WhipTests;
26
27
use InvalidArgumentException;
28
use PHPUnit\Framework\TestCase;
29
use Psr\Http\Message\ServerRequestInterface;
30
use stdClass;
31
use Vectorface\Whip\Whip;
32
33
/**
34
 * Test class for testing Whip.
35
 * @backupGlobals enabled
36
 * @copyright Vectorface, Inc 2015
37
 * @author Daniel Bruce <[email protected]>
38
 */
39
class WhipTest extends TestCase
40
{
41
    /**
42
     * Tests that an invalid source format is rejected.
43
     */
44
    public function testInvalidSource()
45
    {
46
        $this->expectException(InvalidArgumentException::class);
47
        new Whip(Whip::REMOTE_ADDR, [], new stdClass());
48
    }
49
50
    /**
51
     * Tests that we get back the right IP when they are using superglobals.
52
     */
53
    public function testSuperglobal()
54
    {
55
        $_SERVER['REMOTE_ADDR'] = '24.24.24.24';
56
        $lookup = new Whip(Whip::REMOTE_ADDR);
57
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
58
    }
59
60
    /**
61
     * Tests that we get back 127.0.0.1 when there is no superglobal information
62
     * at all.
63
     */
64
    public function testEmptySuperglobal()
65
    {
66
        $_SERVER = [];
67
        $lookup = new Whip();
68
        $this->assertFalse($lookup->getIpAddress());
69
    }
70
71
    /**
72
     * Helper to get a mocked PSR-7 instance.
73
     *
74
     * @param string $remoteAddr The remote address to mock.
75
     * @param string[][] $headers The headers, in the format expected by Psr-7.
76
     */
77
    private function getHttpMessageMock(string $remoteAddr, array $headers = [])
78
    {
79
        $stub = $this->getMockBuilder(ServerRequestInterface::class)
80
            ->getMock();
81
82
        $stub->method('getServerParams')
83
            ->willReturn(['REMOTE_ADDR' => $remoteAddr]);
84
        $stub->method('getHeaders')
85
            ->willReturn($headers);
86
87
        return $stub;
88
    }
89
    /**
90
     * Tests that we can use a PSR-7 ServerRequestInterface compatible class.
91
     */
92
    public function testPsr7Request()
93
    {
94
        $lookup = new Whip(
95
            Whip::PROXY_HEADERS,
96
            [
97
                Whip::PROXY_HEADERS => [
98
                    Whip::IPV4 => [
99
                        '127.0.0.1'
100
                    ]
101
                ]
102
            ],
103
            $this->getHttpMessageMock("127.0.0.1", ['X-Forwarded-For' => ['32.32.32.32,192.168.1.1']])
104
        );
105
106
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
107
    }
108
109
    /**
110
     * Tests that we get false when no valid IP address could be found.
111
     */
112
    public function testNoAddresFoundDueToBitmask()
113
    {
114
        $lookup = new Whip(Whip::PROXY_HEADERS);
115
        $lookup->setSource(['REMOTE_ADDR' => '127.0.0.1']);
116
        $this->assertFalse($lookup->getIpAddress());
117
    }
118
119
    /**
120
     * Tests the standard REMOTE_ADDR method.
121
     */
122
    public function testRemoteAddrMethod()
123
    {
124
        $lookup = new Whip(Whip::REMOTE_ADDR);
125
        $lookup->setSource(['REMOTE_ADDR' => '24.24.24.24']);
126
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
127
    }
128
129
    /**
130
     * Tests that an invalid IPv4 address returns false.
131
     */
132
    public function testInvalidIPv4Address()
133
    {
134
        $lookup = new Whip(Whip::REMOTE_ADDR);
135
        $lookup->setSource(['REMOTE_ADDR' => '127.0.0.256']);
136
        $this->assertFalse($lookup->getValidIpAddress());
137
    }
138
139
    /**
140
     * Tests a valid IPv6 address.
141
     */
142
    public function testValidIPv6Address()
143
    {
144
        $lookup = new Whip(Whip::REMOTE_ADDR);
145
        $lookup->setSource(['REMOTE_ADDR' => '::1']);
146
        $this->assertEquals('::1', $lookup->getValidIpAddress());
147
    }
148
149
    /**
150
     * Tests that we accept whitelisted proxy methods when the IP matches, even
151
     * if the IP listed is a comma separated list.
152
     *
153
     * @dataProvider proxyMethodWhitelistProvider
154
     */
155
    public function testValidWhitelistedProxyMethod($remoteAddr)
156
    {
157
        $lookup = new Whip(
158
            Whip::PROXY_HEADERS,
159
            [
160
                Whip::PROXY_HEADERS => [
161
                    Whip::IPV4 => ['127.0.0.1'],
162
                    Whip::IPV6 => ['::1']
163
                ]
164
            ],
165
            [
166
                'REMOTE_ADDR' => $remoteAddr,
167
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32,192.168.1.1'
168
            ]
169
        );
170
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
171
    }
172
173
    /**
174
     * Repeats the above test twice for ipv4 and ipv6
175
     */
176
    public function proxyMethodWhitelistProvider() : array
177
    {
178
        return [
179
            ['127.0.0.1'],
180
            ['::1'],
181
        ];
182
    }
183
184
    /**
185
     * Tests that we accept proxy method based on a whitelisted IP using the
186
     * dashed range notation.
187
     */
188
    public function testValidWhitelistedProxyMethodWithDashNotation()
189
    {
190
        $lookup = new Whip(
191
            Whip::PROXY_HEADERS,
192
            [
193
                Whip::PROXY_HEADERS => [
194
                    Whip::IPV4 => [
195
                        '127.0.0.0-127.0.255.255',
196
                    ],
197
                    Whip::IPV6 => [
198
                        '::1'
199
                    ]
200
                ]
201
            ],
202
            [
203
                'REMOTE_ADDR' => '127.0.0.1',
204
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
205
            ]
206
        );
207
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
208
    }
209
210
    /**
211
     * Tests that we accept proxy method based on a whitelisted IP using the
212
     * wildcard asterix notation.
213
     */
214
    public function testValidWhitelistedProxyMethodWithWildcardNotation()
215
    {
216
        $lookup = new Whip(
217
            Whip::PROXY_HEADERS,
218
            [
219
                Whip::PROXY_HEADERS => [
220
                    Whip::IPV4 => [
221
                        '127.0.*'
222
                    ],
223
                    Whip::IPV6 => [
224
                        '::1'
225
                    ]
226
                ]
227
            ],
228
            [
229
                'REMOTE_ADDR' => '127.0.0.1',
230
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
231
            ]
232
        );
233
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
234
    }
235
236
    /**
237
     * Tests that we accept proxy method based on a whitelisted IP using the
238
     * CIDR address notation.
239
     */
240
    public function testValidWhitelistedProxyMethodWithCIDRdNotation()
241
    {
242
        $lookup = new Whip(
243
            Whip::PROXY_HEADERS,
244
            [
245
                Whip::PROXY_HEADERS => [
246
                    Whip::IPV4 => [
247
                        '127.0.0.0/24'
248
                    ],
249
                    Whip::IPV6 => [
250
                        '::1'
251
                    ]
252
                ]
253
            ],
254
            [
255
                'REMOTE_ADDR' => '127.0.0.1',
256
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
257
            ]
258
        );
259
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
260
    }
261
262
    /**
263
     * Tests that we get false if there is a valid IP in a proxy header but
264
     * we reject it due to REMOTE_ADDR not being in the whitelist.
265
     */
266
    public function testValidIpRejectedDueToWhitelist()
267
    {
268
        $lookup = new Whip(
269
            Whip::PROXY_HEADERS,
270
            [
271
                Whip::PROXY_HEADERS => [
272
                    Whip::IPV4 => [
273
                        '127.0.0.1/24'
274
                    ],
275
                    Whip::IPV6 => [
276
                        '::1'
277
                    ]
278
                ]
279
            ],
280
            [
281
                'REMOTE_ADDR' => '24.24.24.24',
282
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
283
            ]
284
        );
285
        $this->assertFalse($lookup->getIpAddress());
286
    }
287
288
    /**
289
     * Tests that we reject a proxy listed IPv6 address that does not fall within
290
     * the allowed subnet.
291
     */
292
    public function testIPv6AddressRejectedDueToWhitelist()
293
    {
294
        $lookup = new Whip(
295
            Whip::PROXY_HEADERS,
296
            [
297
                Whip::PROXY_HEADERS => [
298
                    Whip::IPV6 => [
299
                        '2400:cb00::/32'
300
                    ]
301
                ]
302
            ],
303
            [
304
                'REMOTE_ADDR' => '::1',
305
                'HTTP_X_FORWARDED_FOR' => '::1'
306
            ]
307
        );
308
        $this->assertFalse($lookup->getIpAddress());
309
    }
310
311
    /**
312
     * Tests that we reject a proxy listed IPv6 address that does not fall within
313
     * the allowed subnet.
314
     */
315
    public function testIPv6AddressFoundInWhitelist()
316
    {
317
        $lookup = new Whip(
318
            Whip::PROXY_HEADERS,
319
            [
320
                Whip::PROXY_HEADERS => [
321
                    Whip::IPV6 => [
322
                        '::1/32'
323
                    ]
324
                ]
325
            ],
326
            [
327
                'REMOTE_ADDR' => '::1',
328
                'HTTP_X_FORWARDED_FOR' => '::1'
329
            ]
330
        );
331
        $this->assertEquals('::1', $lookup->getIpAddress());
332
    }
333
334
    /**
335
     * Test that an IPv4 address is rejected because the whitelist is empty for
336
     * IPv4.
337
     */
338
    public function testIPv4AddressRejectedDueToEmptyWhitelist()
339
    {
340
        $lookup = new Whip(
341
            Whip::PROXY_HEADERS,
342
            [
343
                Whip::PROXY_HEADERS => [
344
                    Whip::IPV6 => [
345
                        '::1/32'
346
                    ]
347
                ]
348
            ],
349
            [
350
                'REMOTE_ADDR' => '127.0.0.1',
351
                'HTTP_X_FORWARDED_FOR' => '24.24.24.24'
352
            ]
353
        );
354
        $this->assertFalse($lookup->getIpAddress());
355
    }
356
357
    /**
358
     * Test that an IPv6 address is rejected because the whitelist is empty for
359
     * IPv6.
360
     */
361
    public function testIPv6AddressRejectedDueToEmptyWhitelist()
362
    {
363
        $lookup = new Whip(
364
            Whip::PROXY_HEADERS,
365
            [
366
                Whip::PROXY_HEADERS => [
367
                    Whip::IPV4 => [
368
                        '127.0.0.0/24'
369
                    ]
370
                ]
371
            ],
372
            [
373
                'REMOTE_ADDR' => '::1',
374
                'HTTP_X_FORWARDED_FOR' => '::1'
375
            ]
376
        );
377
        $this->assertFalse($lookup->getIpAddress());
378
    }
379
380
    /**
381
     * Test a custom header with a whitelisted IP.
382
     */
383
    public function testCustomHeader()
384
    {
385
        $lookup = new Whip(
386
            Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR,
387
            [
388
                Whip::CUSTOM_HEADERS => [
389
                    Whip::IPV4 => [
390
                        '127.0.0.1',
391
                        '::1'
392
                    ]
393
                ]
394
            ],
395
            [
396
                'REMOTE_ADDR' => '127.0.0.1',
397
                'HTTP_CUSTOM_SECRET_HEADER' => '32.32.32.32'
398
            ]
399
        );
400
        $this->assertEquals(
401
            '32.32.32.32',
402
            $lookup->addCustomHeader('HTTP_CUSTOM_SECRET_HEADER')->getIpAddress()
403
        );
404
    }
405
406
    /**
407
     * Test HTTP_X_REAL_IP header.
408
     */
409
    public function testHttpXRealIpHeader()
410
    {
411
        $lookup = new Whip(
412
            Whip::PROXY_HEADERS | Whip::REMOTE_ADDR,
413
            [],
414
            [
415
                'REMOTE_ADDR' => '127.0.0.1',
416
                'HTTP_X_REAL_IP' => '24.24.24.24'
417
            ]
418
        );
419
        $this->assertEquals('24.24.24.24', $lookup->getIpAddress());
420
    }
421
422
    /**
423
     * Tests that if we specify the source array, it overrides any values found
424
     * in the $_SERVER array.
425
     */
426
    public function testSourceArrayOverridesServerSuperglobal()
427
    {
428
        $source = [
429
            'REMOTE_ADDR' => '24.24.24.24'
430
        ];
431
        $lookup = new Whip(Whip::REMOTE_ADDR, [], ['REMOTE_ADDR' => '127.0.0.1']);
432
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
433
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress($source));
434
    }
435
436
    /**
437
     * Tests that if we specify the source array through Whip::setSource, the
438
     * class will override any values found in $_SERVER.
439
     */
440
    public function testSetSourceArrayOverridesServerSuperglobal()
441
    {
442
        $source = [
443
            'REMOTE_ADDR' => '24.24.24.24'
444
        ];
445
        $lookup = new Whip(Whip::REMOTE_ADDR, [], ['REMOTE_ADDR' => '127.0.0.1']);
446
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
447
        $lookup->setSource($source);
448
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
449
    }
450
451
    /**
452
     * Tests that we fallback to REMOTE_ADDR if the custom header was not found
453
     */
454
    public function testFallbackToRemoteAddr()
455
    {
456
        $source = [
457
            'REMOTE_ADDR' => '24.24.24.24'
458
        ];
459
        $lookup = new Whip(Whip::PROXY_HEADERS | Whip::REMOTE_ADDR, [], $source);
460
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
461
    }
462
}
463