mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-10-26 08:26:22 -04:00
fix: prefer the registry's dates when merging WHOIS referrals
Merging a registry and a registrar reply kept the earliest creation and the latest updated/expiration dates across both, while RDAP reports the registry's. The same domain therefore differed by source: google.com's updated date was 2019 over RDAP but MarkMonitor's 2024 over WHOIS, and amnic.net's creation came from a registrar printing local time without a zone. Keep the registry's dates and fill only what it lacks from the registrar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+27
-17
No files matched your search
@@ -57,3 +57,23 @@ test("mergeWhoisRecords keeps the registrar's fuller registrant over the registr
|
||||
{ type: "registrant", name: "Jane Doe", organization: "Acme Inc", email: "jane@acme.example" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("mergeWhoisRecords keeps the registry's dates and only fills gaps from the registrar", () => {
|
||||
const registry = normalizeWhois(
|
||||
"google.com",
|
||||
"com",
|
||||
"Domain Name: GOOGLE.COM\nUpdated Date: 2019-09-09T15:39:04Z\nCreation Date: 1997-09-15T04:00:00Z\n",
|
||||
"whois.verisign-grs.com",
|
||||
);
|
||||
const registrar = normalizeWhois(
|
||||
"google.com",
|
||||
"com",
|
||||
"Domain Name: google.com\nUpdated Date: 2024-08-02T02:17:33+0000\nCreation Date: 1997-09-15T07:00:00+0000\nRegistrar Registration Expiration Date: 2028-09-13T07:00:00+0000\n",
|
||||
"whois.markmonitor.com",
|
||||
);
|
||||
const merged = mergeWhoisRecords(registry, [registrar]);
|
||||
expect(merged.updatedDate).toBe("2019-09-09T15:39:04Z");
|
||||
expect(merged.creationDate).toBe("1997-09-15T04:00:00Z");
|
||||
// The registry gave no expiration, so the registrar's fills it
|
||||
expect(merged.expirationDate).toBe("2028-09-13T07:00:00Z");
|
||||
});
|
||||
+7
-17
@@ -15,7 +15,7 @@ function dedupeStatuses(a?: DomainRecord["statuses"], b?: DomainRecord["statuses
|
||||
return out.length ? out : undefined;
|
||||
}
|
||||
|
||||
/** Conservative merge: start with base; fill missing scalars; union arrays; prefer more informative dates. */
|
||||
/** Conservative merge: start with the registry's record (base); fill missing scalars and dates; union arrays. */
|
||||
export function mergeWhoisRecords(base: DomainRecord, others: DomainRecord[]): DomainRecord {
|
||||
const merged: DomainRecord = { ...base };
|
||||
for (const cur of others) {
|
||||
@@ -24,10 +24,12 @@ export function mergeWhoisRecords(base: DomainRecord, others: DomainRecord[]): D
|
||||
merged.registrar = merged.registrar ?? cur.registrar;
|
||||
merged.reseller = merged.reseller ?? cur.reseller;
|
||||
merged.statuses = dedupeStatuses(merged.statuses, cur.statuses);
|
||||
// Dates: prefer earliest creation, latest updated/expiration when available
|
||||
merged.creationDate = preferEarliestIso(merged.creationDate, cur.creationDate);
|
||||
merged.updatedDate = preferLatestIso(merged.updatedDate, cur.updatedDate);
|
||||
merged.expirationDate = preferLatestIso(merged.expirationDate, cur.expirationDate);
|
||||
// Dates: the registry's (the base record's) are authoritative, as in RDAP; a registrar's
|
||||
// reply only fills in what the registry lacks. Its own "Updated Date" tracks its records, and
|
||||
// some registrars print local times without a zone.
|
||||
merged.creationDate = merged.creationDate ?? cur.creationDate;
|
||||
merged.updatedDate = merged.updatedDate ?? cur.updatedDate;
|
||||
merged.expirationDate = merged.expirationDate ?? cur.expirationDate;
|
||||
merged.deletionDate = merged.deletionDate ?? cur.deletionDate;
|
||||
merged.transferLock = Boolean(merged.transferLock || cur.transferLock);
|
||||
merged.dnssec = merged.dnssec ?? cur.dnssec;
|
||||
@@ -41,15 +43,3 @@ export function mergeWhoisRecords(base: DomainRecord, others: DomainRecord[]): D
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
function preferEarliestIso(a?: string, b?: string): string | undefined {
|
||||
if (!a) return b;
|
||||
if (!b) return a;
|
||||
return new Date(a) <= new Date(b) ? a : b;
|
||||
}
|
||||
|
||||
function preferLatestIso(a?: string, b?: string): string | undefined {
|
||||
if (!a) return b;
|
||||
if (!b) return a;
|
||||
return new Date(a) >= new Date(b) ? a : b;
|
||||
}
|
||||
Reference in new issue
Block a user