fix: set default ipv6 subnet to 64 (#7509)

This commit is contained in:
Alex Yang
2026-01-20 16:14:04 -08:00
parent f4440274e0
commit 5f0bc5dfb3
4 changed files with 37 additions and 25 deletions

View File

@@ -336,7 +336,7 @@ describe("IPv6 address normalization and rate limiting", () => {
// All should normalize to the same value
expect(uniqueValues.size).toBe(1);
expect(normalized[0]).toBe("2001:0db8:0000:0000:0000:0000:0000:0001");
expect(normalized[0]).toBe("2001:0db8:0000:0000:0000:0000:0000:0000");
});
it("should convert IPv4-mapped IPv6 to IPv4", () => {
@@ -393,12 +393,12 @@ describe("IPv6 address normalization and rate limiting", () => {
});
it("should handle localhost IPv6 addresses", () => {
expect(normalizeIP("::1")).toBe("0000:0000:0000:0000:0000:0000:0000:0001");
expect(normalizeIP("::1")).toBe("0000:0000:0000:0000:0000:0000:0000:0000");
});
it("should handle link-local IPv6 addresses", () => {
const linkLocal = normalizeIP("fe80::1");
expect(linkLocal).toBe("fe80:0000:0000:0000:0000:0000:0000:0001");
expect(linkLocal).toBe("fe80:0000:0000:0000:0000:0000:0000:0000");
});
it("IPv6 subnet should not affect IPv4 addresses", () => {

View File

@@ -158,7 +158,7 @@ export type BetterAuthAdvancedOptions = {
* Note: This only affects IPv6 addresses. IPv4 addresses are always
* rate limited individually.
*
* @default 128 (individual address)
* @default 64 (/64 subnet)
*/
ipv6Subnet?: 128 | 64 | 48 | 32 | undefined;
}

View File

@@ -34,20 +34,22 @@ describe("IP Normalization", () => {
describe("IPv6 Normalization", () => {
it("should normalize compressed IPv6 to full form", () => {
expect(normalizeIP("2001:db8::1")).toBe(
expect(normalizeIP("2001:db8::1", { ipv6Subnet: 128 })).toBe(
"2001:0db8:0000:0000:0000:0000:0000:0001",
);
expect(normalizeIP("::1")).toBe(
expect(normalizeIP("::1", { ipv6Subnet: 128 })).toBe(
"0000:0000:0000:0000:0000:0000:0000:0001",
);
expect(normalizeIP("::")).toBe("0000:0000:0000:0000:0000:0000:0000:0000");
expect(normalizeIP("::", { ipv6Subnet: 128 })).toBe(
"0000:0000:0000:0000:0000:0000:0000:0000",
);
});
it("should normalize uppercase to lowercase", () => {
expect(normalizeIP("2001:DB8::1")).toBe(
expect(normalizeIP("2001:DB8::1", { ipv6Subnet: 128 })).toBe(
"2001:0db8:0000:0000:0000:0000:0000:0001",
);
expect(normalizeIP("2001:0DB8:ABCD:EF00::1")).toBe(
expect(normalizeIP("2001:0DB8:ABCD:EF00::1", { ipv6Subnet: 128 })).toBe(
"2001:0db8:abcd:ef00:0000:0000:0000:0001",
);
});
@@ -55,16 +57,22 @@ describe("IP Normalization", () => {
it("should handle various IPv6 formats consistently", () => {
// All these represent the same address
const normalized = "2001:0db8:0000:0000:0000:0000:0000:0001";
expect(normalizeIP("2001:db8::1")).toBe(normalized);
expect(normalizeIP("2001:0db8:0:0:0:0:0:1")).toBe(normalized);
expect(normalizeIP("2001:db8:0::1")).toBe(normalized);
expect(normalizeIP("2001:0db8::0:0:0:1")).toBe(normalized);
expect(normalizeIP("2001:db8::1", { ipv6Subnet: 128 })).toBe(normalized);
expect(normalizeIP("2001:0db8:0:0:0:0:0:1", { ipv6Subnet: 128 })).toBe(
normalized,
);
expect(normalizeIP("2001:db8:0::1", { ipv6Subnet: 128 })).toBe(
normalized,
);
expect(normalizeIP("2001:0db8::0:0:0:1", { ipv6Subnet: 128 })).toBe(
normalized,
);
});
it("should handle IPv6 with :: at different positions", () => {
expect(normalizeIP("2001:db8:85a3::8a2e:370:7334")).toBe(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
);
expect(
normalizeIP("2001:db8:85a3::8a2e:370:7334", { ipv6Subnet: 128 }),
).toBe("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
expect(normalizeIP("::ffff:192.0.2.1")).not.toContain("::");
});
});
@@ -131,11 +139,11 @@ describe("IP Normalization", () => {
expect(ip1).toBe(ip2);
});
it("should handle /128 (full address) by default", () => {
it("should handle /64 subnet by default", () => {
const ip1 = normalizeIP("2001:db8::1");
const ip2 = normalizeIP("2001:db8::1", { ipv6Subnet: 128 });
const ip2 = normalizeIP("2001:db8::1", { ipv6Subnet: 64 });
expect(ip1).toBe(ip2);
expect(ip1).toBe("2001:0db8:0000:0000:0000:0000:0000:0001");
expect(ip1).toBe("2001:0db8:0000:0000:0000:0000:0000:0000");
});
it("should not affect IPv4 addresses when ipv6Subnet is set", () => {
@@ -179,7 +187,9 @@ describe("IP Normalization", () => {
"2001:db8::0:1",
];
const normalized = representations.map((ip) => normalizeIP(ip));
const normalized = representations.map((ip) =>
normalizeIP(ip, { ipv6Subnet: 128 }),
);
// All should normalize to the same value
const uniqueValues = new Set(normalized);
expect(uniqueValues.size).toBe(1);
@@ -223,19 +233,21 @@ describe("IP Normalization", () => {
describe("Edge Cases", () => {
it("should handle localhost addresses", () => {
expect(normalizeIP("127.0.0.1")).toBe("127.0.0.1");
expect(normalizeIP("::1")).toBe(
expect(normalizeIP("::1", { ipv6Subnet: 128 })).toBe(
"0000:0000:0000:0000:0000:0000:0000:0001",
);
});
it("should handle all-zeros address", () => {
expect(normalizeIP("0.0.0.0")).toBe("0.0.0.0");
expect(normalizeIP("::")).toBe("0000:0000:0000:0000:0000:0000:0000:0000");
expect(normalizeIP("::", { ipv6Subnet: 128 })).toBe(
"0000:0000:0000:0000:0000:0000:0000:0000",
);
});
it("should handle link-local addresses", () => {
expect(normalizeIP("169.254.0.1")).toBe("169.254.0.1");
expect(normalizeIP("fe80::1")).toBe(
expect(normalizeIP("fe80::1", { ipv6Subnet: 128 })).toBe(
"fe80:0000:0000:0000:0000:0000:0000:0001",
);
});

View File

@@ -160,7 +160,7 @@ function normalizeIPv6(
*
* @example
* normalizeIP("2001:DB8::1")
* // -> "2001:0db8:0000:0000:0000:0000:0000:0001"
* // -> "2001:0db8:0000:0000:0000:0000:0000:0000"
*
* @example
* normalizeIP("::ffff:192.0.2.1")
@@ -192,7 +192,7 @@ export function normalizeIP(
}
// Normalize IPv6
const subnetPrefix = options.ipv6Subnet || 128;
const subnetPrefix = options.ipv6Subnet || 64;
return normalizeIPv6(ip, subnetPrefix);
}