fast-xml-parser 5.2.0 → 5.2.2

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.
@@ -22,16 +22,25 @@ export default function readDocType(xmlData, i){
22
22
  let entityName, val;
23
23
  [entityName, val,i] = readEntityExp(xmlData,i+1);
24
24
  if(val.indexOf("&") === -1) //Parameter entities are not supported
25
- entities[ validateEntityName(entityName) ] = {
25
+ entities[ entityName ] = {
26
26
  regx : RegExp( `&${entityName};`,"g"),
27
27
  val: val
28
28
  };
29
29
  }
30
- else if( hasBody && isElement(xmlData, i)) i += 8;//Not supported
31
- else if( hasBody && isAttlist(xmlData, i)) i += 8;//Not supported
32
- else if( hasBody && isNotation(xmlData, i)) i += 9;//Not supported
33
- else if( isComment) comment = true;
34
- else throw new Error("Invalid DOCTYPE");
30
+ else if( hasBody && isElement(xmlData, i)) {
31
+ i += 8;//Not supported
32
+ const {index} = readElementExp(xmlData,i+1);
33
+ i = index;
34
+ }else if( hasBody && isAttlist(xmlData, i)){
35
+ i += 8;//Not supported
36
+ // const {index} = readAttlistExp(xmlData,i+1);
37
+ // i = index;
38
+ }else if( hasBody && isNotation(xmlData, i)) {
39
+ i += 9;//Not supported
40
+ const {index} = readNotationExp(xmlData,i+1);
41
+ i = index;
42
+ }else if( isComment) comment = true;
43
+ else throw new Error("Invalid DOCTYPE");
35
44
 
36
45
  angleBracketsCount++;
37
46
  exp = "";
@@ -62,7 +71,14 @@ export default function readDocType(xmlData, i){
62
71
  return {entities, i};
63
72
  }
64
73
 
65
- function readEntityExp(xmlData,i){
74
+ const skipWhitespace = (data, index) => {
75
+ while (index < data.length && /\s/.test(data[index])) {
76
+ index++;
77
+ }
78
+ return index;
79
+ };
80
+
81
+ function readEntityExp(xmlData, i) {
66
82
  //External entities are not supported
67
83
  // <!ENTITY ext SYSTEM "http://normal-website.com" >
68
84
 
@@ -71,24 +87,265 @@ function readEntityExp(xmlData,i){
71
87
 
72
88
  //Internal entities are supported
73
89
  // <!ENTITY entityname "replacement text">
74
-
75
- //read EntityName
90
+
91
+ // Skip leading whitespace after <!ENTITY
92
+ i = skipWhitespace(xmlData, i);
93
+
94
+ // Read entity name
76
95
  let entityName = "";
77
- for (; i < xmlData.length && (xmlData[i] !== "'" && xmlData[i] !== '"' ); i++) {
78
- // if(xmlData[i] === " ") continue;
79
- // else
96
+ while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
80
97
  entityName += xmlData[i];
98
+ i++;
99
+ }
100
+ validateEntityName(entityName);
101
+
102
+ // Skip whitespace after entity name
103
+ i = skipWhitespace(xmlData, i);
104
+
105
+ // Check for unsupported constructs (external entities or parameter entities)
106
+ if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") {
107
+ throw new Error("External entities are not supported");
108
+ }else if (xmlData[i] === "%") {
109
+ throw new Error("Parameter entities are not supported");
110
+ }
111
+
112
+ // Read entity value (internal entity)
113
+ let entityValue = "";
114
+ [i, entityValue] = readIdentifierVal(xmlData, i, "entity");
115
+ i--;
116
+ return [entityName, entityValue, i ];
117
+ }
118
+
119
+ function readNotationExp(xmlData, i) {
120
+ // Skip leading whitespace after <!NOTATION
121
+ i = skipWhitespace(xmlData, i);
122
+
123
+ // Read notation name
124
+ let notationName = "";
125
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
126
+ notationName += xmlData[i];
127
+ i++;
128
+ }
129
+ validateEntityName(notationName);
130
+
131
+ // Skip whitespace after notation name
132
+ i = skipWhitespace(xmlData, i);
133
+
134
+ // Check identifier type (SYSTEM or PUBLIC)
135
+ const identifierType = xmlData.substring(i, i + 6).toUpperCase();
136
+ if (identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
137
+ throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
138
+ }
139
+ i += identifierType.length;
140
+
141
+ // Skip whitespace after identifier type
142
+ i = skipWhitespace(xmlData, i);
143
+
144
+ // Read public identifier (if PUBLIC)
145
+ let publicIdentifier = null;
146
+ let systemIdentifier = null;
147
+
148
+ if (identifierType === "PUBLIC") {
149
+ [i, publicIdentifier ] = readIdentifierVal(xmlData, i, "publicIdentifier");
150
+
151
+ // Skip whitespace after public identifier
152
+ i = skipWhitespace(xmlData, i);
153
+
154
+ // Optionally read system identifier
155
+ if (xmlData[i] === '"' || xmlData[i] === "'") {
156
+ [i, systemIdentifier ] = readIdentifierVal(xmlData, i,"systemIdentifier");
157
+ }
158
+ } else if (identifierType === "SYSTEM") {
159
+ // Read system identifier (mandatory for SYSTEM)
160
+ [i, systemIdentifier ] = readIdentifierVal(xmlData, i, "systemIdentifier");
161
+
162
+ if (!systemIdentifier) {
163
+ throw new Error("Missing mandatory system identifier for SYSTEM notation");
164
+ }
165
+ }
166
+
167
+ return {notationName, publicIdentifier, systemIdentifier, index: --i};
168
+ }
169
+
170
+ function readIdentifierVal(xmlData, i, type) {
171
+ let identifierVal = "";
172
+ const startChar = xmlData[i];
173
+ if (startChar !== '"' && startChar !== "'") {
174
+ throw new Error(`Expected quoted string, found "${startChar}"`);
175
+ }
176
+ i++;
177
+
178
+ while (i < xmlData.length && xmlData[i] !== startChar) {
179
+ identifierVal += xmlData[i];
180
+ i++;
181
+ }
182
+
183
+ if (xmlData[i] !== startChar) {
184
+ throw new Error(`Unterminated ${type} value`);
185
+ }
186
+ i++;
187
+ return [i, identifierVal];
188
+ }
189
+
190
+ function readElementExp(xmlData, i) {
191
+ // <!ELEMENT name (content-model)>
192
+
193
+ // Skip leading whitespace after <!ELEMENT
194
+ i = skipWhitespace(xmlData, i);
195
+
196
+ // Read element name
197
+ let elementName = "";
198
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
199
+ elementName += xmlData[i];
200
+ i++;
201
+ }
202
+
203
+ // Validate element name
204
+ if (!validateEntityName(elementName)) {
205
+ throw new Error(`Invalid element name: "${elementName}"`);
206
+ }
207
+
208
+ // Skip whitespace after element name
209
+ i = skipWhitespace(xmlData, i);
210
+
211
+ // Expect '(' to start content model
212
+ if (xmlData[i] !== "(") {
213
+ throw new Error(`Expected '(', found "${xmlData[i]}"`);
214
+ }
215
+ i++; // Move past '('
216
+
217
+ // Read content model
218
+ let contentModel = "";
219
+ while (i < xmlData.length && xmlData[i] !== ")") {
220
+ contentModel += xmlData[i];
221
+ i++;
222
+ }
223
+
224
+ if (xmlData[i] !== ")") {
225
+ throw new Error("Unterminated content model");
226
+ }
227
+
228
+ return {
229
+ elementName,
230
+ contentModel: contentModel.trim(),
231
+ index: i
232
+ };
233
+ }
234
+
235
+ function readAttlistExp(xmlData, i) {
236
+ // Skip leading whitespace after <!ATTLIST
237
+ i = skipWhitespace(xmlData, i);
238
+
239
+ // Read element name
240
+ let elementName = "";
241
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
242
+ elementName += xmlData[i];
243
+ i++;
244
+ }
245
+
246
+ // Validate element name
247
+ validateEntityName(elementName)
248
+
249
+ // Skip whitespace after element name
250
+ i = skipWhitespace(xmlData, i);
251
+
252
+ // Read attribute name
253
+ let attributeName = "";
254
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
255
+ attributeName += xmlData[i];
256
+ i++;
257
+ }
258
+
259
+ // Validate attribute name
260
+ if (!validateEntityName(attributeName)) {
261
+ throw new Error(`Invalid attribute name: "${attributeName}"`);
262
+ }
263
+
264
+ // Skip whitespace after attribute name
265
+ i = skipWhitespace(xmlData, i);
266
+
267
+ // Read attribute type
268
+ let attributeType = "";
269
+ if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") {
270
+ attributeType = "NOTATION";
271
+ i += 8; // Move past "NOTATION"
272
+
273
+ // Skip whitespace after "NOTATION"
274
+ i = skipWhitespace(xmlData, i);
275
+
276
+ // Expect '(' to start the list of notations
277
+ if (xmlData[i] !== "(") {
278
+ throw new Error(`Expected '(', found "${xmlData[i]}"`);
279
+ }
280
+ i++; // Move past '('
281
+
282
+ // Read the list of allowed notations
283
+ let allowedNotations = [];
284
+ while (i < xmlData.length && xmlData[i] !== ")") {
285
+ let notation = "";
286
+ while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
287
+ notation += xmlData[i];
288
+ i++;
289
+ }
290
+
291
+ // Validate notation name
292
+ notation = notation.trim();
293
+ if (!validateEntityName(notation)) {
294
+ throw new Error(`Invalid notation name: "${notation}"`);
295
+ }
296
+
297
+ allowedNotations.push(notation);
298
+
299
+ // Skip '|' separator or exit loop
300
+ if (xmlData[i] === "|") {
301
+ i++; // Move past '|'
302
+ i = skipWhitespace(xmlData, i); // Skip optional whitespace after '|'
303
+ }
304
+ }
305
+
306
+ if (xmlData[i] !== ")") {
307
+ throw new Error("Unterminated list of notations");
308
+ }
309
+ i++; // Move past ')'
310
+
311
+ // Store the allowed notations as part of the attribute type
312
+ attributeType += " (" + allowedNotations.join("|") + ")";
313
+ } else {
314
+ // Handle simple types (e.g., CDATA, ID, IDREF, etc.)
315
+ while (i < xmlData.length && !/\s/.test(xmlData[i])) {
316
+ attributeType += xmlData[i];
317
+ i++;
318
+ }
319
+
320
+ // Validate simple attribute type
321
+ const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
322
+ if (!validTypes.includes(attributeType.toUpperCase())) {
323
+ throw new Error(`Invalid attribute type: "${attributeType}"`);
324
+ }
325
+ }
326
+
327
+ // Skip whitespace after attribute type
328
+ i = skipWhitespace(xmlData, i);
329
+
330
+ // Read default value
331
+ let defaultValue = "";
332
+ if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") {
333
+ defaultValue = "#REQUIRED";
334
+ i += 8;
335
+ } else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") {
336
+ defaultValue = "#IMPLIED";
337
+ i += 7;
338
+ } else {
339
+ [i, defaultValue] = readIdentifierVal(xmlData, i, "ATTLIST");
81
340
  }
82
- entityName = entityName.trim();
83
- if(entityName.indexOf(" ") !== -1) throw new Error("External entites are not supported");
84
341
 
85
- //read Entity Value
86
- const startChar = xmlData[i++];
87
- let val = ""
88
- for (; i < xmlData.length && xmlData[i] !== startChar ; i++) {
89
- val += xmlData[i];
342
+ return {
343
+ elementName,
344
+ attributeName,
345
+ attributeType,
346
+ defaultValue,
347
+ index: i
90
348
  }
91
- return [entityName, val, i];
92
349
  }
93
350
 
94
351
  function isComment(xmlData, i){