One household's line or one server gets a whole /64 of IPv6 addresses, and could turn through them past every per-network limit (the second reading's M5). networkOf now counts an IPv6 visitor by their /64 (networkKeyOf), an IPv4 visitor by their address as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
155 lines
4.3 KiB
Dart
155 lines
4.3 KiB
Dart
import 'package:halkyon_base_server/base.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('The proxies the operator trusts (TRUSTED_PROXIES)', () {
|
|
test('are none where unset or empty', () {
|
|
expect(trustedProxiesFrom(null), isEmpty);
|
|
expect(trustedProxiesFrom(''), isEmpty);
|
|
expect(trustedProxiesFrom(' '), isEmpty);
|
|
});
|
|
|
|
test('are IP addresses, separated by commas or spaces', () {
|
|
expect(trustedProxiesFrom('172.30.80.2'), {'172.30.80.2'});
|
|
expect(trustedProxiesFrom(' 172.30.80.2, 2001:db8::2 10.0.0.1 '), {
|
|
'172.30.80.2',
|
|
'2001:db8::2',
|
|
'10.0.0.1',
|
|
});
|
|
});
|
|
|
|
test('take an IPv4 address written as IPv6 as the IPv4 address', () {
|
|
expect(trustedProxiesFrom('::ffff:172.30.80.2'), {'172.30.80.2'});
|
|
});
|
|
|
|
test('stop the server for anything but addresses', () {
|
|
expect(() => trustedProxiesFrom('caddy'), throwsStateError);
|
|
expect(() => trustedProxiesFrom('172.30.80.0/24'), throwsStateError);
|
|
expect(
|
|
() => trustedProxiesFrom('172.30.80.2, 300.1.1.1'),
|
|
throwsStateError,
|
|
);
|
|
});
|
|
});
|
|
|
|
group("A request's network", () {
|
|
const caddy = {'172.30.80.2'};
|
|
|
|
test("is the connection's address where no proxy is trusted, whatever "
|
|
'X-Forwarded-For says', () {
|
|
expect(
|
|
visitorAddress(
|
|
socket: '203.0.113.9',
|
|
forwardedFor: ['198.51.100.7'],
|
|
trustedProxies: const {},
|
|
),
|
|
'203.0.113.9',
|
|
);
|
|
});
|
|
|
|
test('is the last address of X-Forwarded-For, the one the trusted proxy '
|
|
'saw itself, for a connection from that proxy', () {
|
|
expect(
|
|
visitorAddress(
|
|
socket: '172.30.80.2',
|
|
forwardedFor: ['198.51.100.7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
'198.51.100.7',
|
|
);
|
|
// What a visitor sent along is in front of it, never believed.
|
|
expect(
|
|
visitorAddress(
|
|
socket: '172.30.80.2',
|
|
forwardedFor: ['10.9.9.9, 192.0.2.1 , 198.51.100.7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
'198.51.100.7',
|
|
);
|
|
expect(
|
|
visitorAddress(
|
|
socket: '172.30.80.2',
|
|
forwardedFor: ['10.9.9.9', '2001:db8::7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
'2001:db8::7',
|
|
);
|
|
});
|
|
|
|
test('is never X-Forwarded-For from a connection the operator trusts '
|
|
'not', () {
|
|
expect(
|
|
visitorAddress(
|
|
socket: '203.0.113.9',
|
|
forwardedFor: ['198.51.100.7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
'203.0.113.9',
|
|
);
|
|
});
|
|
|
|
test("knows the proxy by its IPv4 address on the server's IPv6 socket", () {
|
|
expect(
|
|
visitorAddress(
|
|
socket: '::ffff:172.30.80.2',
|
|
forwardedFor: ['::ffff:198.51.100.7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
'198.51.100.7',
|
|
);
|
|
});
|
|
|
|
test("is the proxy's own address where its header is missing or no "
|
|
'address', () {
|
|
for (final header in [
|
|
null,
|
|
<String>[],
|
|
[''],
|
|
['198.51.100.7, '],
|
|
['unknown'],
|
|
['198.51.100.7:4711'],
|
|
['[2001:db8::7]'],
|
|
]) {
|
|
expect(
|
|
visitorAddress(
|
|
socket: '172.30.80.2',
|
|
forwardedFor: header,
|
|
trustedProxies: caddy,
|
|
),
|
|
'172.30.80.2',
|
|
reason: '$header',
|
|
);
|
|
}
|
|
});
|
|
|
|
test("is an IPv6 visitor's /64, as one household or server holds a "
|
|
'whole /64 and could turn through its addresses past every limit; '
|
|
'an IPv4 visitor its address', () {
|
|
expect(
|
|
networkKeyOf('2001:db8:1:2:aaaa:bbbb:cccc:1'),
|
|
'2001:db8:1:2::/64',
|
|
);
|
|
expect(
|
|
networkKeyOf('2001:db8:1:2::ffff'),
|
|
networkKeyOf('2001:db8:1:2:9::1'),
|
|
);
|
|
expect(
|
|
networkKeyOf('2001:db8:1:3::1'),
|
|
isNot(networkKeyOf('2001:db8:1:2::1')),
|
|
);
|
|
expect(networkKeyOf('198.51.100.7'), '198.51.100.7');
|
|
expect(networkKeyOf(null), 'null');
|
|
});
|
|
|
|
test('is none without a connection (tests, future calls)', () {
|
|
expect(
|
|
visitorAddress(
|
|
socket: null,
|
|
forwardedFor: ['198.51.100.7'],
|
|
trustedProxies: caddy,
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
});
|
|
}
|