fast-xml-parser 5.5.5 → 5.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/lib/fxbuilder.min.js +1 -1
- package/lib/fxbuilder.min.js.map +1 -1
- package/lib/fxp.cjs +1 -1
- package/lib/fxp.d.cts +7 -0
- package/lib/fxp.min.js +1 -1
- package/lib/fxp.min.js.map +1 -1
- package/lib/fxparser.min.js +1 -1
- package/lib/fxparser.min.js.map +1 -1
- package/package.json +3 -3
- package/src/fxp.d.ts +7 -0
- package/src/xmlparser/DocTypeReader.js +28 -18
- package/src/xmlparser/OptionsBuilder.js +6 -6
- package/src/xmlparser/OrderedObjParser.js +27 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-parser",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.7",
|
|
4
4
|
"description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
|
|
5
5
|
"main": "./lib/fxp.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
}
|
|
88
88
|
],
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"fast-xml-builder": "^1.1.
|
|
90
|
+
"fast-xml-builder": "^1.1.4",
|
|
91
91
|
"path-expression-matcher": "^1.1.3",
|
|
92
|
-
"strnum": "^2.
|
|
92
|
+
"strnum": "^2.2.0"
|
|
93
93
|
}
|
|
94
94
|
}
|
package/src/fxp.d.ts
CHANGED
|
@@ -516,6 +516,13 @@ export type XmlBuilderOptions = {
|
|
|
516
516
|
|
|
517
517
|
|
|
518
518
|
oneListGroup?: boolean;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Maximum number of nested tags
|
|
522
|
+
*
|
|
523
|
+
* Defaults to `100`
|
|
524
|
+
*/
|
|
525
|
+
maxNestedTags?: number;
|
|
519
526
|
};
|
|
520
527
|
|
|
521
528
|
type ESchema = string | object | Array<string | object>;
|
|
@@ -28,13 +28,14 @@ export default class DocTypeReader {
|
|
|
28
28
|
[entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);
|
|
29
29
|
if (val.indexOf("&") === -1) { //Parameter entities are not supported
|
|
30
30
|
if (this.options.enabled !== false &&
|
|
31
|
-
this.options.maxEntityCount &&
|
|
31
|
+
this.options.maxEntityCount != null &&
|
|
32
32
|
entityCount >= this.options.maxEntityCount) {
|
|
33
33
|
throw new Error(
|
|
34
34
|
`Entity count (${entityCount + 1}) exceeds maximum allowed (${this.options.maxEntityCount})`
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
|
-
const escaped = entityName.replace(/[.\-+*:]/g, '\\.');
|
|
37
|
+
//const escaped = entityName.replace(/[.\-+*:]/g, '\\.');
|
|
38
|
+
const escaped = entityName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
38
39
|
entities[entityName] = {
|
|
39
40
|
regx: RegExp(`&${escaped};`, "g"),
|
|
40
41
|
val: val
|
|
@@ -99,11 +100,12 @@ export default class DocTypeReader {
|
|
|
99
100
|
i = skipWhitespace(xmlData, i);
|
|
100
101
|
|
|
101
102
|
// Read entity name
|
|
102
|
-
|
|
103
|
+
const startIndex = i;
|
|
103
104
|
while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
|
|
104
|
-
entityName += xmlData[i];
|
|
105
105
|
i++;
|
|
106
106
|
}
|
|
107
|
+
let entityName = xmlData.substring(startIndex, i);
|
|
108
|
+
|
|
107
109
|
validateEntityName(entityName);
|
|
108
110
|
|
|
109
111
|
// Skip whitespace after entity name
|
|
@@ -124,7 +126,7 @@ export default class DocTypeReader {
|
|
|
124
126
|
|
|
125
127
|
// Validate entity size
|
|
126
128
|
if (this.options.enabled !== false &&
|
|
127
|
-
this.options.maxEntitySize &&
|
|
129
|
+
this.options.maxEntitySize != null &&
|
|
128
130
|
entityValue.length > this.options.maxEntitySize) {
|
|
129
131
|
throw new Error(
|
|
130
132
|
`Entity "${entityName}" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`
|
|
@@ -140,11 +142,13 @@ export default class DocTypeReader {
|
|
|
140
142
|
i = skipWhitespace(xmlData, i);
|
|
141
143
|
|
|
142
144
|
// Read notation name
|
|
143
|
-
|
|
145
|
+
|
|
146
|
+
const startIndex = i;
|
|
144
147
|
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
145
|
-
notationName += xmlData[i];
|
|
146
148
|
i++;
|
|
147
149
|
}
|
|
150
|
+
let notationName = xmlData.substring(startIndex, i);
|
|
151
|
+
|
|
148
152
|
!this.suppressValidationErr && validateEntityName(notationName);
|
|
149
153
|
|
|
150
154
|
// Skip whitespace after notation name
|
|
@@ -194,10 +198,11 @@ export default class DocTypeReader {
|
|
|
194
198
|
}
|
|
195
199
|
i++;
|
|
196
200
|
|
|
201
|
+
const startIndex = i;
|
|
197
202
|
while (i < xmlData.length && xmlData[i] !== startChar) {
|
|
198
|
-
identifierVal += xmlData[i];
|
|
199
203
|
i++;
|
|
200
204
|
}
|
|
205
|
+
identifierVal = xmlData.substring(startIndex, i);
|
|
201
206
|
|
|
202
207
|
if (xmlData[i] !== startChar) {
|
|
203
208
|
throw new Error(`Unterminated ${type} value`);
|
|
@@ -217,11 +222,11 @@ export default class DocTypeReader {
|
|
|
217
222
|
i = skipWhitespace(xmlData, i);
|
|
218
223
|
|
|
219
224
|
// Read element name
|
|
220
|
-
|
|
225
|
+
const startIndex = i;
|
|
221
226
|
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
222
|
-
elementName += xmlData[i];
|
|
223
227
|
i++;
|
|
224
228
|
}
|
|
229
|
+
let elementName = xmlData.substring(startIndex, i);
|
|
225
230
|
|
|
226
231
|
// Validate element name
|
|
227
232
|
if (!this.suppressValidationErr && !isName(elementName)) {
|
|
@@ -238,10 +243,12 @@ export default class DocTypeReader {
|
|
|
238
243
|
i++; // Move past '('
|
|
239
244
|
|
|
240
245
|
// Read content model
|
|
246
|
+
const startIndex = i;
|
|
241
247
|
while (i < xmlData.length && xmlData[i] !== ")") {
|
|
242
|
-
contentModel += xmlData[i];
|
|
243
248
|
i++;
|
|
244
249
|
}
|
|
250
|
+
contentModel = xmlData.substring(startIndex, i);
|
|
251
|
+
|
|
245
252
|
if (xmlData[i] !== ")") {
|
|
246
253
|
throw new Error("Unterminated content model");
|
|
247
254
|
}
|
|
@@ -262,11 +269,11 @@ export default class DocTypeReader {
|
|
|
262
269
|
i = skipWhitespace(xmlData, i);
|
|
263
270
|
|
|
264
271
|
// Read element name
|
|
265
|
-
let
|
|
272
|
+
let startIndex = i;
|
|
266
273
|
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
267
|
-
elementName += xmlData[i];
|
|
268
274
|
i++;
|
|
269
275
|
}
|
|
276
|
+
let elementName = xmlData.substring(startIndex, i);
|
|
270
277
|
|
|
271
278
|
// Validate element name
|
|
272
279
|
validateEntityName(elementName)
|
|
@@ -275,11 +282,11 @@ export default class DocTypeReader {
|
|
|
275
282
|
i = skipWhitespace(xmlData, i);
|
|
276
283
|
|
|
277
284
|
// Read attribute name
|
|
278
|
-
|
|
285
|
+
startIndex = i;
|
|
279
286
|
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
280
|
-
attributeName += xmlData[i];
|
|
281
287
|
i++;
|
|
282
288
|
}
|
|
289
|
+
let attributeName = xmlData.substring(startIndex, i);
|
|
283
290
|
|
|
284
291
|
// Validate attribute name
|
|
285
292
|
if (!validateEntityName(attributeName)) {
|
|
@@ -307,11 +314,13 @@ export default class DocTypeReader {
|
|
|
307
314
|
// Read the list of allowed notations
|
|
308
315
|
let allowedNotations = [];
|
|
309
316
|
while (i < xmlData.length && xmlData[i] !== ")") {
|
|
310
|
-
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
const startIndex = i;
|
|
311
320
|
while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
|
|
312
|
-
notation += xmlData[i];
|
|
313
321
|
i++;
|
|
314
322
|
}
|
|
323
|
+
let notation = xmlData.substring(startIndex, i);
|
|
315
324
|
|
|
316
325
|
// Validate notation name
|
|
317
326
|
notation = notation.trim();
|
|
@@ -337,10 +346,11 @@ export default class DocTypeReader {
|
|
|
337
346
|
attributeType += " (" + allowedNotations.join("|") + ")";
|
|
338
347
|
} else {
|
|
339
348
|
// Handle simple types (e.g., CDATA, ID, IDREF, etc.)
|
|
349
|
+
const startIndex = i;
|
|
340
350
|
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
341
|
-
attributeType += xmlData[i];
|
|
342
351
|
i++;
|
|
343
352
|
}
|
|
353
|
+
attributeType += xmlData.substring(startIndex, i);
|
|
344
354
|
|
|
345
355
|
// Validate simple attribute type
|
|
346
356
|
const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
|
|
@@ -103,12 +103,12 @@ function normalizeProcessEntities(value) {
|
|
|
103
103
|
// Object config - merge with defaults
|
|
104
104
|
if (typeof value === 'object' && value !== null) {
|
|
105
105
|
return {
|
|
106
|
-
enabled: value.enabled !== false,
|
|
107
|
-
maxEntitySize: value.maxEntitySize ?? 10000,
|
|
108
|
-
maxExpansionDepth: value.maxExpansionDepth ?? 10,
|
|
109
|
-
maxTotalExpansions: value.maxTotalExpansions ?? 1000,
|
|
110
|
-
maxExpandedLength: value.maxExpandedLength ?? 100000,
|
|
111
|
-
maxEntityCount: value.maxEntityCount ?? 100,
|
|
106
|
+
enabled: value.enabled !== false,
|
|
107
|
+
maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),
|
|
108
|
+
maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10),
|
|
109
|
+
maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1000),
|
|
110
|
+
maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
|
|
111
|
+
maxEntityCount: Math.max(1, value.maxEntityCount ?? 100),
|
|
112
112
|
allowedTags: value.allowedTags ?? null,
|
|
113
113
|
tagFilter: value.tagFilter ?? null
|
|
114
114
|
};
|
|
@@ -422,6 +422,8 @@ const parseXml = function (xmlData) {
|
|
|
422
422
|
if (this.options.strictReservedNames &&
|
|
423
423
|
(tagName === this.options.commentPropName
|
|
424
424
|
|| tagName === this.options.cdataPropName
|
|
425
|
+
|| tagName === this.options.textNodeName
|
|
426
|
+
|| tagName === this.options.attributesGroupName
|
|
425
427
|
)) {
|
|
426
428
|
throw new Error(`Invalid tag name: ${tagName}`);
|
|
427
429
|
}
|
|
@@ -621,7 +623,7 @@ function replaceEntitiesValue(val, tagName, jPath) {
|
|
|
621
623
|
}
|
|
622
624
|
|
|
623
625
|
// Replace DOCTYPE entities
|
|
624
|
-
for (
|
|
626
|
+
for (const entityName of Object.keys(this.docTypeEntities)) {
|
|
625
627
|
const entity = this.docTypeEntities[entityName];
|
|
626
628
|
const matches = val.match(entity.regx);
|
|
627
629
|
|
|
@@ -653,19 +655,38 @@ function replaceEntitiesValue(val, tagName, jPath) {
|
|
|
653
655
|
}
|
|
654
656
|
}
|
|
655
657
|
}
|
|
656
|
-
if (val.indexOf('&') === -1) return val; // Early exit
|
|
657
|
-
|
|
658
658
|
// Replace standard entities
|
|
659
|
-
for (
|
|
659
|
+
for (const entityName of Object.keys(this.lastEntities)) {
|
|
660
660
|
const entity = this.lastEntities[entityName];
|
|
661
|
+
const matches = val.match(entity.regex);
|
|
662
|
+
if (matches) {
|
|
663
|
+
this.entityExpansionCount += matches.length;
|
|
664
|
+
if (entityConfig.maxTotalExpansions &&
|
|
665
|
+
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
666
|
+
throw new Error(
|
|
667
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
661
671
|
val = val.replace(entity.regex, entity.val);
|
|
662
672
|
}
|
|
663
|
-
if (val.indexOf('&') === -1) return val;
|
|
673
|
+
if (val.indexOf('&') === -1) return val;
|
|
664
674
|
|
|
665
675
|
// Replace HTML entities if enabled
|
|
666
676
|
if (this.options.htmlEntities) {
|
|
667
|
-
for (
|
|
677
|
+
for (const entityName of Object.keys(this.htmlEntities)) {
|
|
668
678
|
const entity = this.htmlEntities[entityName];
|
|
679
|
+
const matches = val.match(entity.regex);
|
|
680
|
+
if (matches) {
|
|
681
|
+
//console.log(matches);
|
|
682
|
+
this.entityExpansionCount += matches.length;
|
|
683
|
+
if (entityConfig.maxTotalExpansions &&
|
|
684
|
+
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
669
690
|
val = val.replace(entity.regex, entity.val);
|
|
670
691
|
}
|
|
671
692
|
}
|