Passed
Pull Request — master (#257)
by Wilmer
10:49
created

TrustedHostsNetworkResolverTest   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 162
c 1
b 0
f 0
dl 0
loc 277
rs 10
wmc 10
1
<?php
2
3
namespace Yiisoft\Yii\Web\Tests\Middleware;
4
5
use Nyholm\Psr7\Factory\Psr17Factory;
6
use Nyholm\Psr7\ServerRequest;
7
use PHPUnit\Framework\TestCase;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Yiisoft\Yii\Web\Middleware\TrustedHostsNetworkResolver;
10
use Yiisoft\Yii\Web\Tests\Middleware\Mock\MockRequestHandler;
11
12
class TrustedHostsNetworkResolverTest extends TestCase
13
{
14
    protected function newRequestWithSchemaAndHeaders(
15
        string $scheme = 'http',
16
        array $headers = [],
17
        array $serverParams = []
18
    ): ServerRequestInterface {
19
        $request = new ServerRequest('GET', '/', $headers, null, '1.1', $serverParams);
20
        $uri = $request->getUri()->withScheme($scheme);
21
        return $request->withUri($uri);
22
    }
23
24
    public function trustedDataProvider(): array
25
    {
26
        return [
27
            'xForwardLevel1' => [
28
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
29
                ['REMOTE_ADDR' => '127.0.0.1'],
30
                [
31
                    ['hosts' => ['8.8.8.8', '127.0.0.1'], 'ipHeaders' => ['x-forwarded-for']]
32
                ],
33
                '2.2.2.2',
34
            ],
35
            'xForwardLevel2' => [
36
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
37
                ['REMOTE_ADDR' => '127.0.0.1'],
38
                [
39
                    ['hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'], 'ipHeaders' => ['x-forwarded-for']],
40
                ],
41
                '5.5.5.5',
42
            ],
43
            'rfc7239Level1' => [
44
                ['forwarded' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
45
                ['REMOTE_ADDR' => '127.0.0.1'],
46
                [
47
                    [
48
                        'hosts' => ['8.8.8.8', '127.0.0.1'],
49
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
50
                    ]
51
                ],
52
                '2.2.2.2',
53
            ],
54
            'rfc7239Level2' => [
55
                ['forwarded' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
56
                ['REMOTE_ADDR' => '127.0.0.1'],
57
                [
58
                    [
59
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
60
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
61
                    ],
62
63
                ],
64
                '5.5.5.5',
65
            ],
66
            'rfc7239Level2HostAndProtocol' => [
67
                ['forwarded' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2']],
68
                ['REMOTE_ADDR' => '127.0.0.1'],
69
                [
70
                    [
71
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
72
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
73
                        'hostHeaders' => ['forwarded'],
74
                        'protocolHeaders' => ['forwarded' => ['http' => 'http', 'https' => 'https']],
75
                    ],
76
                ],
77
                '5.5.5.5',
78
                'test',
79
                'https',
80
            ],
81
            'rfc7239Level2HostAndProtocolAndUrl' => [
82
                [
83
                    'forwarded' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2'],
84
                    'x-rewrite-url' => ['/test?test=test'],
85
                ],
86
                ['REMOTE_ADDR' => '127.0.0.1'],
87
                [
88
                    [
89
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
90
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
91
                        'hostHeaders' => ['forwarded'],
92
                        'protocolHeaders' => ['forwarded' => ['http' => 'http', 'https' => 'https']],
93
                        'urlHeaders' => ['x-rewrite-url'],
94
                    ],
95
                ],
96
                '5.5.5.5',
97
                'test',
98
                'https',
99
                '/test',
100
                'test=test',
101
            ],
102
            'rfc7239Level2AnotherHost&AnotherProtocol&Url' => [
103
                [
104
                    'forwarded' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2'],
105
                    'x-rewrite-url' => ['/test?test=test'],
106
                    'x-forwarded-host' => ['test.another'],
107
                    'x-forwarded-proto' => ['on']
108
                ],
109
                ['REMOTE_ADDR' => '127.0.0.1'],
110
                [
111
                    [
112
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
113
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
114
                        'hostHeaders' => ['x-forwarded-host', 'forwarded'],
115
                        'protocolHeaders' => [
116
                            'x-forwarded-proto' => ['http' => 'http', 'httpsss' => 'on'],
117
                            'forwarded' => ['http' => 'http', 'https' => 'https']
118
                        ],
119
                        'urlHeaders' => ['x-rewrite-url'],
120
                    ],
121
                ],
122
                '5.5.5.5',
123
                'test.another',
124
                'httpsss',
125
                '/test',
126
                'test=test',
127
            ],
128
            'rfc7239Level2AnotherHost&AnotherProtocol&Url&Port' => [
129
                [
130
                    'forwarded' => ['for=9.9.9.9', 'proto=https;for="5.5.5.5:123";host=test', 'for=2.2.2.2'],
131
                    'x-rewrite-url' => ['/test?test=test'],
132
                    'x-forwarded-host' => ['test.another'],
133
                    'x-forwarded-proto' => ['on']
134
                ],
135
                ['REMOTE_ADDR' => '127.0.0.1'],
136
                [
137
                    [
138
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
139
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
140
                        'hostHeaders' => ['x-forwarded-host', 'forwarded'],
141
                        'protocolHeaders' => [
142
                            'x-forwarded-proto' => ['http' => 'http', 'httpsss' => 'on'],
143
                            'forwarded' => ['http' => 'http', 'https' => 'https']
144
                        ],
145
                        'urlHeaders' => ['x-rewrite-url'],
146
                        'portHeaders' => ['forwarded'],
147
                    ],
148
                ],
149
                '5.5.5.5',
150
                'test.another',
151
                'httpsss',
152
                '/test',
153
                'test=test',
154
                123,
155
            ],
156
        ];
157
    }
158
159
    /**
160
     * @dataProvider trustedDataProvider
161
     */
162
    public function testTrusted(
163
        array $headers,
164
        array $serverParams,
165
        array $trustedHosts,
166
        string $expectedClientIp,
167
        ?string $expectedHttpHost = null,
168
        string $expectedHttpScheme = 'http',
169
        string $expectedPath = '/',
170
        string $expectedQuery = '',
171
        ?int $expectedPort = null
172
    ): void {
173
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
174
        $requestHandler = new MockRequestHandler();
175
176
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
177
        foreach ($trustedHosts as $data) {
178
            $middleware = $middleware->withAddedTrustedHosts(
179
                $data['hosts'],
180
                $data['ipHeaders'] ?? [],
181
                $data['protocolHeaders'] ?? [],
182
                $data['hostHeaders'] ?? [],
183
                $data['urlHeaders'] ?? [],
184
                $data['portHeaders'] ?? [],
185
                $data['trustedHeaders'] ?? null
186
            );
187
        }
188
        $response = $middleware->process($request, $requestHandler);
189
        $this->assertSame(200, $response->getStatusCode());
190
        $this->assertSame($expectedClientIp, $requestHandler->processedRequest->getAttribute('requestClientIp'));
191
        if ($expectedHttpHost !== null) {
192
            $this->assertSame($expectedHttpHost, $requestHandler->processedRequest->getUri()->getHost());
193
        }
194
        $this->assertSame($expectedHttpScheme, $requestHandler->processedRequest->getUri()->getScheme());
195
        $this->assertSame($expectedPath, $requestHandler->processedRequest->getUri()->getPath());
196
        $this->assertSame($expectedQuery, $requestHandler->processedRequest->getUri()->getQuery());
197
        $this->assertSame($expectedPort, $requestHandler->processedRequest->getUri()->getPort());
198
    }
199
200
    public function notTrustedDataProvider(): array
201
    {
202
        return [
203
            'none' => [
204
                [],
205
                ['REMOTE_ADDR' => '127.0.0.1'],
206
                [],
207
            ],
208
            'x-forwarded-for' => [
209
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
210
                ['REMOTE_ADDR' => '127.0.0.1'],
211
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
212
            ],
213
            'rfc7239' => [
214
                ['x-forwarded-for' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
215
                ['REMOTE_ADDR' => '127.0.0.1'],
216
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
217
            ],
218
        ];
219
    }
220
221
    /**
222
     * @dataProvider notTrustedDataProvider
223
     */
224
    public function testNotTrusted(array $headers, array $serverParams, array $trustedHosts): void
225
    {
226
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
227
        $requestHandler = new MockRequestHandler();
228
229
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
230
        foreach ($trustedHosts as $data) {
231
            $middleware = $middleware->withAddedTrustedHosts(
232
                $data['hosts'],
233
                $data['ipHeaders'] ?? [],
234
                $data['protocolHeaders'] ?? [],
235
                [],
236
                [],
237
                [],
238
                $data['trustedHeaders'] ?? []
239
            );
240
        }
241
        $middleware->process($request, $requestHandler);
242
        $this->assertNull($request->getAttribute('requestClientIp'));
243
    }
244
245
    public function addedTrustedHostsInvalidParameterDataProvider(): array
246
    {
247
        return [
248
            'hostsEmpty' => ['hosts' => []],
249
            'hostsEmptyString' => ['hosts' => ['']],
250
            'hostsNumeric' => ['hosts' => [888]],
251
            'hostsSpaces' => ['hosts' => ['    ']],
252
            'hostsNotDomain' => ['host' => ['-apple']],
253
            'urlHeadersEmpty' => ['urlHeaders' => ['']],
254
            'urlHeadersNumeric' => ['urlHeaders' => [888]],
255
            'urlHeadersSpaces' => ['urlHeaders' => ['   ']],
256
            'trustedHeadersEmpty' => ['trustedHeaders' => ['']],
257
            'trustedHeadersNumeric' => ['trustedHeaders' => [888]],
258
            'trustedHeadersSpaces' => ['trustedHeaders' => ['   ']],
259
            'protocolHeadersNumeric' => ['protocolHeaders' => ['http' => 888]],
260
            'ipHeadersEmptyString' => ['ipHeaders' => [' ']],
261
            'ipHeadersNumeric' => ['ipHeaders' => [888]],
262
            'ipHeadersInvalidType' => ['ipHeaders' => [['---', 'aaa']]],
263
            'ipHeadersInvalidTypeValue' => [
264
                'ipHeaders' => [
265
                    [
266
                        TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239,
267
                        888
268
                    ]
269
                ]
270
            ],
271
        ];
272
    }
273
274
    /**
275
     * @dataProvider addedTrustedHostsInvalidParameterDataProvider
276
     */
277
    public function testAddedTrustedHostsInvalidParameter(array $data): void
278
    {
279
        $this->expectException(\InvalidArgumentException::class);
280
        (new TrustedHostsNetworkResolver(new Psr17Factory()))
281
            ->withAddedTrustedHosts(
282
                $data['hosts'] ?? [],
283
                $data['ipHeaders'] ?? [],
284
                $data['protocolHeaders'] ?? [],
285
                $data['hostHeaders'] ?? [],
286
                $data['urlHeaders'] ?? [],
287
                $data['portHeaders'] ?? [],
288
                $data['trustedHeaders'] ?? null
289
            );
290
    }
291
}
292