toBeNull(); })->with([ 'ipv4 loopback' => 'https://127.0.0.1', 'ipv4 shortened notation' => 'https://127.1', 'ipv4 decimal integer notation' => 'https://2130706433', 'ipv4 hexadecimal notation' => 'https://0x7f000001', 'ipv4 octal notation' => 'https://0177.0.0.1', 'ipv4 private' => 'https://10.0.0.1', 'ipv4 link local' => 'https://169.254.169.254', 'ipv4 cgnat' => 'https://100.64.0.1', 'ipv4 documentation' => 'https://192.0.2.1', 'ipv6 loopback' => 'https://[::1]', 'ipv6 unique local' => 'https://[fd00::1]', 'ipv6 link local' => 'https://[fe80::1]', 'ipv6 nat64 private target' => 'https://[64:ff9b::a00:1]', 'ipv6 protocol assignment' => 'https://[2001::1]', 'ipv6 documentation' => 'https://[2001:db8::1]', ]); test('wallet api guard rejects hostname when any dns answer is non-public', function (): void { fakeWalletApiDns([ 'wallet.example.test' => ['93.184.216.34', '10.0.0.8'], ], []); expect(app(WalletApiRequestGuard::class)->guard('https://wallet.example.test', 10)) ->toBeNull(); }); test('wallet api guard rejects unresolved hostname', function (): void { fakeWalletApiDns([], []); expect(app(WalletApiRequestGuard::class)->guard('https://missing.example.test', 10)) ->toBeNull(); }); test('wallet api guard accepts public ipv4 and ipv6 dns answers and bounds connect timeout', function (): void { fakeWalletApiDns([ 'wallet.example.test' => ['93.184.216.34', '2606:4700:4700::1111'], ], []); $endpoint = app(WalletApiRequestGuard::class)->guard('https://wallet.example.test:8443', 120); expect($endpoint)->not->toBeNull() ->and($endpoint?->baseUrl)->toBe('https://wallet.example.test:8443') ->and($endpoint?->port)->toBe(8443) ->and($endpoint?->pinnedIp)->toBe('93.184.216.34') ->and($endpoint?->totalTimeoutSeconds)->toBe(120) ->and($endpoint?->connectTimeoutSeconds)->toBe(5); $pending = $endpoint?->request(['Authorization' => 'Bearer secret']); $optionsProperty = new ReflectionProperty(PendingRequest::class, 'options'); $options = $optionsProperty->getValue($pending); expect($options['allow_redirects'] ?? null)->toBeFalse() ->and($options['proxy'] ?? null)->toBe('') ->and($options['timeout'] ?? null)->toBe(120) ->and($options['connect_timeout'] ?? null)->toBe(5) ->and($options['curl'][CURLOPT_RESOLVE] ?? null) ->toBe(['wallet.example.test:8443:93.184.216.34']); }); test('wallet api sanitizer accepts normal public literals', function (): void { expect(WalletApiUrlSanitizer::normalizeAndValidate('https://93.184.216.34')) ->toBe('https://93.184.216.34') ->and(WalletApiUrlSanitizer::normalizeAndValidate('https://[2606:4700:4700::1111]')) ->toBe('https://[2606:4700:4700::1111]'); });