Skip to content

Commit

Permalink
More validation to ensure expression does not contain any unexpected …
Browse files Browse the repository at this point in the history
…characters (#331)
  • Loading branch information
bradymholt authored Jan 15, 2025
1 parent f387219 commit 30179d0
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 12 deletions.
29 changes: 20 additions & 9 deletions src/cronParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class CronParser {
*/
parse(): string[] {
let parsed: string[];

var expression = this.expression ?? '';

if (expression.startsWith('@')) {
Expand Down Expand Up @@ -316,12 +316,22 @@ export class CronParser {
}

protected validate(parsed: string[]) {
this.assertNoInvalidCharacters("DOW", parsed[5]);
this.assertNoInvalidCharacters("DOM", parsed[3]);
this.validateRange(parsed);
const standardCronPartCharacters = "0-9,\\-*\/";
this.validateOnlyExpectedCharactersFound(parsed[0], standardCronPartCharacters);
this.validateOnlyExpectedCharactersFound(parsed[1], standardCronPartCharacters);
this.validateOnlyExpectedCharactersFound(parsed[2], standardCronPartCharacters);
// DOM
this.validateOnlyExpectedCharactersFound(parsed[3], "0-9,\\-*\/LW");
this.validateOnlyExpectedCharactersFound(parsed[4], standardCronPartCharacters);
// DOW
this.validateOnlyExpectedCharactersFound(parsed[5], "0-9,\\-*\/L#");
this.validateOnlyExpectedCharactersFound(parsed[6], standardCronPartCharacters);

this.validateAnyRanges(parsed);

}

protected validateRange(parsed: string[]) {
protected validateAnyRanges(parsed: string[]) {
RangeValidator.secondRange(parsed[0]);
RangeValidator.minuteRange(parsed[1]);
RangeValidator.hourRange(parsed[2]);
Expand All @@ -330,11 +340,12 @@ export class CronParser {
RangeValidator.dayOfWeekRange(parsed[5], this.dayOfWeekStartIndexZero);
}

protected assertNoInvalidCharacters(partDescription: string, expression: string) {
// No characters other than 'L' or 'W' should remain after normalization
let invalidChars = expression.match(/[A-KM-VX-Z]+/gi);
protected validateOnlyExpectedCharactersFound(cronPart: string, allowedCharsExpression: string) {
// Write code that will ensure the expression string only contains numbers or any of the following characters , - * /
// If any other characters are found, it is an error.
let invalidChars = cronPart.match(new RegExp(`[^${allowedCharsExpression}]+`, "gi"));
if (invalidChars && invalidChars.length) {
throw new Error(`${partDescription} part contains invalid values: '${invalidChars.toString()}'`);
throw new Error(`Expression contains invalid values: '${invalidChars.toString()}'`);
}
}
}
4 changes: 2 additions & 2 deletions src/expressionDescriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ export class ExpressionDescriptor {
protected formatTime(hourExpression: string, minuteExpression: string, secondExpression: string) {
let hourOffset: number = 0;
let minuteOffset: number = 0;

if(this.options.tzOffset) {
hourOffset = this.options.tzOffset > 0 ? Math.floor(this.options.tzOffset) : Math.ceil(this.options.tzOffset);

Expand All @@ -674,7 +674,7 @@ export class ExpressionDescriptor {
minute += 60;
hour -= 1;
}

if (hour >= 24) {
hour = hour - 24;
} else if (hour < 0) {
Expand Down
16 changes: 15 additions & 1 deletion test/cronParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,21 @@ describe("CronParser", function () {
it("should error if DOW part is not valid", function () {
assert.throws(function () {
new CronParser("* * * * MO").parse();
}, `DOW part contains invalid values: 'MO'`);
}, `Expression contains invalid values: 'MO'`);
});

it("does not allow unexpected characters or statements in any part", function () {
const maliciousStatement = "\nDROP\tDATABASE\tusers;";

for(let i = 0; i <= 6; i++) {
const cleanCronParts = ["*", "*", "*", "*", "*", "*", "*"];
cleanCronParts[i] = maliciousStatement;
const cronToTest = cleanCronParts.join(" ");
assert.throws(function () {
new CronParser(cronToTest).parse();
}, `Expression contains invalid values:`);
}

});

it("should parse cron with multiple spaces between parts", function () {
Expand Down
6 changes: 6 additions & 0 deletions test/cronstrue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,12 @@ describe("Cronstrue", function () {
}, "Error: Expression has only 1 part. At least 5 parts are required.");
});

it("too many parts", function () {
assert.throws(function () {
cronstrue.toString("* * * * * * * *");
}, "Expression has 8 parts; too many!");
});

it("empty expression", function () {
assert.throws(function () {
cronstrue.toString("");
Expand Down

0 comments on commit 30179d0

Please sign in to comment.