Skip to content

Commit 60835a4

Browse files
committed
support maxNestedTags
1 parent f55657c commit 60835a4

5 files changed

Lines changed: 95 additions & 3 deletions

File tree

docs/v4/2.XMLparseOptions.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,62 @@ Output
477477
}
478478
```
479479

480+
## maxNestedTags
481+
Though it is not recommended to use deeply nested XML, but if you are using it then you can set `maxNestedTags` option to limit the nesting depth.
482+
483+
Eg
484+
```js
485+
const xmlDataStr = `
486+
<root>
487+
<a>
488+
<b>
489+
<c>
490+
<d>
491+
<e>
492+
<f>
493+
<g>
494+
<h>
495+
<i>
496+
<j>
497+
<k>
498+
<l>
499+
<m>
500+
<n>
501+
<o>
502+
<p>
503+
<q>
504+
<r>
505+
<s/>
506+
</r>
507+
</q>
508+
</p>
509+
</o>
510+
</n>
511+
</m>
512+
</l>
513+
</k>
514+
</j>
515+
</i>
516+
</h>
517+
</g>
518+
</f>
519+
</e>
520+
</d>
521+
</c>
522+
</b>
523+
</a>
524+
</root>`;
525+
526+
const options = {
527+
maxNestedTags: 10 //default is 100
528+
};
529+
const parser = new XMLParser(options);
530+
const output = parser.parse(xmlDataStr);
531+
```
532+
Output
533+
```bash
534+
Error: Maximum nested tags exceeded
535+
```
480536

481537
## numberParseOptions
482538
FXP uses [strnum](https://github.com/NaturalIntelligence/strnum) library to parse string into numbers. This property allows you to set configuration for strnum package.

lib/fxp.d.cts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,13 @@ type X2jOptions = {
278278
* metadata about each the node in the XML file.
279279
*/
280280
captureMetaData?: boolean;
281+
282+
/**
283+
* Maximum number of nested tags
284+
*
285+
* Defaults to `100`
286+
*/
287+
maxNestedTags?: number;
281288
};
282289

283290
type strnumOptions = {

spec/nfr_spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,21 @@ describe("XMLParser", function () {
2323
// console.log(JSON.stringify(result, null,4));
2424
expect(result).toEqual(expected);
2525
});
26+
27+
it("should throw error for deeply nested XML", function () {
28+
29+
const depth = 102;
30+
const xmlData = '<a>'.repeat(depth) + 'x' + '</a>'.repeat(depth);
31+
32+
const parser = new XMLParser();
33+
expect(() => parser.parse(xmlData)).toThrowError("Maximum nested tags exceeded");
34+
});
35+
it("should not throw error when max depth is not reached", function () {
36+
37+
const depth = 100;
38+
const xmlData = '<a>'.repeat(depth) + 'x' + '</a>'.repeat(depth);
39+
40+
const parser = new XMLParser({ maxNestedTags: 101 });
41+
parser.parse(xmlData);
42+
});
2643
});

src/fxp.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,17 @@ export type X2jOptions = {
278278
* metadata about each the node in the XML file.
279279
*/
280280
captureMetaData?: boolean;
281+
282+
/**
283+
* Maximum number of nested tags
284+
*
285+
* Defaults to `100`
286+
*/
287+
maxNestedTags?: number;
281288
};
282289

290+
291+
283292
export type strnumOptions = {
284293
hex: boolean;
285294
leadingZeros: boolean,

src/xmlparser/OrderedObjParser.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default class OrderedObjParser {
1919
this.options = options;
2020
this.currentNode = null;
2121
this.tagsNodeStack = [];
22-
this.docTypeEntities = {};
22+
this.docTypeEntities = Object.create(null);
2323
this.lastEntities = {
2424
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
2525
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
@@ -150,7 +150,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
150150

151151
const matches = getAllMatches(attrStr, attrsRegx);
152152
const len = matches.length; //don't make it inline
153-
const attrs = {};
153+
const attrs = Object.create(null);
154154
for (let i = 0; i < len; i++) {
155155
const attrName = this.resolveNameSpace(matches[i][1]);
156156
if (this.ignoreAttributesFn(attrName, jPath)) {
@@ -192,7 +192,7 @@ function buildAttributesMap(attrStr, jPath, tagName) {
192192
return;
193193
}
194194
if (this.options.attributesGroupName) {
195-
const attrCollection = {};
195+
const attrCollection = Object.create(null);
196196
attrCollection[this.options.attributesGroupName] = attrs;
197197
return attrCollection;
198198
}
@@ -409,6 +409,9 @@ const parseXml = function (xmlData) {
409409
//opening tag
410410
else {
411411
const childNode = new xmlNode(tagName);
412+
if (this.tagsNodeStack.length > this.options.maxNestedTags) {
413+
throw new Error("Maximum nested tags exceeded");
414+
}
412415
this.tagsNodeStack.push(currentNode);
413416

414417
if (tagName !== tagExp && attrExpPresent) {

0 commit comments

Comments
 (0)