Skip to content

Commit 97b7317

Browse files
committed
fix: internalize xml parsing
1 parent 7a2428e commit 97b7317

4 files changed

Lines changed: 357 additions & 90 deletions

File tree

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@
204204
"detox"
205205
],
206206
"dependencies": {
207-
"fast-xml-parser": "^5.7.2",
208207
"pngjs": "^7.0.0",
209208
"yaml": "^2.9.0"
210209
},

pnpm-lock.yaml

Lines changed: 0 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import assert from 'node:assert/strict';
2+
import { test } from 'vitest';
3+
4+
import { parseXmlDocumentSync } from '../xml.ts';
5+
6+
test('parseXmlDocumentSync preserves ordered nodes with attributes and decoded text', () => {
7+
const nodes = parseXmlDocumentSync(
8+
[
9+
'<?xml version="1.0" encoding="UTF-8"?>',
10+
'<plist version="1.0">',
11+
'<dict>',
12+
'<key>CFBundleDisplayName</key>',
13+
'<string escaped="&quot;yes&quot;">Example &amp; App</string>',
14+
'<empty enabled="true"/>',
15+
'</dict>',
16+
'</plist>',
17+
].join(''),
18+
);
19+
20+
assert.deepEqual(nodes, [
21+
{
22+
name: 'plist',
23+
attributes: { version: '1.0' },
24+
text: null,
25+
children: [
26+
{
27+
name: 'dict',
28+
attributes: {},
29+
text: null,
30+
children: [
31+
{ name: 'key', attributes: {}, text: 'CFBundleDisplayName', children: [] },
32+
{
33+
name: 'string',
34+
attributes: { escaped: '"yes"' },
35+
text: 'Example & App',
36+
children: [],
37+
},
38+
{ name: 'empty', attributes: { enabled: 'true' }, text: null, children: [] },
39+
],
40+
},
41+
],
42+
},
43+
]);
44+
});
45+
46+
test('parseXmlDocumentSync reads cdata text and skips declarations', () => {
47+
const nodes = parseXmlDocumentSync(
48+
[
49+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
50+
'<root>',
51+
'<!-- ignored -->',
52+
'<value><![CDATA[ <raw>&text</raw> ]]></value>',
53+
'</root>',
54+
].join(''),
55+
);
56+
57+
assert.equal(nodes[0]?.children[0]?.text, '<raw>&text</raw>');
58+
});
59+
60+
test('parseXmlDocumentSync rejects mismatched closing tags', () => {
61+
assert.throws(() => parseXmlDocumentSync('<root><child></root>'), /Expected <\/child>/);
62+
});
63+
64+
test('parseXmlDocumentSync does not expand custom doctype entities', () => {
65+
const nodes = parseXmlDocumentSync(
66+
'<!DOCTYPE root [<!ENTITY secret "expanded">]><root>&secret;</root>',
67+
);
68+
69+
assert.equal(nodes[0]?.text, '&secret;');
70+
});
71+
72+
test('parseXmlDocumentSync rejects unsafe attribute names', () => {
73+
assert.throws(
74+
() => parseXmlDocumentSync('<root __proto__="polluted"/>'),
75+
/Unsupported XML attribute name "__proto__"/,
76+
);
77+
});
78+
79+
test('parseXmlDocumentSync rejects excessive nesting depth', () => {
80+
const xml = `${'<node>'.repeat(257)}${'</node>'.repeat(257)}`;
81+
82+
assert.throws(() => parseXmlDocumentSync(xml), /Maximum XML nesting depth/);
83+
});
84+
85+
test('parseXmlDocumentSync rejects documents above the configured size limit', () => {
86+
assert.throws(
87+
() => parseXmlDocumentSync('<root>oversized</root>', { maxDocumentChars: 10 }),
88+
/XML document exceeds maximum supported size of 10 characters/,
89+
);
90+
});

0 commit comments

Comments
 (0)