mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-10-26 08:26:22 -04:00
fix: follow WHOIS referrals in indented replies
Verisign indents every line of its WHOIS output, so the referral patterns (anchored at column 0) never matched and .com/.net lookups stopped at the thin registry record without registrant data. Allow leading whitespace and keep the value on the same line so a blank field no longer borrows the next line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+75
-3
No files matched your search
@@ -129,12 +129,14 @@ export async function ianaWhoisServerForTld(
|
||||
|
||||
/**
|
||||
* Extract registrar referral WHOIS server from a WHOIS response, if present.
|
||||
* Labels may be indented (Verisign indents every line), and a blank value never borrows the
|
||||
* next line.
|
||||
*/
|
||||
export function extractWhoisReferral(text: string): string | undefined {
|
||||
const patterns = [
|
||||
/^Registrar WHOIS Server:\s*(.+)$/im,
|
||||
/^Whois Server:\s*(.+)$/im,
|
||||
/^ReferralServer:\s*whois:\/\/(.+)$/im,
|
||||
/^[ \t]*Registrar WHOIS Server:[ \t]*(\S.*)$/im,
|
||||
/^[ \t]*Whois Server:[ \t]*(\S.*)$/im,
|
||||
/^[ \t]*ReferralServer:[ \t]*whois:\/\/(\S.*)$/im,
|
||||
];
|
||||
for (const re of patterns) {
|
||||
const m = text.match(re);
|
||||
|
||||
@@ -17,8 +17,58 @@ vi.mock("./client.js", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
import { extractWhoisReferral } from "./discovery";
|
||||
import { collectWhoisReferralChain } from "./referral";
|
||||
|
||||
// Excerpt of Verisign's reply for google.com: every line is indented and CRLF-terminated
|
||||
const VERISIGN_GOOGLE_COM = [
|
||||
" Domain Name: GOOGLE.COM",
|
||||
" Registry Domain ID: 2138514_DOMAIN_COM-VRSN",
|
||||
" Registrar WHOIS Server: whois.markmonitor.com",
|
||||
" Registrar URL: http://www.markmonitor.com",
|
||||
" Creation Date: 1997-09-15T04:00:00Z",
|
||||
" Registry Expiry Date: 2028-09-14T04:00:00Z",
|
||||
" Registrar: MarkMonitor Inc.",
|
||||
" Name Server: NS1.GOOGLE.COM",
|
||||
">>> Last update of whois database: 2026-10-03T18:28:44Z <<<",
|
||||
"",
|
||||
].join("\r\n");
|
||||
|
||||
// Excerpt of MarkMonitor's registrar reply for the same domain
|
||||
const MARKMONITOR_GOOGLE_COM = [
|
||||
"Domain Name: google.com",
|
||||
"Registrar WHOIS Server: whois.markmonitor.com",
|
||||
"Creation Date: 1997-09-15T07:00:00+0000",
|
||||
"Registrar Registration Expiration Date: 2028-09-13T07:00:00+0000",
|
||||
"Registrar: MarkMonitor, Inc.",
|
||||
"Registrant Organization: Google LLC",
|
||||
"Registrant Country: US",
|
||||
"Name Server: ns1.google.com",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
describe("extractWhoisReferral", () => {
|
||||
it("reads an indented, CRLF-terminated referral (Verisign)", () => {
|
||||
expect(extractWhoisReferral(VERISIGN_GOOGLE_COM)).toBe("whois.markmonitor.com");
|
||||
});
|
||||
|
||||
it("does not borrow the next line when the referral field is blank", () => {
|
||||
const text = " Registrar WHOIS Server: \r\n Registrar URL: http://registrar.test\r\n";
|
||||
expect(extractWhoisReferral(text)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls through a blank field to a later pattern", () => {
|
||||
const text = "Registrar WHOIS Server:\nWhois Server: whois.registrar.test\n";
|
||||
expect(extractWhoisReferral(text)).toBe("whois.registrar.test");
|
||||
});
|
||||
|
||||
it("reads an indented ARIN-style ReferralServer", () => {
|
||||
expect(extractWhoisReferral(" ReferralServer: whois://whois.ripe.net\n")).toBe(
|
||||
"whois.ripe.net",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
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", {
|
||||
@@ -49,6 +99,26 @@ describe("WHOIS referral safety", () => {
|
||||
expect(warnings[0]).toMatch(/unsafe host/);
|
||||
});
|
||||
|
||||
it("follows the registrar referral in Verisign's indented reply", async () => {
|
||||
const { whoisQuery } = await import("./client.js");
|
||||
const mocked = vi.mocked(whoisQuery);
|
||||
mocked.mockClear();
|
||||
mocked.mockImplementation(async (server: string) => ({
|
||||
serverQueried: server,
|
||||
text: server === "whois.verisign-grs.com" ? VERISIGN_GOOGLE_COM : MARKMONITOR_GOOGLE_COM,
|
||||
}));
|
||||
const { results, warnings } = await collectWhoisReferralChain(
|
||||
"whois.verisign-grs.com",
|
||||
"google.com",
|
||||
{ followWhoisReferral: true },
|
||||
);
|
||||
expect(results.map((r) => r.serverQueried)).toEqual([
|
||||
"whois.verisign-grs.com",
|
||||
"whois.markmonitor.com",
|
||||
]);
|
||||
expect(warnings).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps the registry record when the registrar throttles", async () => {
|
||||
const { whoisQuery } = await import("./client.js");
|
||||
vi.mocked(whoisQuery).mockImplementation(async (server: string) => ({
|
||||
|
||||
Reference in new issue
Block a user