fix(plist): keep character references beyond Unicode literal
Reserving the __proto__ placeholder decodes the whole document, so <![CDATA[�]]> or a comment with an out-of-range reference threw a RangeError, and every XML plist reader failed on a valid document. References beyond U+10FFFF now stay as written. The XML decoder still rejects them outside comments and CDATA. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6729ee7636
commit
5afd8a850b
|
|
@ -49,6 +49,16 @@ it("keeps an entity-encoded key that decodes to the placeholder", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("keeps entity-like text beyond Unicode in comments and CDATA", () => {
|
||||
const text = plist(
|
||||
"<!-- � --><dict><key>raw</key><string><![CDATA[�]]></string><key>__proto__</key><string>x</string></dict>",
|
||||
);
|
||||
expect(parseXmlPropertyList(text)).toEqual({
|
||||
value: { raw: "�" },
|
||||
omittedPrototypeKeys: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it("omits own __proto__ entries that JSON decoding creates", () => {
|
||||
const decoded: unknown = JSON.parse(
|
||||
'{"__proto__":{"x":1},"a":[{"__proto__":2,"b":3}]}',
|
||||
|
|
|
|||
|
|
@ -56,13 +56,23 @@ const XML_ENTITIES: Readonly<Record<string, string>> = {
|
|||
quot: '"',
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand XML character and predefined entity references. A reference beyond
|
||||
* Unicode stays as written: comments and CDATA may hold such text literally,
|
||||
* and the XML decoder rejects it anywhere else.
|
||||
*/
|
||||
const decodeXmlText = (text: string): string =>
|
||||
text.replace(
|
||||
/&(?:#x([\da-f]+)|#(\d+)|([a-z]+));/giu,
|
||||
(entity, hex?: string, decimal?: string, name?: string) => {
|
||||
if (hex !== undefined) return String.fromCodePoint(parseInt(hex, 16));
|
||||
if (decimal !== undefined)
|
||||
return String.fromCodePoint(parseInt(decimal, 10));
|
||||
const codePoint =
|
||||
hex !== undefined
|
||||
? parseInt(hex, 16)
|
||||
: decimal !== undefined
|
||||
? parseInt(decimal, 10)
|
||||
: undefined;
|
||||
if (codePoint !== undefined)
|
||||
return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : entity;
|
||||
return (name === undefined ? undefined : XML_ENTITIES[name]) ?? entity;
|
||||
},
|
||||
);
|
||||
|
|
@ -77,6 +87,7 @@ export const parseXmlPropertyList = (
|
|||
): PropertyListWithoutPrototypeKeys => {
|
||||
// The decoded text holds every key the decoder can produce, including
|
||||
// entity-encoded spellings of the placeholder, so no source key aliases it.
|
||||
// Decoding comment or CDATA text too can only lengthen the placeholder.
|
||||
const decoded = decodeXmlText(text);
|
||||
let placeholder = "__rea_prototype_key__";
|
||||
while (decoded.includes(placeholder)) placeholder = `_${placeholder}`;
|
||||
|
|
|
|||
Loading…
Reference in New Issue