Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,266 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
SubdomainTenantResolverError,
TENANT_SUBDOMAIN_CODES,
} from "../error-codes/error-codes.js";
import { createSubdomainTenantResolver } from "../resolver/resolver.js";
import {
cidrMatches,
isTrustedProxy,
parseCidr,
parseIp,
} from "./forwarded.js";
function reqWith(
headers: Record<string, string>,
extras: Record<string, unknown> = {}
): Request {
const req = new Request(`https://example.com/${Math.random()}`, {
headers,
});
for (const [k, v] of Object.entries(extras)) {
Object.defineProperty(req, k, { value: v, enumerable: true });
}
return req;
}
describe("parseCidr", () => {
it("parses IPv4 with prefix", { timeout: 5000 }, () => {
const c = parseCidr("10.0.0.0/8");
expect(c).not.toBeNull();
expect(c?.family).toBe(4);
expect(c?.prefix).toBe(8);
expect(Array.from(c?.bytes ?? [])).toEqual([10, 0, 0, 0]);
});
it("defaults IPv4 prefix to 32 when omitted", { timeout: 5000 }, () => {
const c = parseCidr("10.4.7.2");
expect(c?.prefix).toBe(32);
});
it("parses IPv6 with prefix and :: shorthand", { timeout: 5000 }, () => {
const c = parseCidr("2001:db8::/32");
expect(c?.family).toBe(6);
expect(c?.prefix).toBe(32);
});
it("defaults IPv6 prefix to 128 when omitted", { timeout: 5000 }, () => {
const c = parseCidr("2001:db8::1");
expect(c?.prefix).toBe(128);
});
it("returns null for malformed input", { timeout: 5000 }, () => {
expect(parseCidr("")).toBeNull();
expect(parseCidr(null as unknown as string)).toBeNull();
expect(parseCidr("999.0.0.0/8")).toBeNull();
expect(parseCidr("10.0.0.0/33")).toBeNull();
expect(parseCidr("10.0.0.0/abc")).toBeNull();
expect(parseCidr("2001:db8::/129")).toBeNull();
expect(parseCidr("2001:db8::/zz")).toBeNull();
expect(parseCidr("not-an-ip")).toBeNull();
expect(parseCidr("1:2:3:4:5:6:7:8:9")).toBeNull();
expect(parseCidr("g:db8::1")).toBeNull();
expect(parseCidr("1.2.3")).toBeNull();
expect(parseCidr("1.2.3.4.5")).toBeNull();
});
it("rejects extra ipv6 groups without ::", { timeout: 5000 }, () => {
expect(parseCidr("1:2:3:4:5:6:7")).toBeNull();
});
it("parses fully-expanded ipv6 (no ::)", { timeout: 5000 }, () => {
const c = parseCidr("1:2:3:4:5:6:7:8/128");
expect(c?.family).toBe(6);
expect(c?.prefix).toBe(128);
});
it("rejects ipv6 hex group longer than 4 chars", {
timeout: 5000,
}, () => {
expect(parseCidr("12345::1")).toBeNull();
});
it("rejects ipv6 empty hex group", { timeout: 5000 }, () => {
// Triple colon makes the middle slot an empty group, not a ::.
expect(parseCidr("1:::2")).toBeNull();
});
it("parses '::' as all-zero", { timeout: 5000 }, () => {
const c = parseCidr("::");
expect(c?.family).toBe(6);
expect(Array.from(c?.bytes ?? [])).toEqual(Array(16).fill(0));
});
it("parses '::1'", { timeout: 5000 }, () => {
const c = parseCidr("::1");
expect(c?.family).toBe(6);
});
it("parses '1::'", { timeout: 5000 }, () => {
const c = parseCidr("1::");
expect(c?.family).toBe(6);
});
it("strips IPv6 zone-id before parsing", { timeout: 5000 }, () => {
const c = parseCidr("fe80::1%eth0/64");
expect(c?.family).toBe(6);
expect(c?.prefix).toBe(64);
});
});
describe("parseIp", () => {
it("parses IPv4 and IPv6", { timeout: 5000 }, () => {
expect(parseIp("10.0.0.1")?.family).toBe(4);
expect(parseIp("2001:db8::1")?.family).toBe(6);
});
it("returns null for empty or malformed", { timeout: 5000 }, () => {
expect(parseIp("")).toBeNull();
expect(parseIp(null as unknown as string)).toBeNull();
expect(parseIp("not-an-ip")).toBeNull();
expect(parseIp("zzzz::1")).toBeNull();
});
});
describe("cidrMatches", () => {
it("matches v4 inside /8", { timeout: 5000 }, () => {
const cidr = parseCidr("10.0.0.0/8");
const ip = parseIp("10.4.7.2");
expect(cidrMatches(ip!, cidr!)).toBe(true);
});
it("rejects v4 outside /8", { timeout: 5000 }, () => {
const cidr = parseCidr("10.0.0.0/8");
const ip = parseIp("8.8.8.8");
expect(cidrMatches(ip!, cidr!)).toBe(false);
});
it("matches /0 universally inside family", { timeout: 5000 }, () => {
const cidr = parseCidr("0.0.0.0/0");
expect(cidrMatches(parseIp("1.2.3.4")!, cidr!)).toBe(true);
});
it("matches v6 inside /32", { timeout: 5000 }, () => {
const cidr = parseCidr("2001:db8::/32");
expect(cidrMatches(parseIp("2001:db8:1::1")!, cidr!)).toBe(true);
expect(cidrMatches(parseIp("2001:dead::1")!, cidr!)).toBe(false);
});
it("family-disjoint match returns false", { timeout: 5000 }, () => {
const cidr = parseCidr("10.0.0.0/8");
const ip = parseIp("2001:db8::1");
expect(cidrMatches(ip!, cidr!)).toBe(false);
});
it("handles partial-byte prefixes", { timeout: 5000 }, () => {
const cidr = parseCidr("10.4.0.0/14");
expect(cidrMatches(parseIp("10.4.0.1")!, cidr!)).toBe(true);
expect(cidrMatches(parseIp("10.7.255.255")!, cidr!)).toBe(true);
expect(cidrMatches(parseIp("10.8.0.0")!, cidr!)).toBe(false);
});
});
describe("isTrustedProxy", () => {
it("returns false for nullish remoteAddress", { timeout: 5000 }, () => {
expect(isTrustedProxy(null, [])).toBe(false);
expect(isTrustedProxy(undefined, [])).toBe(false);
});
it("returns false for malformed remoteAddress", { timeout: 5000 }, () => {
const c = parseCidr("10.0.0.0/8");
expect(isTrustedProxy("not-an-ip", [c!])).toBe(false);
});
it("returns true when any CIDR matches", { timeout: 5000 }, () => {
const cidrs = [parseCidr("10.0.0.0/8")!, parseCidr("172.16.0.0/12")!];
expect(isTrustedProxy("172.16.1.1", cidrs)).toBe(true);
});
it("returns false when no CIDR matches", { timeout: 5000 }, () => {
const cidrs = [parseCidr("10.0.0.0/8")!];
expect(isTrustedProxy("8.8.8.8", cidrs)).toBe(false);
});
});
describe("resolver — X-Forwarded-Host trust", () => {
it("trusted proxy honours forwarded host", { timeout: 5000 }, async () => {
const r = createSubdomainTenantResolver({
baseDomain: "api.example.com",
trustForwardedHost: true,
trustedProxyCidrs: ["10.0.0.0/8"],
});
const req = reqWith(
{
host: "internal.lb",
"x-forwarded-host": "acme.api.example.com",
},
{ remoteAddress: "10.4.7.2" }
);
const ctx = await r.resolve(req);
expect(ctx).toEqual({ id: "acme", slug: "acme" });
});
it("untrusted proxy raises FORWARDED_HOST_UNTRUSTED in strict mode", {
timeout: 5000,
}, async () => {
const r = createSubdomainTenantResolver({
baseDomain: "api.example.com",
strict: true,
trustForwardedHost: true,
trustedProxyCidrs: ["10.0.0.0/8"],
});
const req = reqWith(
{
host: "acme.api.example.com",
"x-forwarded-host": "victim.api.example.com",
},
{ remoteAddress: "8.8.8.8" }
);
let thrown: unknown;
try {
await r.resolve(req);
} catch (e) {
thrown = e;
}
expect(thrown).toBeInstanceOf(SubdomainTenantResolverError);
expect((thrown as SubdomainTenantResolverError).code).toBe(
TENANT_SUBDOMAIN_CODES.FORWARDED_HOST_UNTRUSTED
);
});
it("non-strict: untrusted proxy returns null", {
timeout: 5000,
}, async () => {
const r = createSubdomainTenantResolver({
baseDomain: "api.example.com",
trustForwardedHost: true,
trustedProxyCidrs: ["10.0.0.0/8"],
});
const req = reqWith(
{
host: "acme.api.example.com",
"x-forwarded-host": "victim.api.example.com",
},
{ remoteAddress: "8.8.8.8" }
);
expect(await r.resolve(req)).toBeNull();
});
it("trustForwardedHost=false silently drops the header", {
timeout: 5000,
}, async () => {
const r = createSubdomainTenantResolver({
baseDomain: "api.example.com",
strict: true,
});
const req = reqWith(
{
host: "acme.api.example.com",
"x-forwarded-host": "victim.api.example.com",
},
{ remoteAddress: "10.4.7.2" }
);
const ctx = await r.resolve(req);
expect(ctx).toEqual({ id: "acme", slug: "acme" });
});
});