Skip to content

Commit c3ffbab

Browse files
committed
support strictReservedNames
1 parent c692040 commit c3ffbab

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

docs/v4/2.XMLparseOptions.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const parser = new XMLParser(options);
1111
let jsonObj = parser.parse(xmlDataStr);
1212
```
1313

14+
> ⚠️ Security Note: If accepting untrusted JSON input, sanitize/validate property names before passing to the builder. Reserved property names like your configured commentPropertyName, cdataPropertyName , or attribute name after attributeNamePrefix should be blocked or escaped in untrusted input.
15+
1416
Let's understand each option in detail with necessary examples
1517

1618
## allowBooleanAttributes
@@ -801,6 +803,7 @@ Output
801803
}
802804
```
803805

806+
804807
## stopNodes
805808

806809
At particular point, if you don't want to parse a tag and it's nested tags then you can set their path in `stopNodes`.
@@ -857,6 +860,34 @@ nested stop notes are also not allowed
857860
<stop> invalid </stop>
858861
</stop>
859862
```
863+
## strictReservedNames
864+
865+
By default, FXP will throw an error if it encounters a tag or attribute name that matches a reserved property name set by user or default(e.g., `__comment`, `__cdata`).
866+
867+
Setting `strictReservedNames: false` allows the parser to continue processing even if such names are found, treating them as regular tag or attribute names.
868+
869+
Example:
870+
```js
871+
const xmlDataStr = `
872+
<root>
873+
<__comment>1</__comment>
874+
</root>`;
875+
876+
const options = {
877+
commentPropName: "__comment",
878+
strictReservedNames: false
879+
};
880+
const parser = new XMLParser(options);
881+
const output = parser.parse(xmlDataStr);
882+
```
883+
Output:
884+
```json
885+
{
886+
"root": {
887+
"__comment": 1
888+
}
889+
}
890+
```
860891

861892
## tagValueProcessor
862893
With `tagValueProcessor` you can control how and which tag value should be parsed.

lib/fxp.d.cts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ type X2jOptions = {
285285
* Defaults to `100`
286286
*/
287287
maxNestedTags?: number;
288+
289+
/**
290+
* Whether to strictly validate tag names
291+
*
292+
* Defaults to `true`
293+
*/
294+
strictReservedNames?: boolean;
288295
};
289296

290297
type strnumOptions = {

spec/nfr_spec.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,38 @@ describe("XMLParser", function () {
4040
const parser = new XMLParser({ maxNestedTags: 101 });
4141
parser.parse(xmlData);
4242
});
43+
44+
it("should throw error for tag name with reserved name", function () {
45+
const xmlData = `
46+
<root>
47+
<__comment>1</__comment>
48+
</root>
49+
`;
50+
const parser = new XMLParser({
51+
commentPropName: "__comment"
52+
});
53+
expect(() => parser.parse(xmlData)).toThrowError("Invalid tag name: __comment");
54+
});
55+
56+
57+
it("should not throw error for tag name with reserved name when strictReservedNames is false", function () {
58+
const xmlData = `
59+
<root>
60+
<__comment>1</__comment>
61+
</root>
62+
`;
63+
const parser = new XMLParser({
64+
commentPropName: "__comment",
65+
strictReservedNames: false
66+
});
67+
const expected = {
68+
"root": {
69+
"__comment": 1
70+
}
71+
}
72+
let result = parser.parse(xmlData);
73+
//console.log(JSON.stringify(result, null, 4));
74+
expect(result).toEqual(expected);
75+
});
76+
4377
});

src/fxp.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,13 @@ export type X2jOptions = {
285285
* Defaults to `100`
286286
*/
287287
maxNestedTags?: number;
288+
289+
/**
290+
* Whether to strictly validate tag names
291+
*
292+
* Defaults to `true`
293+
*/
294+
strictReservedNames?: boolean;
288295
};
289296

290297

src/xmlparser/OptionsBuilder.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const defaultOptions = {
3939
// skipEmptyListItem: false
4040
captureMetaData: false,
4141
maxNestedTags: 100,
42+
strictReservedNames: true,
4243
};
4344

4445
/**

src/xmlparser/OrderedObjParser.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
163163
aName = this.options.transformAttributeName(aName);
164164
}
165165
if (aName === "__proto__") aName = "#__proto__";
166+
166167
if (oldVal !== undefined) {
167168
if (this.options.trimValues) {
168169
oldVal = oldVal.trim();
@@ -322,6 +323,13 @@ const parseXml = function (xmlData) {
322323
tagName = newTagName;
323324
}
324325

326+
if (this.options.strictReservedNames &&
327+
(tagName === this.options.commentPropName
328+
|| tagName === this.options.cdataPropName
329+
)) {
330+
throw new Error(`Invalid tag name: ${tagName}`);
331+
}
332+
325333
//save text as child node
326334
if (currentNode && textData) {
327335
if (currentNode.tagname !== '!xml') {

0 commit comments

Comments
 (0)