/** * The deceptive reading of `label`, or null when it is not pretending to be an * ASCII name. Split from {@link confusableHost} so the rule is testable on a * bare label, without an URL around it. * * The two tiers are evidential strength, not attack class: * * WARNING — the WHOLE label skeletons to ASCII, so it reads as an ASCII name * outright, and no glyph in it survives as evidence of a real foreign word. * NOTE — the label carries a cross-script confusable but keeps an unmapped * glyph, so it does not read as any ASCII name. That is the "unmapped glyph * spliced in to suppress the strong rule" shape, and it is also what an ordinary * word with one look-alike letter produces, so the evidence is thin and the * finding stays quiet. * @param {string} label a single DNS label, already decoded from punycode * @returns {string | null} the label's severity, or null when it is not pretending */ export function confusableLabel(label: string): string | null; /** * The loudest confusable finding among `url`'s DNS labels, or null. * * `new URL()` is the only host parser here — hand-splitting an authority would * get userinfo, ports and IPv6 literals wrong — but it hands back the IDNA * A-label form (`xn--pple-43d.com`), which is the punycode, not the deception. * `domainToUnicode` reverses that, so a URL written either way reaches the rule * in the same shape. Both are `node:url` built-ins, so this costs no dependency. * * Fails OPEN on a URL the parser rejects: an unparseable authority is not * evidence of a disguise, and this repo weighs a false flag as the worse failure * (see the precision doctrine in CLAUDE.md). The parser runs IDNA ToASCII itself * and rejects undecodable punycode (`xn--a.com`) there, so `domainToUnicode` * below is only ever handed a host it can decode. * @param {string} url * @returns {{ severity: string, host: string, ascii: string, reads: string } | null} */ export function confusableHost(url: string): { severity: string; host: string; ascii: string; reads: string; } | null; /** * Operator-facing description of a confusable host: the deceptive form, the * punycode a resolver actually sees, and the ASCII name it reads as. All three, * because each answers a different question — what was written, what will be * fetched, and what the reader thought they saw. * @param {{ host: string, ascii: string, reads: string }} found * @returns {string} */ export function describeConfusableHost({ host, ascii, reads }: { host: string; ascii: string; reads: string; }): string;