fix(whois): improve type safety and harden WHOIS parsing for missing fields

This commit is contained in:
2026-05-01 16:20:47 +00:00
parent dc3288cc51
commit c7d2b13c8b
11 changed files with 3637 additions and 4135 deletions
+43 -20
View File
@@ -37,6 +37,13 @@ export interface IWhoisInfo {
expiryDate: string;
}
export interface IAdditionalWhoisData {
httpStatus: number;
httpsStatus: number;
httpHeaders: unknown;
httpsHeaders: unknown;
}
export class SmartWhois {
/**
* can be used to prepare an input for the whois command
@@ -50,15 +57,15 @@ export class SmartWhois {
return {
fullUrl: smarturlInstance.toString(),
fullDomain: smarturlInstance.hostname,
domain: tldtsData.domain,
publicSuffix: tldtsData.publicSuffix,
subdomain: tldtsData.subdomain,
domainWithoutSuffix: tldtsData.domainWithoutSuffix,
isIcann: tldtsData.isIcann,
}
domain: tldtsData.domain ?? '',
publicSuffix: tldtsData.publicSuffix ?? '',
subdomain: tldtsData.subdomain ?? '',
domainWithoutSuffix: tldtsData.domainWithoutSuffix ?? '',
isIcann: tldtsData.isIcann ?? undefined,
};
}
public async getAdditionalWhoisDataForDomain(domainArg: string) {
public async getAdditionalWhoisDataForDomain(domainArg: string): Promise<IAdditionalWhoisData> {
if (domainArg.includes('//')) {
const parsedUrlResult = await this.getDomainDelegation(domainArg);
domainArg = parsedUrlResult.fullDomain;
@@ -79,11 +86,9 @@ export class SmartWhois {
httpsStatus: httpsResult.status,
httpHeaders: httpResult.headers,
httpsHeaders: httpsResult.headers,
}
};
}
public async getWhoisForDomain(domainArg: string): Promise<IWhoisInfo> {
if (domainArg.includes('//')) {
const parsedUrlResult = await this.getDomainDelegation(domainArg);
@@ -91,21 +96,39 @@ export class SmartWhois {
}
const whoisInfo = await plugins.whoiser.domain(domainArg);
const whoisServers = Object.keys(whoisInfo);
const selectedWhoisInfo: any = whoisInfo[whoisServers[0]];
const registrar = selectedWhoisInfo.Registrar;
const registrarUrl = selectedWhoisInfo['Registrar URL'];
const createdDate = selectedWhoisInfo['Created Date'];
const updatedDate = selectedWhoisInfo['Updated Date'];
const expiryDate = selectedWhoisInfo['Expiry Date'];
const selectedWhoisInfo = whoisInfo[whoisServers[0]] as Record<string, unknown>;
const getStringField = (fieldName: string): string => {
const fieldValue = selectedWhoisInfo[fieldName];
return typeof fieldValue === 'string' ? fieldValue : '';
};
const registrar = getStringField('Registrar');
const registrarUrl = getStringField('Registrar URL');
const createdDate = getStringField('Created Date');
const updatedDate = getStringField('Updated Date');
const expiryDate = getStringField('Expiry Date');
const domainStatusRaw = selectedWhoisInfo['Domain Status'];
const domainStatusSource = Array.isArray(domainStatusRaw)
? domainStatusRaw
: typeof domainStatusRaw === 'string'
? [domainStatusRaw]
: [];
const domainStatus = domainStatusSource.map((statusArg) => String(statusArg).split(' ')[0]) as IWhoisInfo['domainStatus'];
const tldtsData = plugins.tldts.parse(domainArg);
return {
...tldtsData,
domain: tldtsData.domain ?? '',
domainWithoutSuffix: tldtsData.domainWithoutSuffix ?? '',
hostname: tldtsData.hostname ?? '',
isIcann: tldtsData.isIcann ?? false,
isIp: tldtsData.isIp ?? false,
isPrivate: tldtsData.isPrivate ?? false,
publicSuffix: tldtsData.publicSuffix ?? '',
subdomain: tldtsData.subdomain ?? '',
isRegistered: true,
domainStatus: selectedWhoisInfo['Domain Status'].map((statusArg: string) => statusArg.split(' ')[0]),
dnsSec: selectedWhoisInfo['DNSSEC'],
originalWhoisInfo: whoisInfo,
domainStatus,
dnsSec: getStringField('DNSSEC') as IWhoisInfo['dnsSec'],
originalWhoisInfo: whoisInfo as Record<string, unknown>,
whoisServers,
registrar,
registrarUrl,