fast-xml-parser 4.5.3 → 4.5.5
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 +17 -0
- package/package.json +5 -6
- package/src/fxp.d.ts +126 -18
- package/src/util.js +26 -9
- package/src/xmlbuilder/orderedJs2Xml.js +14 -3
- package/src/xmlparser/DocTypeReader.js +387 -131
- package/src/xmlparser/OptionsBuilder.js +139 -41
- package/src/xmlparser/OrderedObjParser.js +346 -191
- package/src/v6/CharsSymbol.js +0 -16
- package/src/v6/EntitiesParser.js +0 -104
- package/src/v6/OptionsBuilder.js +0 -61
- package/src/v6/OutputBuilders/BaseOutputBuilder.js +0 -69
- package/src/v6/OutputBuilders/JsArrBuilder.js +0 -103
- package/src/v6/OutputBuilders/JsMinArrBuilder.js +0 -100
- package/src/v6/OutputBuilders/JsObjBuilder.js +0 -154
- package/src/v6/OutputBuilders/ParserOptionsBuilder.js +0 -94
- package/src/v6/Report.js +0 -0
- package/src/v6/TagPath.js +0 -81
- package/src/v6/TagPathMatcher.js +0 -13
- package/src/v6/XMLParser.js +0 -83
- package/src/v6/Xml2JsParser.js +0 -235
- package/src/v6/XmlPartReader.js +0 -210
- package/src/v6/XmlSpecialTagsReader.js +0 -111
- package/src/v6/inputSource/BufferSource.js +0 -116
- package/src/v6/inputSource/StringSource.js +0 -121
- package/src/v6/valueParsers/EntitiesParser.js +0 -105
- package/src/v6/valueParsers/booleanParser.js +0 -22
- package/src/v6/valueParsers/booleanParserExt.js +0 -19
- package/src/v6/valueParsers/currency.js +0 -38
- package/src/v6/valueParsers/join.js +0 -13
- package/src/v6/valueParsers/number.js +0 -14
- package/src/v6/valueParsers/trim.js +0 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
<small>Note: If you find missing information about particular minor version, that version must have been changed without any functional change in this library.</small>
|
|
2
2
|
|
|
3
|
+
**4.5.4 / 2026-02-26**
|
|
4
|
+
- support strictReservedNames
|
|
5
|
+
- support captureMetaData
|
|
6
|
+
- support maxNestedTags
|
|
7
|
+
- handle non-array input for XML builder when preserveOrder is true (By Angelo Coetzee)
|
|
8
|
+
- Improve security and performance of entity processing
|
|
9
|
+
- new options maxEntitySize, maxExpansionDepth, maxTotalExpansions, maxExpandedLength, allowedTags,tagFilter
|
|
10
|
+
- fast return when no edtity is present
|
|
11
|
+
- improvement replacement logic to reduce number of calls
|
|
12
|
+
- fix: Escape regex char in entity name
|
|
13
|
+
- fix: handle HTML numeric and hex entities when out of range
|
|
14
|
+
- fix #775: transformTagName with allowBooleanAttributes adds an unnecessary attribute
|
|
15
|
+
- Use Uint8Array in place of Buffer in Parser
|
|
16
|
+
- Support EMPTY and ANY with ELEMENT in DOCTYPE
|
|
17
|
+
- fix: support numeric entities with values over 0xFFFF (#726) (By Marc Durdin)
|
|
18
|
+
|
|
19
|
+
|
|
3
20
|
**4.5.2 / 2025-02-18**
|
|
4
21
|
- Fix null CDATA to comply with undefined behavior (#701) (By [Matthieu BOHEAS](https://github.com/Kelgors))
|
|
5
22
|
- Fix(performance): Update check for leaf node in saveTextToParentTag function in OrderedObjParser.js (#707) (By [...](https://github.com/tomingtoming))
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-parser",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.5",
|
|
4
4
|
"description": "Validate XML, Parse XML, Build XML without C/C++ based libraries",
|
|
5
5
|
"main": "./src/fxp.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "c8 --reporter=lcov --reporter=text jasmine spec/*spec.js",
|
|
8
8
|
"test-types": "tsc --noEmit spec/typings/typings-test.ts",
|
|
9
9
|
"unit": "jasmine",
|
|
10
|
-
"coverage": "nyc report --reporter html --reporter text -t .nyc_output --report-dir .nyc_output/summary",
|
|
11
10
|
"perf": "node ./benchmark/perfTest3.js",
|
|
12
11
|
"lint": "eslint src/*.js spec/*.js",
|
|
13
12
|
"bundle": "webpack --config webpack-prod.config.js",
|
|
@@ -49,10 +48,10 @@
|
|
|
49
48
|
"@babel/register": "^7.13.8",
|
|
50
49
|
"@types/node": "20",
|
|
51
50
|
"babel-loader": "^8.2.2",
|
|
51
|
+
"c8": "^10.1.3",
|
|
52
52
|
"eslint": "^8.3.0",
|
|
53
53
|
"he": "^1.2.0",
|
|
54
54
|
"jasmine": "^3.6.4",
|
|
55
|
-
"nyc": "^15.1.0",
|
|
56
55
|
"prettier": "^1.19.1",
|
|
57
56
|
"publish-please": "^5.5.2",
|
|
58
57
|
"typescript": "5",
|
|
@@ -67,6 +66,6 @@
|
|
|
67
66
|
}
|
|
68
67
|
],
|
|
69
68
|
"dependencies": {
|
|
70
|
-
"strnum": "^1.
|
|
69
|
+
"strnum": "^1.0.5"
|
|
71
70
|
}
|
|
72
|
-
}
|
|
71
|
+
}
|
package/src/fxp.d.ts
CHANGED
|
@@ -1,4 +1,67 @@
|
|
|
1
|
-
type
|
|
1
|
+
export type ProcessEntitiesOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Whether to enable entity processing
|
|
4
|
+
*
|
|
5
|
+
* Defaults to `true`
|
|
6
|
+
*/
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Maximum size in characters for a single entity definition
|
|
11
|
+
*
|
|
12
|
+
* Defaults to `10000`
|
|
13
|
+
*/
|
|
14
|
+
maxEntitySize?: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maximum depth for nested entity references (reserved for future use)
|
|
18
|
+
*
|
|
19
|
+
* Defaults to `10`
|
|
20
|
+
*/
|
|
21
|
+
maxExpansionDepth?: number;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Maximum total number of entity expansions allowed
|
|
25
|
+
*
|
|
26
|
+
* Defaults to `1000`
|
|
27
|
+
*/
|
|
28
|
+
maxTotalExpansions?: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Maximum total expanded content length in characters
|
|
32
|
+
*
|
|
33
|
+
* Defaults to `100000`
|
|
34
|
+
*/
|
|
35
|
+
maxExpandedLength?: number;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of entities allowed in the XML
|
|
39
|
+
*
|
|
40
|
+
* Defaults to `100`
|
|
41
|
+
*/
|
|
42
|
+
maxEntityCount?: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Array of tag names where entity replacement is allowed.
|
|
46
|
+
* If null, entities are replaced in all tags.
|
|
47
|
+
*
|
|
48
|
+
* Defaults to `null`
|
|
49
|
+
*/
|
|
50
|
+
allowedTags?: string[] | null;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom filter function to determine if entities should be replaced in a tag
|
|
54
|
+
*
|
|
55
|
+
* @param tagName - The name of the current tag
|
|
56
|
+
* @param jPath - The jPath of the current tag
|
|
57
|
+
* @returns `true` to allow entity replacement, `false` to skip
|
|
58
|
+
*
|
|
59
|
+
* Defaults to `null`
|
|
60
|
+
*/
|
|
61
|
+
tagFilter?: ((tagName: string, jPath: string) => boolean) | null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type X2jOptions = {
|
|
2
65
|
/**
|
|
3
66
|
* Preserve the order of tags in resulting JS object
|
|
4
67
|
*
|
|
@@ -10,7 +73,7 @@ type X2jOptions = {
|
|
|
10
73
|
* Give a prefix to the attribute name in the resulting JS object
|
|
11
74
|
*
|
|
12
75
|
* Defaults to '@_'
|
|
13
|
-
*/
|
|
76
|
+
*/
|
|
14
77
|
attributeNamePrefix?: string;
|
|
15
78
|
|
|
16
79
|
/**
|
|
@@ -64,7 +127,7 @@ type X2jOptions = {
|
|
|
64
127
|
parseTagValue?: boolean;
|
|
65
128
|
|
|
66
129
|
/**
|
|
67
|
-
* Whether to parse
|
|
130
|
+
* Whether to parse attribute value with `strnum` package
|
|
68
131
|
*
|
|
69
132
|
* Defaults to `false`
|
|
70
133
|
*/
|
|
@@ -161,9 +224,15 @@ type X2jOptions = {
|
|
|
161
224
|
/**
|
|
162
225
|
* Whether to process default and DOCTYPE entities
|
|
163
226
|
*
|
|
227
|
+
* When `true` - enables entity processing with default limits
|
|
228
|
+
*
|
|
229
|
+
* When `false` - disables all entity processing
|
|
230
|
+
*
|
|
231
|
+
* When `ProcessEntitiesOptions` - enables entity processing with custom configuration
|
|
232
|
+
*
|
|
164
233
|
* Defaults to `true`
|
|
165
234
|
*/
|
|
166
|
-
processEntities?: boolean;
|
|
235
|
+
processEntities?: boolean | ProcessEntitiesOptions;
|
|
167
236
|
|
|
168
237
|
/**
|
|
169
238
|
* Whether to process HTML entities
|
|
@@ -209,24 +278,56 @@ type X2jOptions = {
|
|
|
209
278
|
*
|
|
210
279
|
* Defaults to `(tagName, jPath, attrs) => tagName`
|
|
211
280
|
*/
|
|
212
|
-
updateTag?: (tagName: string, jPath: string, attrs: {[k: string]: string}) =>
|
|
281
|
+
updateTag?: (tagName: string, jPath: string, attrs: { [k: string]: string }) => string | boolean;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* If true, adds a Symbol to all object nodes, accessible by {@link XMLParser.getMetaDataSymbol} with
|
|
285
|
+
* metadata about each the node in the XML file.
|
|
286
|
+
*/
|
|
287
|
+
captureMetaData?: boolean;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Maximum number of nested tags
|
|
291
|
+
*
|
|
292
|
+
* Defaults to `100`
|
|
293
|
+
*/
|
|
294
|
+
maxNestedTags?: number;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Whether to strictly validate tag names
|
|
298
|
+
*
|
|
299
|
+
* Defaults to `true`
|
|
300
|
+
*/
|
|
301
|
+
strictReservedNames?: boolean;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Function to sanitize dangerous property names
|
|
305
|
+
*
|
|
306
|
+
* @param name - The name of the property
|
|
307
|
+
* @returns {string} The sanitized name
|
|
308
|
+
*
|
|
309
|
+
* Defaults to `(name) => __name`
|
|
310
|
+
*/
|
|
311
|
+
onDangerousProperty?: (name: string) => string;
|
|
213
312
|
};
|
|
214
313
|
|
|
215
|
-
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
export type strnumOptions = {
|
|
216
317
|
hex: boolean;
|
|
217
318
|
leadingZeros: boolean,
|
|
218
319
|
skipLike?: RegExp,
|
|
219
320
|
eNotation?: boolean
|
|
220
321
|
}
|
|
221
322
|
|
|
222
|
-
type validationOptions = {
|
|
323
|
+
export type validationOptions = {
|
|
223
324
|
/**
|
|
224
325
|
* Whether to allow attributes without value
|
|
225
326
|
*
|
|
226
327
|
* Defaults to `false`
|
|
227
328
|
*/
|
|
228
329
|
allowBooleanAttributes?: boolean;
|
|
229
|
-
|
|
330
|
+
|
|
230
331
|
/**
|
|
231
332
|
* List of tags without closing tags
|
|
232
333
|
*
|
|
@@ -235,12 +336,12 @@ type validationOptions = {
|
|
|
235
336
|
unpairedTags?: string[];
|
|
236
337
|
};
|
|
237
338
|
|
|
238
|
-
type XmlBuilderOptions = {
|
|
339
|
+
export type XmlBuilderOptions = {
|
|
239
340
|
/**
|
|
240
341
|
* Give a prefix to the attribute name in the resulting JS object
|
|
241
342
|
*
|
|
242
343
|
* Defaults to '@_'
|
|
243
|
-
*/
|
|
344
|
+
*/
|
|
244
345
|
attributeNamePrefix?: string;
|
|
245
346
|
|
|
246
347
|
/**
|
|
@@ -385,22 +486,29 @@ type XmlBuilderOptions = {
|
|
|
385
486
|
|
|
386
487
|
|
|
387
488
|
oneListGroup?: boolean;
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Maximum number of nested tags
|
|
492
|
+
*
|
|
493
|
+
* Defaults to `100`
|
|
494
|
+
*/
|
|
495
|
+
maxNestedTags?: number;
|
|
388
496
|
};
|
|
389
497
|
|
|
390
|
-
type ESchema = string | object | Array<string|object>;
|
|
498
|
+
type ESchema = string | object | Array<string | object>;
|
|
391
499
|
|
|
392
|
-
type ValidationError = {
|
|
393
|
-
err: {
|
|
500
|
+
export type ValidationError = {
|
|
501
|
+
err: {
|
|
394
502
|
code: string;
|
|
395
503
|
msg: string,
|
|
396
504
|
line: number,
|
|
397
|
-
col: number
|
|
505
|
+
col: number
|
|
398
506
|
};
|
|
399
507
|
};
|
|
400
508
|
|
|
401
509
|
export class XMLParser {
|
|
402
510
|
constructor(options?: X2jOptions);
|
|
403
|
-
parse(xmlData: string |
|
|
511
|
+
parse(xmlData: string | Uint8Array, validationOptions?: validationOptions | boolean): any;
|
|
404
512
|
/**
|
|
405
513
|
* Add Entity which is not by default supported by this library
|
|
406
514
|
* @param entityIdentifier {string} Eg: 'ent' for &ent;
|
|
@@ -409,10 +517,10 @@ export class XMLParser {
|
|
|
409
517
|
addEntity(entityIdentifier: string, entityValue: string): void;
|
|
410
518
|
}
|
|
411
519
|
|
|
412
|
-
export class XMLValidator{
|
|
413
|
-
static validate(
|
|
520
|
+
export class XMLValidator {
|
|
521
|
+
static validate(xmlData: string, options?: validationOptions): true | ValidationError;
|
|
414
522
|
}
|
|
415
523
|
export class XMLBuilder {
|
|
416
524
|
constructor(options?: XmlBuilderOptions);
|
|
417
|
-
build(jObj: any):
|
|
525
|
+
build(jObj: any): string;
|
|
418
526
|
}
|
package/src/util.js
CHANGED
|
@@ -5,7 +5,7 @@ const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
|
|
|
5
5
|
const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*'
|
|
6
6
|
const regexName = new RegExp('^' + nameRegexp + '$');
|
|
7
7
|
|
|
8
|
-
const getAllMatches = function(string, regex) {
|
|
8
|
+
const getAllMatches = function (string, regex) {
|
|
9
9
|
const matches = [];
|
|
10
10
|
let match = regex.exec(string);
|
|
11
11
|
while (match) {
|
|
@@ -21,16 +21,16 @@ const getAllMatches = function(string, regex) {
|
|
|
21
21
|
return matches;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const isName = function(string) {
|
|
24
|
+
const isName = function (string) {
|
|
25
25
|
const match = regexName.exec(string);
|
|
26
26
|
return !(match === null || typeof match === 'undefined');
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
exports.isExist = function(v) {
|
|
29
|
+
exports.isExist = function (v) {
|
|
30
30
|
return typeof v !== 'undefined';
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
exports.isEmptyObject = function(obj) {
|
|
33
|
+
exports.isEmptyObject = function (obj) {
|
|
34
34
|
return Object.keys(obj).length === 0;
|
|
35
35
|
};
|
|
36
36
|
|
|
@@ -39,13 +39,13 @@ exports.isEmptyObject = function(obj) {
|
|
|
39
39
|
* @param {*} target
|
|
40
40
|
* @param {*} a
|
|
41
41
|
*/
|
|
42
|
-
exports.merge = function(target, a, arrayMode) {
|
|
42
|
+
exports.merge = function (target, a, arrayMode) {
|
|
43
43
|
if (a) {
|
|
44
44
|
const keys = Object.keys(a); // will return an array of own properties
|
|
45
45
|
const len = keys.length; //don't make it inline
|
|
46
46
|
for (let i = 0; i < len; i++) {
|
|
47
47
|
if (arrayMode === 'strict') {
|
|
48
|
-
target[keys[i]] = [
|
|
48
|
+
target[keys[i]] = [a[keys[i]]];
|
|
49
49
|
} else {
|
|
50
50
|
target[keys[i]] = a[keys[i]];
|
|
51
51
|
}
|
|
@@ -56,7 +56,7 @@ exports.merge = function(target, a, arrayMode) {
|
|
|
56
56
|
return Object.assign(b,a);
|
|
57
57
|
} */
|
|
58
58
|
|
|
59
|
-
exports.getValue = function(v) {
|
|
59
|
+
exports.getValue = function (v) {
|
|
60
60
|
if (exports.isExist(v)) {
|
|
61
61
|
return v;
|
|
62
62
|
} else {
|
|
@@ -64,9 +64,26 @@ exports.getValue = function(v) {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Dangerous property names that could lead to prototype pollution or security issues
|
|
69
|
+
*/
|
|
70
|
+
const DANGEROUS_PROPERTY_NAMES = [
|
|
71
|
+
// '__proto__',
|
|
72
|
+
// 'constructor',
|
|
73
|
+
// 'prototype',
|
|
74
|
+
'hasOwnProperty',
|
|
75
|
+
'toString',
|
|
76
|
+
'valueOf',
|
|
77
|
+
'__defineGetter__',
|
|
78
|
+
'__defineSetter__',
|
|
79
|
+
'__lookupGetter__',
|
|
80
|
+
'__lookupSetter__'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const criticalProperties = ["__proto__", "constructor", "prototype"];
|
|
69
84
|
|
|
70
85
|
exports.isName = isName;
|
|
71
86
|
exports.getAllMatches = getAllMatches;
|
|
72
87
|
exports.nameRegexp = nameRegexp;
|
|
88
|
+
exports.DANGEROUS_PROPERTY_NAMES = DANGEROUS_PROPERTY_NAMES;
|
|
89
|
+
exports.criticalProperties = criticalProperties;
|
|
@@ -18,10 +18,21 @@ function arrToStr(arr, options, jPath, indentation) {
|
|
|
18
18
|
let xmlStr = "";
|
|
19
19
|
let isPreviousElementTag = false;
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
if (!Array.isArray(arr)) {
|
|
23
|
+
// Non-array values (e.g. string tag values) should be treated as text content
|
|
24
|
+
if (arr !== undefined && arr !== null) {
|
|
25
|
+
let text = arr.toString();
|
|
26
|
+
text = replaceEntitiesValue(text, options);
|
|
27
|
+
return text;
|
|
28
|
+
}
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
|
|
21
32
|
for (let i = 0; i < arr.length; i++) {
|
|
22
33
|
const tagObj = arr[i];
|
|
23
34
|
const tagName = propName(tagObj);
|
|
24
|
-
if(tagName === undefined) continue;
|
|
35
|
+
if (tagName === undefined) continue;
|
|
25
36
|
|
|
26
37
|
let newJPath = "";
|
|
27
38
|
if (jPath.length === 0) newJPath = tagName
|
|
@@ -92,7 +103,7 @@ function propName(obj) {
|
|
|
92
103
|
const keys = Object.keys(obj);
|
|
93
104
|
for (let i = 0; i < keys.length; i++) {
|
|
94
105
|
const key = keys[i];
|
|
95
|
-
if(!
|
|
106
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
96
107
|
if (key !== ":@") return key;
|
|
97
108
|
}
|
|
98
109
|
}
|
|
@@ -101,7 +112,7 @@ function attr_to_str(attrMap, options) {
|
|
|
101
112
|
let attrStr = "";
|
|
102
113
|
if (attrMap && !options.ignoreAttributes) {
|
|
103
114
|
for (let attr in attrMap) {
|
|
104
|
-
if(!
|
|
115
|
+
if (!Object.prototype.hasOwnProperty.call(attrMap, attr)) continue;
|
|
105
116
|
let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
|
|
106
117
|
attrVal = replaceEntitiesValue(attrVal, options);
|
|
107
118
|
if (attrVal === true && options.suppressBooleanAttributes) {
|