mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-09-23 01:25: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:
@@ -0,0 +1,40 @@
|
||||
import type { DomainRecord } from "../types";
|
||||
|
||||
// Real WHOIS records are much longer; throttle notices and error pages are short.
|
||||
const MAX_THROTTLE_TEXT_LENGTH = 2048;
|
||||
|
||||
const THROTTLE_PATTERNS: RegExp[] = [
|
||||
/rate[\s-]?limit/i,
|
||||
/limit\s+exceeded/i,
|
||||
/quota\s+exceeded/i,
|
||||
/exceeded\s+.{0,40}(quer|limit|request|connection)/i,
|
||||
/too\s+many\s+(quer|request|connection)/i,
|
||||
/try\s+again\s+(later|in)/i,
|
||||
/access\s+(denied|limit)/i,
|
||||
/\b(blocked|blacklisted|banned)\b/i,
|
||||
/^\s*<(!doctype|html)\b/i,
|
||||
];
|
||||
|
||||
/**
|
||||
* Heuristic: does this WHOIS response look like a throttle notice or error page rather than
|
||||
* a domain record? Only short responses qualify, so a real record that mentions "rate limit"
|
||||
* in a remark is not rejected.
|
||||
*/
|
||||
export function detectWhoisThrottle(text: string | undefined): boolean {
|
||||
if (!text) return false;
|
||||
if (text.length > MAX_THROTTLE_TEXT_LENGTH) return false;
|
||||
return THROTTLE_PATTERNS.some((re) => re.test(text));
|
||||
}
|
||||
|
||||
/** True when a normalized WHOIS record carries none of the fields a real registration would. */
|
||||
export function looksEmptyWhois(record: DomainRecord): boolean {
|
||||
return !(
|
||||
record.registrar ||
|
||||
record.creationDate ||
|
||||
record.updatedDate ||
|
||||
record.expirationDate ||
|
||||
record.nameservers?.length ||
|
||||
record.statuses?.length ||
|
||||
record.contacts?.length
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user