fast-xml-validator 1.1.1 → 1.3.0
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/README.md +8 -0
- package/lib/fxv.cjs +1 -1
- package/lib/fxv.min.js +1 -1
- package/lib/fxv.min.js.map +1 -1
- package/package.json +4 -4
- package/src/util.js +0 -18
- package/src/validator.js +17 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-xml-validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Validate XML for Syntax and Business rules without C/C++ based libraries",
|
|
5
5
|
"main": "./lib/fxv.cjs",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"lib",
|
|
28
28
|
"src",
|
|
29
|
-
"
|
|
29
|
+
"README.md"
|
|
30
30
|
],
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
}
|
|
66
66
|
],
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"detailed-xml-validator": "^2.
|
|
69
|
-
"xml-naming": "^0.
|
|
68
|
+
"detailed-xml-validator": "^2.2.0",
|
|
69
|
+
"xml-naming": "^0.3.0"
|
|
70
70
|
}
|
|
71
71
|
}
|
package/src/util.js
CHANGED
|
@@ -31,21 +31,3 @@ export function getValue(v) {
|
|
|
31
31
|
return '';
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Dangerous property names that could lead to prototype pollution or security issues
|
|
37
|
-
*/
|
|
38
|
-
export const DANGEROUS_PROPERTY_NAMES = [
|
|
39
|
-
// '__proto__',
|
|
40
|
-
// 'constructor',
|
|
41
|
-
// 'prototype',
|
|
42
|
-
'hasOwnProperty',
|
|
43
|
-
'toString',
|
|
44
|
-
'valueOf',
|
|
45
|
-
'__defineGetter__',
|
|
46
|
-
'__defineSetter__',
|
|
47
|
-
'__lookupGetter__',
|
|
48
|
-
'__lookupSetter__'
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
export const criticalProperties = ["__proto__", "constructor", "prototype"];
|
package/src/validator.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import { getAllMatches } from './util.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createValidator } from 'xml-naming';
|
|
5
5
|
import DocTypeValidator from './DocTypeValidator.js';
|
|
6
6
|
import ValidationError from './ValidationError.js';
|
|
7
7
|
|
|
@@ -39,10 +39,16 @@ export function validate(xmlData, options) {
|
|
|
39
39
|
|
|
40
40
|
const docTypeValidator = new DocTypeValidator(options.docType, xmlVersion);
|
|
41
41
|
|
|
42
|
+
// One memoized validator instance per production for this parse call — element/attribute
|
|
43
|
+
// names are repeated across many siblings, so caching pays off. xmlVersion is fixed for
|
|
44
|
+
// the whole parse, so it's safe to bind it once here rather than re-resolving per call.
|
|
45
|
+
const qNameValidator = createValidator('qName', { xmlVersion });
|
|
46
|
+
const nameValidator = createValidator('name', { xmlVersion });
|
|
47
|
+
|
|
42
48
|
for (let i = 0; i < xmlData.length; i++) {
|
|
43
49
|
if (xmlData[i] === '<' && xmlData[i + 1] === '?') {
|
|
44
50
|
i += 2;
|
|
45
|
-
i = readPI(xmlData, i,
|
|
51
|
+
i = readPI(xmlData, i, nameValidator);
|
|
46
52
|
if (i.err) return i;
|
|
47
53
|
} else if (xmlData[i] === '<') {
|
|
48
54
|
//starting of tag
|
|
@@ -84,9 +90,9 @@ export function validate(xmlData, options) {
|
|
|
84
90
|
i--;
|
|
85
91
|
}
|
|
86
92
|
|
|
87
|
-
// use
|
|
93
|
+
// use the shared qName validator so namespaced tags (e.g. <ns:tag>) are
|
|
88
94
|
// accepted and malformed ones (e.g. <:tag>, <ns:>, <a:b:c>) are rejected.
|
|
89
|
-
if (!
|
|
95
|
+
if (!qNameValidator(tagName)) {
|
|
90
96
|
let msg;
|
|
91
97
|
if (tagName.trim().length === 0) {
|
|
92
98
|
msg = "Invalid space after '<'.";
|
|
@@ -107,7 +113,7 @@ export function validate(xmlData, options) {
|
|
|
107
113
|
//self closing tag
|
|
108
114
|
const attrStrStart = i - attrStr.length;
|
|
109
115
|
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
110
|
-
const isValid = validateAttributeString(attrStr, options, xmlVersion);
|
|
116
|
+
const isValid = validateAttributeString(attrStr, options, qNameValidator, xmlVersion);
|
|
111
117
|
if (isValid === true) {
|
|
112
118
|
if (reachedRoot === true) {
|
|
113
119
|
throwError('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, tagStartPos));
|
|
@@ -147,7 +153,7 @@ export function validate(xmlData, options) {
|
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
} else {
|
|
150
|
-
const isValid = validateAttributeString(attrStr, options, xmlVersion);
|
|
156
|
+
const isValid = validateAttributeString(attrStr, options, qNameValidator, xmlVersion);
|
|
151
157
|
if (isValid !== true) {
|
|
152
158
|
throwError(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
|
153
159
|
}
|
|
@@ -177,7 +183,7 @@ export function validate(xmlData, options) {
|
|
|
177
183
|
continue;
|
|
178
184
|
} else if (xmlData[i + 1] === '?') {
|
|
179
185
|
i += 2;
|
|
180
|
-
i = readPI(xmlData, i,
|
|
186
|
+
i = readPI(xmlData, i, nameValidator);
|
|
181
187
|
if (i.err) return i;
|
|
182
188
|
} else {
|
|
183
189
|
break;
|
|
@@ -225,7 +231,7 @@ function isWhiteSpace(char) {
|
|
|
225
231
|
return char === ' ' || char === '\t' || char === '\n' || char === '\r';
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
function readPI(xmlData, i,
|
|
234
|
+
function readPI(xmlData, i, nameValidator) {
|
|
229
235
|
const piStart = i; // points just after '<?'
|
|
230
236
|
const nameStart = i;
|
|
231
237
|
let nameValidated = false;
|
|
@@ -244,7 +250,7 @@ function readPI(xmlData, i, xmlVersion = '1.0') {
|
|
|
244
250
|
'XML declaration allowed only at the start of the document.',
|
|
245
251
|
getLineNumberForPosition(xmlData, piStart - 2) // point at '<'
|
|
246
252
|
);
|
|
247
|
-
} else if (!
|
|
253
|
+
} else if (!nameValidator(piName)) {
|
|
248
254
|
// PI target must be a valid XML Name (XML 1.0 §2.6)
|
|
249
255
|
throwError(
|
|
250
256
|
'InvalidXml',
|
|
@@ -366,7 +372,7 @@ function readAttributeStr(xmlData, i) {
|
|
|
366
372
|
|
|
367
373
|
const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
|
368
374
|
|
|
369
|
-
function validateAttributeString(attrStr, options, xmlVersion) {
|
|
375
|
+
function validateAttributeString(attrStr, options, qNameValidator, xmlVersion) {
|
|
370
376
|
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
|
371
377
|
const attrNames = {};
|
|
372
378
|
|
|
@@ -383,7 +389,7 @@ function validateAttributeString(attrStr, options, xmlVersion) {
|
|
|
383
389
|
|
|
384
390
|
//validate attribute names as QNames so namespaced attributes
|
|
385
391
|
// (e.g. xml:lang, xmlns:xsi) are accepted and malformed ones are rejected.
|
|
386
|
-
if (!
|
|
392
|
+
if (!qNameValidator(attrName)) {
|
|
387
393
|
return { err: { code: 'InvalidAttr', msg: "Attribute '" + attrName + "' is an invalid name.", line: getPositionFromMatch(matches[i]) } };
|
|
388
394
|
}
|
|
389
395
|
|
|
@@ -441,18 +447,6 @@ function throwError(code, message, lineNumber) {
|
|
|
441
447
|
throw new ValidationError(message, code, lineNumber.line || lineNumber, lineNumber.col);
|
|
442
448
|
}
|
|
443
449
|
|
|
444
|
-
/**
|
|
445
|
-
* Validate an XML QName (Namespaces in XML 1.0 §2.3).
|
|
446
|
-
* Delegates to xml-naming's qName production, which enforces:
|
|
447
|
-
* - Non-empty input.
|
|
448
|
-
* - At most one colon (used as prefix separator).
|
|
449
|
-
* - Neither prefix nor local part may be empty (:foo, ns:, : all rejected).
|
|
450
|
-
* - Both parts must satisfy NCName character rules.
|
|
451
|
-
*/
|
|
452
|
-
function validateQName(str, xmlVersion = '1.0') {
|
|
453
|
-
return qName(str, { xmlVersion });
|
|
454
|
-
}
|
|
455
|
-
|
|
456
450
|
function getLineNumberForPosition(xmlData, index) {
|
|
457
451
|
const lines = xmlData.substring(0, index).split(/\r?\n/);
|
|
458
452
|
return {
|