mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-09-23 01:25:31 -04:00
- Parse `Retry-After` headers (delay-seconds or HTTP-date) into `retryAfterMs` via a new `parseRetryAfterMs` helper and expose the value on both `LookupAttempt` and `LookupResult` so callers can honour server-requested back-off without parsing the error string - Propagate `retryAfterMs` through `classifyError`, `traced`, `failure`, and the `rdap_unavailable` path so the value surfaces on the top-level result - Add `blocked` to `LookupErrorCode` for WHOIS servers that permanently refuse a client (distinct from `rate_limited`, which is a temporary throttle that may succeed on retry) - Add `blockPrivateAddresses` to `WhoisTransportOptions`; when set, a custom `dns.lookup` shim rejects the connection before it opens if any resolved address is non-public, covering DNS rebinding and hostnames that resolve to private ranges - Expand `isPrivateIp` / `isSafeWhoisReferralHost` to reject IPv4-mapped (`::ffff:7f00:1`), NAT64 (`64:ff9b::`), 6to4 (`2002:7f00::`), Teredo (`2001:0:`), deprecated site-local (`fec0::`), and bracketed IPv6 literals
68 lines
2.7 KiB
TypeScript
68 lines
2.7 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("./client.js", () => ({
|
|
whoisQuery: vi.fn(async (server: string) => {
|
|
if (server === "whois.nic.io") {
|
|
// TLD WHOIS shows a clearly registered domain and a registrar referral
|
|
return {
|
|
serverQueried: server,
|
|
text: `Domain Name: RAINDROP.IO\nCreation Date: 2013-08-20T20:30:16Z\nRegistry Expiry Date: 2027-08-20T20:30:16Z\nRegistrar WHOIS Server: whois.1api.net\nName Server: BEAU.NS.CLOUDFLARE.COM\nName Server: BARBARA.NS.CLOUDFLARE.COM\n`,
|
|
};
|
|
}
|
|
// Registrar WHOIS contradicts with an availability phrase
|
|
return {
|
|
serverQueried: server,
|
|
text: "No match for RAINDROP.IO",
|
|
};
|
|
}),
|
|
}));
|
|
|
|
import { collectWhoisReferralChain } from "./referral";
|
|
|
|
describe("WHOIS referral contradiction handling", () => {
|
|
it("collects chain and does not append contradictory registrar", async () => {
|
|
const { results: chain } = await collectWhoisReferralChain("whois.nic.io", "raindrop.io", {
|
|
followWhoisReferral: true,
|
|
maxWhoisReferralHops: 2,
|
|
});
|
|
expect(Array.isArray(chain)).toBe(true);
|
|
// Mocked registrar is contradictory, so chain should contain only the TLD response
|
|
expect(chain.length).toBe(1);
|
|
expect(chain[0]?.serverQueried).toBe("whois.nic.io");
|
|
});
|
|
});
|
|
|
|
describe("WHOIS referral safety", () => {
|
|
it("does not query an unsafe referral host and reports a warning", async () => {
|
|
const { whoisQuery } = await import("./client.js");
|
|
const mocked = vi.mocked(whoisQuery);
|
|
mocked.mockClear();
|
|
mocked.mockImplementation(async (server: string) => ({
|
|
serverQueried: server,
|
|
text: "Domain Name: EVIL.COM\nCreation Date: 2013-08-20T20:30:16Z\nRegistrar WHOIS Server: 169.254.169.254\n",
|
|
}));
|
|
const { results, warnings } = await collectWhoisReferralChain("whois.nic.io", "evil.com", {
|
|
followWhoisReferral: true,
|
|
});
|
|
expect(results).toHaveLength(1);
|
|
expect(mocked).toHaveBeenCalledTimes(1);
|
|
expect(warnings[0]).toMatch(/unsafe host/);
|
|
});
|
|
|
|
it("keeps the registry record when the registrar throttles", async () => {
|
|
const { whoisQuery } = await import("./client.js");
|
|
vi.mocked(whoisQuery).mockImplementation(async (server: string) => ({
|
|
serverQueried: server,
|
|
text:
|
|
server === "whois.nic.io"
|
|
? "Domain Name: X.IO\nCreation Date: 2013-08-20T20:30:16Z\nRegistrar WHOIS Server: whois.1api.net\n"
|
|
: "WHOIS LIMIT EXCEEDED",
|
|
}));
|
|
const { results, warnings } = await collectWhoisReferralChain("whois.nic.io", "x.io", {
|
|
followWhoisReferral: true,
|
|
});
|
|
expect(results).toHaveLength(1);
|
|
expect(warnings[0]).toMatch(/rate limited/);
|
|
});
|
|
});
|