mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-09-23 02:35:31 -04:00
feat: add SSRF host validation, rate_limited/unparseable error codes, and referral chain warnings
- Add `isSafeWhoisReferralHost` to reject private/loopback/link-local IPs and malformed hostnames before following WHOIS referrals (SSRF guard)
- Detect WHOIS throttle replies in the referral chain: drop the registrar response, keep the registry record, and surface a warning instead of returning bad data
- Add `looksEmptyWhois` guard in the main lookup path: a "registered" record with no parseable fields now fails with `errorCode: "unparseable"` rather than resolving silently
- Map RDAP 429 responses to a structured `rate_limited` error (including `Retry-After` header) so callers can distinguish throttling from generic HTTP errors and the fallback to WHOIS is recorded in `attempts`
- Add `rate_limited` and `unparseable` to `LookupErrorCode`
- Change `collectWhoisReferralChain` to return `{ results, warnings }` instead of a bare array; warnings are merged onto the final `DomainRecord`
- Remove the `followWhoisReferrals` fallback path from `index.ts` (dead code after the chain API stabilised)
This commit is contained in:
@@ -32,7 +32,7 @@ describe("WHOIS referral contradiction handling", () => {
|
||||
});
|
||||
|
||||
it("collects chain and does not append contradictory registrar", async () => {
|
||||
const chain = await collectWhoisReferralChain("whois.nic.io", "raindrop.io", {
|
||||
const { results: chain } = await collectWhoisReferralChain("whois.nic.io", "raindrop.io", {
|
||||
followWhoisReferral: true,
|
||||
maxWhoisReferralHops: 2,
|
||||
});
|
||||
@@ -42,3 +42,37 @@ describe("WHOIS referral contradiction handling", () => {
|
||||
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/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user