From d87e898a9f591a860d03979e020a063a146c0d69 Mon Sep 17 00:00:00 2001 From: Jake Jarvis Date: Mon, 20 Oct 2025 14:15:23 -0400 Subject: [PATCH] Add additional synonyms for WHOIS normalization (fixes #10) --- src/whois/normalize.test.ts | 34 ++++++++++++++++++++++++++++++++++ src/whois/normalize.ts | 9 +++++++++ 2 files changed, 43 insertions(+) diff --git a/src/whois/normalize.test.ts b/src/whois/normalize.test.ts index a585b58..843898f 100644 --- a/src/whois/normalize.test.ts +++ b/src/whois/normalize.test.ts @@ -105,6 +105,40 @@ Domain expires: 31-Jul-2026 expect(rec.expirationDate).toBe("2026-07-31T00:00:00Z"); }); +test("WHOIS .it uses 'Expire Date' for expiration", () => { + const text = ` +Domain: EXAMPLE.IT +Registrar: Registrar S.p.A. +Expire Date: 2026-01-20 +Last Update: 2025-01-10 +`; + const rec = normalizeWhois("example.it", "it", text, "whois.nic.it"); + expect(rec.expirationDate).toBe("2026-01-20T00:00:00Z"); +}); + +test("WHOIS .cn uses 'Registration Time' and 'Expiration Time'", () => { + const text = ` +Domain Name: example.cn +ROID: 20200102s10001s00000000-cn +Registration Time: 2020-01-02 03:04:05 +Expiration Time: 2031-01-02 03:04:05 +`; + const rec = normalizeWhois("example.cn", "cn", text, "whois.cnnic.cn"); + expect(rec.creationDate).toBe("2020-01-02T03:04:05Z"); + expect(rec.expirationDate).toBe("2031-01-02T03:04:05Z"); +}); + +test("WHOIS with bare 'expires' key is recognized", () => { + const text = ` +domain: example.in.ua +created: 2020-01-02 03:04:05+03 +expires: 2030-01-02 03:04:05+03 +`; + const rec = normalizeWhois("example.in.ua", "in.ua", text, "whois.in.ua"); + expect(Boolean(rec.expirationDate)).toBe(true); + expect(rec.expirationDate).toBe("2030-01-02T00:04:05Z"); +}); + test("Privacy redacted WHOIS normalizes without contacts", () => { const text = ` Domain Name: EXAMPLE.COM diff --git a/src/whois/normalize.ts b/src/whois/normalize.ts index 3867ad2..d68ede6 100644 --- a/src/whois/normalize.ts +++ b/src/whois/normalize.ts @@ -31,12 +31,15 @@ export function normalizeWhois( "domain create date", "created", "registered", + // CNNIC (.cn) + "registration time", // EDUCAUSE (.edu) "domain record activated", ]); const updatedDate = anyValue(map, [ "updated date", "last updated", + "last update", "last modified", "modified", // EDUCAUSE (.edu) @@ -47,6 +50,10 @@ export function normalizeWhois( "registry expiration date", "expiry date", "expiration date", + // ccTLDs like .it use "Expire Date" + "expire date", + // CNNIC (.cn) + "expiration time", // Registrar-side synonyms commonly seen after referrals "registrar registration expiration date", "registrar registration expiry date", @@ -56,6 +63,8 @@ export function normalizeWhois( "domain expires", "paid-till", "expires on", + // Some registries use a bare "expires" key + "expires", "renewal date", ]);