*/ public static function addressProvider(): iterable { yield 'loopback v4' => ['127.0.0.1', true]; yield 'loopback v6' => ['::1', true]; yield 'rfc1918 10/8' => ['10.5.6.7', true]; yield 'rfc1918 172.16/12' => ['172.16.42.1', true]; yield 'rfc1918 172.31/12 (boundary)' => ['172.31.255.255', true]; yield 'just outside 172.16/12' => ['172.32.0.1', false]; yield 'rfc1918 192.168/16' => ['192.168.1.1', true]; yield 'public 1.1.1.1' => ['1.1.1.1', false]; yield 'public v4' => ['203.0.113.4', false]; yield 'public v6' => ['2001:db8::1', false]; yield 'malformed' => ['not-an-ip', false]; yield 'empty' => ['', false]; } #[DataProvider('addressProvider')] public function testNetworkGate(string $remoteAddr, bool $shouldPass): void { $middleware = new InternalNetworkMiddleware(new ResponseFactory()); $req = (new ServerRequestFactory())->createServerRequest( 'POST', '/internal/jobs/tick', ['REMOTE_ADDR' => $remoteAddr], ); $passthrough = new class () implements RequestHandlerInterface { public bool $reached = false; public function handle(ServerRequestInterface $request): ResponseInterface { $this->reached = true; $factory = new ResponseFactory(); return $factory->createResponse(204); } }; $response = $middleware->process($req, $passthrough); if ($shouldPass) { self::assertSame(204, $response->getStatusCode()); self::assertTrue($passthrough->reached); } else { self::assertSame(404, $response->getStatusCode()); self::assertFalse($passthrough->reached, 'handler must not see disallowed sources'); } } }