37 lines
1.9 KiB
JavaScript
37 lines
1.9 KiB
JavaScript
// @ts-check
|
|
// Minimal HTML entity decoder shared by the scraping providers whose sources
|
|
// return raw HTML (as opposed to a JSON API). Handles named entities (&,
|
|
// <, …) and numeric entities (ü / ü).
|
|
//
|
|
// Previously duplicated verbatim across deutschebahn.mjs and hecklerkoch.mjs
|
|
// (CodeRabbit finding on #1555) — same drift risk flagged separately on
|
|
// successfactors.mjs/dassault.mjs/softgarden.mjs/rheinmetall.mjs (#1639),
|
|
// where a numeric-entity range guard drifted out of sync between copies:
|
|
// checking only Number.isFinite still lets String.fromCodePoint throw a
|
|
// RangeError for a code point above 0x10FFFF (e.g. `�`), crashing
|
|
// the entire parse for a single malformed/adversarial entity. Centralized
|
|
// here so the guard can't diverge again.
|
|
//
|
|
// The hex/decimal alternatives are matched separately (not "#x?[0-9a-fA-F]+")
|
|
// so a decimal entity can never absorb trailing hex letters — "a2;" no
|
|
// longer silently parses as codepoint 1 and drops "a2"; it just fails to
|
|
// match and passes through untouched, same as any other malformed entity.
|
|
const NAMED_ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: ' ' };
|
|
|
|
/** @param {string} s */
|
|
export function decodeEntities(s) {
|
|
return s.replace(/&(#[xX][0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g, (m, body) => {
|
|
if (body[0] === '#') {
|
|
const isHex = body[1] === 'x' || body[1] === 'X';
|
|
const code = parseInt(body.slice(isHex ? 2 : 1), isHex ? 16 : 10);
|
|
// A lone surrogate half (0xD800-0xDFFF) is a valid codepoint per spec —
|
|
// fromCodePoint won't throw for it — but it's not a valid Unicode scalar
|
|
// value, so we still reject it defensively rather than emit an
|
|
// ill-formed string.
|
|
const valid = Number.isFinite(code) && code >= 0 && code <= 0x10ffff && !(code >= 0xd800 && code <= 0xdfff);
|
|
return valid ? String.fromCodePoint(code) : m;
|
|
}
|
|
return NAMED_ENTITIES[body.toLowerCase()] ?? m;
|
|
});
|
|
}
|