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.
@@ -1,47 +1,145 @@
1
1
 
2
+ const { DANGEROUS_PROPERTY_NAMES, criticalProperties } = require("../util");
3
+
4
+ const defaultOnDangerousProperty = (name) => {
5
+ if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
6
+ return "__" + name;
7
+ }
8
+ return name;
9
+ };
2
10
  const defaultOptions = {
3
- preserveOrder: false,
4
- attributeNamePrefix: '@_',
5
- attributesGroupName: false,
6
- textNodeName: '#text',
7
- ignoreAttributes: true,
8
- removeNSPrefix: false, // remove NS from tag name or attribute name if true
9
- allowBooleanAttributes: false, //a tag can have attributes without any value
10
- //ignoreRootElement : false,
11
- parseTagValue: true,
12
- parseAttributeValue: false,
13
- trimValues: true, //Trim string values of tag and attributes
14
- cdataPropName: false,
15
- numberParseOptions: {
16
- hex: true,
17
- leadingZeros: true,
18
- eNotation: true
19
- },
20
- tagValueProcessor: function(tagName, val) {
21
- return val;
22
- },
23
- attributeValueProcessor: function(attrName, val) {
24
- return val;
25
- },
26
- stopNodes: [], //nested tags will not be parsed even for errors
27
- alwaysCreateTextNode: false,
28
- isArray: () => false,
29
- commentPropName: false,
30
- unpairedTags: [],
31
- processEntities: true,
32
- htmlEntities: false,
33
- ignoreDeclaration: false,
34
- ignorePiTags: false,
35
- transformTagName: false,
36
- transformAttributeName: false,
37
- updateTag: function(tagName, jPath, attrs){
38
- return tagName
39
- },
40
- // skipEmptyListItem: false
11
+ preserveOrder: false,
12
+ attributeNamePrefix: '@_',
13
+ attributesGroupName: false,
14
+ textNodeName: '#text',
15
+ ignoreAttributes: true,
16
+ removeNSPrefix: false, // remove NS from tag name or attribute name if true
17
+ allowBooleanAttributes: false, //a tag can have attributes without any value
18
+ //ignoreRootElement : false,
19
+ parseTagValue: true,
20
+ parseAttributeValue: false,
21
+ trimValues: true, //Trim string values of tag and attributes
22
+ cdataPropName: false,
23
+ numberParseOptions: {
24
+ hex: true,
25
+ leadingZeros: true,
26
+ eNotation: true
27
+ },
28
+ tagValueProcessor: function (tagName, val) {
29
+ return val;
30
+ },
31
+ attributeValueProcessor: function (attrName, val) {
32
+ return val;
33
+ },
34
+ stopNodes: [], //nested tags will not be parsed even for errors
35
+ alwaysCreateTextNode: false,
36
+ isArray: () => false,
37
+ commentPropName: false,
38
+ unpairedTags: [],
39
+ processEntities: true,
40
+ htmlEntities: false,
41
+ ignoreDeclaration: false,
42
+ ignorePiTags: false,
43
+ transformTagName: false,
44
+ transformAttributeName: false,
45
+ updateTag: function (tagName, jPath, attrs) {
46
+ return tagName
47
+ },
48
+ // skipEmptyListItem: false
49
+ captureMetaData: false,
50
+ maxNestedTags: 100,
51
+ strictReservedNames: true,
52
+ onDangerousProperty: defaultOnDangerousProperty
41
53
  };
42
-
43
- const buildOptions = function(options) {
44
- return Object.assign({}, defaultOptions, options);
54
+ /**
55
+ * Validates that a property name is safe to use
56
+ * @param {string} propertyName - The property name to validate
57
+ * @param {string} optionName - The option field name (for error message)
58
+ * @throws {Error} If property name is dangerous
59
+ */
60
+ function validatePropertyName(propertyName, optionName) {
61
+ if (typeof propertyName !== 'string') {
62
+ return; // Only validate string property names
63
+ }
64
+
65
+ const normalized = propertyName.toLowerCase();
66
+ if (DANGEROUS_PROPERTY_NAMES.some(dangerous => normalized === dangerous.toLowerCase())) {
67
+ throw new Error(
68
+ `[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
69
+ );
70
+ }
71
+
72
+ if (criticalProperties.some(dangerous => normalized === dangerous.toLowerCase())) {
73
+ throw new Error(
74
+ `[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Normalizes processEntities option for backward compatibility
81
+ * @param {boolean|object} value
82
+ * @returns {object} Always returns normalized object
83
+ */
84
+ function normalizeProcessEntities(value) {
85
+ // Boolean backward compatibility
86
+ if (typeof value === 'boolean') {
87
+ return {
88
+ enabled: value, // true or false
89
+ maxEntitySize: 10000,
90
+ maxExpansionDepth: 10,
91
+ maxTotalExpansions: 1000,
92
+ maxExpandedLength: 100000,
93
+ allowedTags: null,
94
+ tagFilter: null
95
+ };
96
+ }
97
+
98
+ // Object config - merge with defaults
99
+ if (typeof value === 'object' && value !== null) {
100
+ return {
101
+ enabled: value.enabled !== false,
102
+ maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),
103
+ maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10),
104
+ maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1000),
105
+ maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
106
+ maxEntityCount: Math.max(1, value.maxEntityCount ?? 100),
107
+ allowedTags: value.allowedTags ?? null,
108
+ tagFilter: value.tagFilter ?? null
109
+ };
110
+ }
111
+
112
+ // Default to enabled with limits
113
+ return normalizeProcessEntities(true);
114
+ }
115
+
116
+ const buildOptions = function (options) {
117
+ const built = Object.assign({}, defaultOptions, options);
118
+
119
+
120
+ // Validate property names to prevent prototype pollution
121
+ const propertyNameOptions = [
122
+ { value: built.attributeNamePrefix, name: 'attributeNamePrefix' },
123
+ { value: built.attributesGroupName, name: 'attributesGroupName' },
124
+ { value: built.textNodeName, name: 'textNodeName' },
125
+ { value: built.cdataPropName, name: 'cdataPropName' },
126
+ { value: built.commentPropName, name: 'commentPropName' }
127
+ ];
128
+
129
+ for (const { value, name } of propertyNameOptions) {
130
+ if (value) {
131
+ validatePropertyName(value, name);
132
+ }
133
+ }
134
+
135
+ if (built.onDangerousProperty === null) {
136
+ built.onDangerousProperty = defaultOnDangerousProperty;
137
+ }
138
+
139
+ // Always normalize processEntities for backward compatibility and validation
140
+ built.processEntities = normalizeProcessEntities(built.processEntities);
141
+ //console.debug(built.processEntities)
142
+ return built;
45
143
  };
46
144
 
47
145
  exports.buildOptions = buildOptions;