• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NaturalIntelligence / fast-xml-parser / 23146869289

16 Mar 2026 01:45PM UTC coverage: 97.709% (-0.1%) from 97.837%
23146869289

push

github

amitguptagwl
refactor: performance improvement

1144 of 1190 branches covered (96.13%)

2 of 2 new or added lines in 1 file covered. (100.0%)

44 existing lines in 4 files now uncovered.

9382 of 9602 relevant lines covered (97.71%)

464759.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.08
/src/xmlparser/OptionsBuilder.js
1
import { DANGEROUS_PROPERTY_NAMES, criticalProperties } from "../util.js";
5✔
2

5✔
3
const defaultOnDangerousProperty = (name) => {
5✔
UNCOV
4
  if (DANGEROUS_PROPERTY_NAMES.includes(name)) {
×
UNCOV
5
    return "__" + name;
×
UNCOV
6
  }
×
UNCOV
7
  return name;
×
8
};
5✔
9

5✔
10

5✔
11
export const defaultOptions = {
5✔
12
  preserveOrder: false,
5✔
13
  attributeNamePrefix: '@_',
5✔
14
  attributesGroupName: false,
5✔
15
  textNodeName: '#text',
5✔
16
  ignoreAttributes: true,
5✔
17
  removeNSPrefix: false, // remove NS from tag name or attribute name if true
5✔
18
  allowBooleanAttributes: false, //a tag can have attributes without any value
5✔
19
  //ignoreRootElement : false,
5✔
20
  parseTagValue: true,
5✔
21
  parseAttributeValue: false,
5✔
22
  trimValues: true, //Trim string values of tag and attributes
5✔
23
  cdataPropName: false,
5✔
24
  numberParseOptions: {
5✔
25
    hex: true,
5✔
26
    leadingZeros: true,
5✔
27
    eNotation: true
5✔
28
  },
5✔
29
  tagValueProcessor: function (tagName, val) {
5✔
30
    return val;
1,830✔
31
  },
5✔
32
  attributeValueProcessor: function (attrName, val) {
5✔
33
    return val;
1,195✔
34
  },
5✔
35
  stopNodes: [], //nested tags will not be parsed even for errors
5✔
36
  alwaysCreateTextNode: false,
5✔
37
  isArray: () => false,
5✔
38
  commentPropName: false,
5✔
39
  unpairedTags: [],
5✔
40
  processEntities: true,
5✔
41
  htmlEntities: false,
5✔
42
  ignoreDeclaration: false,
5✔
43
  ignorePiTags: false,
5✔
44
  transformTagName: false,
5✔
45
  transformAttributeName: false,
5✔
46
  updateTag: function (tagName, jPath, attrs) {
5✔
47
    return tagName
6,355✔
48
  },
5✔
49
  // skipEmptyListItem: false
5✔
50
  captureMetaData: false,
5✔
51
  maxNestedTags: 100,
5✔
52
  strictReservedNames: true,
5✔
53
  jPath: true, // if true, pass jPath string to callbacks; if false, pass matcher instance
5✔
54
  onDangerousProperty: defaultOnDangerousProperty
5✔
55
};
5✔
56

5✔
57

5✔
58
/**
5✔
59
 * Validates that a property name is safe to use
5✔
60
 * @param {string} propertyName - The property name to validate
5✔
61
 * @param {string} optionName - The option field name (for error message)
5✔
62
 * @throws {Error} If property name is dangerous
5✔
63
 */
5✔
64
function validatePropertyName(propertyName, optionName) {
2,385✔
65
  if (typeof propertyName !== 'string') {
2,385!
UNCOV
66
    return; // Only validate string property names
×
UNCOV
67
  }
×
68

2,385✔
69
  const normalized = propertyName.toLowerCase();
2,385✔
70
  if (DANGEROUS_PROPERTY_NAMES.some(dangerous => normalized === dangerous.toLowerCase())) {
2,385✔
71
    throw new Error(
35✔
72
      `[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
35✔
73
    );
35✔
74
  }
35✔
75

2,350✔
76
  if (criticalProperties.some(dangerous => normalized === dangerous.toLowerCase())) {
2,385✔
77
    throw new Error(
15✔
78
      `[SECURITY] Invalid ${optionName}: "${propertyName}" is a reserved JavaScript keyword that could cause prototype pollution`
15✔
79
    );
15✔
80
  }
15✔
81
}
2,385✔
82

5✔
83
/**
5✔
84
 * Normalizes processEntities option for backward compatibility
5✔
85
 * @param {boolean|object} value 
5✔
86
 * @returns {object} Always returns normalized object
5✔
87
 */
5✔
88
function normalizeProcessEntities(value) {
1,210✔
89
  // Boolean backward compatibility
1,210✔
90
  if (typeof value === 'boolean') {
1,210✔
91
    return {
1,090✔
92
      enabled: value, // true or false
1,090✔
93
      maxEntitySize: 10000,
1,090✔
94
      maxExpansionDepth: 10,
1,090✔
95
      maxTotalExpansions: 1000,
1,090✔
96
      maxExpandedLength: 100000,
1,090✔
97
      maxEntityCount: 100,
1,090✔
98
      allowedTags: null,
1,090✔
99
      tagFilter: null
1,090✔
100
    };
1,090✔
101
  }
1,090✔
102

120✔
103
  // Object config - merge with defaults
120✔
104
  if (typeof value === 'object' && value !== null) {
1,210✔
105
    return {
120✔
106
      enabled: value.enabled !== false, // default true if not specified
120✔
107
      maxEntitySize: value.maxEntitySize ?? 10000,
120✔
108
      maxExpansionDepth: value.maxExpansionDepth ?? 10,
120✔
109
      maxTotalExpansions: value.maxTotalExpansions ?? 1000,
120✔
110
      maxExpandedLength: value.maxExpandedLength ?? 100000,
120✔
111
      maxEntityCount: value.maxEntityCount ?? 100,
120✔
112
      allowedTags: value.allowedTags ?? null,
120✔
113
      tagFilter: value.tagFilter ?? null
120✔
114
    };
120✔
115
  }
120✔
UNCOV
116

×
UNCOV
117
  // Default to enabled with limits
×
UNCOV
118
  return normalizeProcessEntities(true);
×
119
}
1,210✔
120

5✔
121
export const buildOptions = function (options) {
5✔
122
  const built = Object.assign({}, defaultOptions, options);
1,260✔
123

1,260✔
124
  // Validate property names to prevent prototype pollution
1,260✔
125
  const propertyNameOptions = [
1,260✔
126
    { value: built.attributeNamePrefix, name: 'attributeNamePrefix' },
1,260✔
127
    { value: built.attributesGroupName, name: 'attributesGroupName' },
1,260✔
128
    { value: built.textNodeName, name: 'textNodeName' },
1,260✔
129
    { value: built.cdataPropName, name: 'cdataPropName' },
1,260✔
130
    { value: built.commentPropName, name: 'commentPropName' }
1,260✔
131
  ];
1,260✔
132

1,260✔
133
  for (const { value, name } of propertyNameOptions) {
1,260✔
134
    if (value) {
6,200✔
135
      validatePropertyName(value, name);
2,385✔
136
    }
2,385✔
137
  }
6,200✔
138

1,210✔
139
  if (built.onDangerousProperty === null) {
1,260!
UNCOV
140
    built.onDangerousProperty = defaultOnDangerousProperty;
×
UNCOV
141
  }
×
142

1,210✔
143
  // Always normalize processEntities for backward compatibility and validation
1,210✔
144
  built.processEntities = normalizeProcessEntities(built.processEntities);
1,210✔
145

1,210✔
146
  // Convert old-style stopNodes for backward compatibility
1,210✔
147
  if (built.stopNodes && Array.isArray(built.stopNodes)) {
1,260✔
148
    built.stopNodes = built.stopNodes.map(node => {
1,210✔
149
      if (typeof node === 'string' && node.startsWith('*.')) {
265✔
150
        // Old syntax: *.tagname meant "tagname anywhere"
60✔
151
        // Convert to new syntax: ..tagname
60✔
152
        return '..' + node.substring(2);
60✔
153
      }
60✔
154
      return node;
205✔
155
    });
1,210✔
156
  }
1,210✔
157
  //console.debug(built.processEntities)
1,210✔
158
  return built;
1,210✔
159
};
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc