Validate errorInfo.fixInfo object/properties in extension calls to onError.

This commit is contained in:
David Anson 2019-09-14 13:39:27 -07:00
parent 5895ea62cb
commit 65f6d38978
3 changed files with 95 additions and 22 deletions

View file

@ -44,6 +44,11 @@ module.exports.isEmptyString = function isEmptyString(str) {
return str.length === 0; return str.length === 0;
}; };
// Returns true iff the input is an object
module.exports.isObject = function isObject(obj) {
return (obj !== null) && (typeof obj === "object") && !Array.isArray(obj);
};
// Returns true iff the input line is blank (no content) // Returns true iff the input line is blank (no content)
// Example: Contains nothing, whitespace, or comments // Example: Contains nothing, whitespace, or comments
const blankLineRe = />|(?:<!--.*?-->)/g; const blankLineRe = />|(?:<!--.*?-->)/g;

View file

@ -382,6 +382,37 @@ function lintContent(
lines[errorInfo.lineNumber - 1].length))) { lines[errorInfo.lineNumber - 1].length))) {
throwError("range"); throwError("range");
} }
const fixInfo = errorInfo.fixInfo;
if (fixInfo) {
if (!helpers.isObject(fixInfo)) {
throwError("fixInfo");
}
if ((fixInfo.lineNumber !== undefined) &&
(!helpers.isNumber(fixInfo.lineNumber) ||
(fixInfo.lineNumber < 1) ||
(fixInfo.lineNumber > lines.length))) {
throwError("fixInfo.lineNumber");
}
const effectiveLineNumber = fixInfo.lineNumber || errorInfo.lineNumber;
if ((fixInfo.editColumn !== undefined) &&
(!helpers.isNumber(fixInfo.editColumn) ||
(fixInfo.editColumn < 1) ||
(fixInfo.editColumn >
lines[effectiveLineNumber - 1].length + 1))) {
throwError("fixInfo.editColumn");
}
if ((fixInfo.deleteCount !== undefined) &&
(!helpers.isNumber(fixInfo.deleteCount) ||
(fixInfo.deleteCount < -1) ||
(fixInfo.deleteCount >
lines[effectiveLineNumber - 1].length))) {
throwError("fixInfo.deleteCount");
}
if ((fixInfo.insertText !== undefined) &&
!helpers.isString(fixInfo.insertText)) {
throwError("fixInfo.insertText");
}
}
errors.push({ errors.push({
"lineNumber": errorInfo.lineNumber + frontMatterLines.length, "lineNumber": errorInfo.lineNumber + frontMatterLines.length,
"detail": errorInfo.detail || null, "detail": errorInfo.detail || null,

View file

@ -3177,19 +3177,32 @@ module.exports.customRulesOnErrorNull = function customRulesOnErrorNull(test) {
}; };
module.exports.customRulesOnErrorBad = function customRulesOnErrorBad(test) { module.exports.customRulesOnErrorBad = function customRulesOnErrorBad(test) {
test.expect(44); test.expect(84);
[ [
[ "lineNumber", [ null, "string" ] ], [ "lineNumber", null, [ null, "string" ] ],
[ "detail", [ 10, [] ] ], [ "detail", null, [ 10, [] ] ],
[ "context", [ 10, [] ] ], [ "context", null, [ 10, [] ] ],
[ "range", [ 10, [], [ 10 ], [ 10, null ], [ 10, 11, 12 ] ] ] [ "range", null, [ 10, [], [ 10 ], [ 10, null ], [ 10, 11, 12 ] ] ],
[ "fixInfo", null, [ 10, "string" ] ],
[ "fixInfo", "lineNumber", [ null, "string" ] ],
[ "fixInfo", "editColumn", [ null, "string" ] ],
[ "fixInfo", "deleteCount", [ null, "string" ] ],
[ "fixInfo", "insertText", [ 10, [] ] ]
].forEach(function forProperty(property) { ].forEach(function forProperty(property) {
const propertyName = property[0]; const [ propertyName, subPropertyName, propertyValues ] = property;
property[1].forEach(function forPropertyValue(propertyValue) { propertyValues.forEach(function forPropertyValue(propertyValue) {
const badObject = { const badObject = {
"lineNumber": 1 "lineNumber": 1
}; };
let propertyNames = null;
if (subPropertyName) {
badObject[propertyName] = {};
badObject[propertyName][subPropertyName] = propertyValue;
propertyNames = `${propertyName}.${subPropertyName}`;
} else {
badObject[propertyName] = propertyValue; badObject[propertyName] = propertyValue;
propertyNames = propertyName;
}
const options = { const options = {
"customRules": [ "customRules": [
{ {
@ -3211,7 +3224,7 @@ module.exports.customRulesOnErrorBad = function customRulesOnErrorBad(test) {
test.ok(err, "Did not get an error for bad object."); test.ok(err, "Did not get an error for bad object.");
test.ok(err instanceof Error, "Error not instance of Error."); test.ok(err instanceof Error, "Error not instance of Error.");
test.equal(err.message, test.equal(err.message,
"Property '" + propertyName + "' of onError parameter is incorrect.", "Property '" + propertyNames + "' of onError parameter is incorrect.",
"Incorrect message for bad object."); "Incorrect message for bad object.");
return true; return true;
}, "Did not get exception for bad object."); }, "Did not get exception for bad object.");
@ -3222,17 +3235,28 @@ module.exports.customRulesOnErrorBad = function customRulesOnErrorBad(test) {
module.exports.customRulesOnErrorInvalid = module.exports.customRulesOnErrorInvalid =
function customRulesOnErrorInvalid(test) { function customRulesOnErrorInvalid(test) {
test.expect(36); test.expect(68);
[ [
[ "lineNumber", [ -1, 0, 3, 4 ] ], [ "lineNumber", null, [ -1, 0, 3, 4 ] ],
[ "range", [ [ 0, 1 ], [ 1, 0 ], [ 5, 1 ], [ 1, 5 ], [ 4, 2 ] ] ] [ "range", null, [ [ 0, 1 ], [ 1, 0 ], [ 5, 1 ], [ 1, 5 ], [ 4, 2 ] ] ],
[ "fixInfo", "lineNumber", [ -1, 0, 3, 4 ] ],
[ "fixInfo", "editColumn", [ 0, 6 ] ],
[ "fixInfo", "deleteCount", [ -2, 5 ] ]
].forEach(function forProperty(property) { ].forEach(function forProperty(property) {
const propertyName = property[0]; const [ propertyName, subPropertyName, propertyValues ] = property;
property[1].forEach(function forPropertyValue(propertyValue) { propertyValues.forEach(function forPropertyValue(propertyValue) {
const badObject = { const badObject = {
"lineNumber": 1 "lineNumber": 1
}; };
let propertyNames = null;
if (subPropertyName) {
badObject[propertyName] = {};
badObject[propertyName][subPropertyName] = propertyValue;
propertyNames = `${propertyName}.${subPropertyName}`;
} else {
badObject[propertyName] = propertyValue; badObject[propertyName] = propertyValue;
propertyNames = propertyName;
}
const options = { const options = {
"customRules": [ "customRules": [
{ {
@ -3254,7 +3278,7 @@ module.exports.customRulesOnErrorInvalid =
test.ok(err, "Did not get an error for invalid object."); test.ok(err, "Did not get an error for invalid object.");
test.ok(err instanceof Error, "Error not instance of Error."); test.ok(err instanceof Error, "Error not instance of Error.");
test.equal(err.message, test.equal(err.message,
`Property '${propertyName}' of onError parameter is incorrect.`, `Property '${propertyNames}' of onError parameter is incorrect.`,
"Incorrect message for invalid object."); "Incorrect message for invalid object.");
return true; return true;
}, "Did not get exception for invalid object."); }, "Did not get exception for invalid object.");
@ -3265,17 +3289,30 @@ module.exports.customRulesOnErrorInvalid =
module.exports.customRulesOnErrorValid = module.exports.customRulesOnErrorValid =
function customRulesOnErrorValid(test) { function customRulesOnErrorValid(test) {
test.expect(7); test.expect(24);
[ [
[ "lineNumber", [ 1, 2 ] ], [ "lineNumber", null, [ 1, 2 ] ],
[ "range", [ [ 1, 1 ], [ 1, 4 ], [ 2, 2 ], [ 3, 2 ], [ 4, 1 ] ] ] [ "range", null, [ [ 1, 1 ], [ 1, 4 ], [ 2, 2 ], [ 3, 2 ], [ 4, 1 ] ] ],
[ "fixInfo", "lineNumber", [ 1, 2 ] ],
[ "fixInfo", "editColumn", [ 1, 2, 4, 5 ] ],
[ "fixInfo", "deleteCount", [ -1, 0, 1, 4 ] ],
[
"fixInfo",
"insertText",
[ "", "1", "123456", "\n", "\nText", "Text\n", "\nText\n" ]
]
].forEach(function forProperty(property) { ].forEach(function forProperty(property) {
const propertyName = property[0]; const [ propertyName, subPropertyName, propertyValues ] = property;
property[1].forEach(function forPropertyValue(propertyValue) { propertyValues.forEach(function forPropertyValue(propertyValue) {
const goodObject = { const goodObject = {
"lineNumber": 1 "lineNumber": 1
}; };
if (subPropertyName) {
goodObject[propertyName] = {};
goodObject[propertyName][subPropertyName] = propertyValue;
} else {
goodObject[propertyName] = propertyValue; goodObject[propertyName] = propertyValue;
}
const options = { const options = {
"customRules": [ "customRules": [
{ {