Skip to content

Commit d36575f

Browse files
committed
Validator: Fail for stray ampersand characters
Fixes #214
1 parent 156d02f commit d36575f

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

spec/validator_spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,23 @@ attribute2="attribute2"
525525
var result = validator.validate(xmlData).err;
526526
expect(result).toEqual(expected);
527527
});
528+
529+
it('should validate value with ampersand', function () {
530+
const error = {
531+
InvalidChar: "char '&' is not expected."
532+
};
533+
validate('<rootNode>jekyll &amp; hyde</rootNode>');
534+
validate('<rootNode>jekyll &#123; hyde</rootNode>');
535+
validate('<rootNode>jekyll &#x1945abcdef; hyde</rootNode>');
536+
validate('<rootNode>jekyll &#x1ah; hyde</rootNode>', error);
537+
validate('<rootNode>jekyll &#1a; hyde</rootNode>', error);
538+
validate('<rootNode>jekyll &#123 hyde</rootNode>', error);
539+
validate('<rootNode>jekyll &#1abcd hyde</rootNode>', error);
540+
validate('<rootNode>jekyll & hyde</rootNode>', error);
541+
validate('<rootNode>jekyll &aa</rootNode>', error);
542+
validate('<rootNode>jekyll &abcdefghij1234567890;</rootNode>');
543+
validate('<rootNode>jekyll &abcdefghij1234567890a;</rootNode>', error); // limit to 20 chars
544+
});
528545
});
529546

530547
describe("should not validate XML documents with multiple root nodes", () => {

src/validator.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ exports.validate = function (xmlData, options) {
155155
} else {
156156
break;
157157
}
158+
} else if (xmlData[i] === '&') {
159+
const afterAmp = validateAmpersand(xmlData, i);
160+
if (afterAmp == -1)
161+
return getErrorObject('InvalidChar', `char '&' is not expected.`, getLineNumberForPosition(xmlData, i));
162+
i = afterAmp;
158163
}
159164
} //end of reading tag text value
160165
if (xmlData[i] === '<') {
@@ -332,6 +337,41 @@ function validateAttributeString(attrStr, options, regxAttrName) {
332337
return true;
333338
}
334339

340+
function validateNumberAmpersand(xmlData, i) {
341+
let re = /\d/;
342+
if (xmlData[i] === 'x') {
343+
i++;
344+
re = /[\da-fA-F]/;
345+
}
346+
for (; i < xmlData.length; i++) {
347+
if (xmlData[i] === ';')
348+
return i;
349+
if (!xmlData[i].match(re))
350+
break;
351+
}
352+
return -1;
353+
}
354+
355+
function validateAmpersand(xmlData, i) {
356+
// https://www.w3.org/TR/xml/#dt-charref
357+
i++;
358+
if (xmlData[i] === ';')
359+
return -1;
360+
if (xmlData[i] === '#') {
361+
i++;
362+
return validateNumberAmpersand(xmlData, i);
363+
}
364+
let count = 0;
365+
for (; i < xmlData.length; i++, count++) {
366+
if (xmlData[i].match(/\w/) && count < 20)
367+
continue;
368+
if (xmlData[i] === ';')
369+
break;
370+
return -1;
371+
}
372+
return i;
373+
}
374+
335375
function getErrorObject(code, message, lineNumber) {
336376
return {
337377
err: {

0 commit comments

Comments
 (0)