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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-xml-parser",
3
- "version": "5.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.3",
90
+ "fast-xml-builder": "^1.1.4",
91
91
  "path-expression-matcher": "^1.1.3",
92
- "strnum": "^2.1.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
- let entityName = "";
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
- let notationName = "";
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
- let elementName = "";
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 elementName = "";
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
- let attributeName = "";
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
- let notation = "";
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, // default true if not specified
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 (let entityName in this.docTypeEntities) {
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 (let entityName in this.lastEntities) {
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; // Early exit
673
+ if (val.indexOf('&') === -1) return val;
664
674
 
665
675
  // Replace HTML entities if enabled
666
676
  if (this.options.htmlEntities) {
667
- for (let entityName in this.htmlEntities) {
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
  }