diff --git a/src/lib/clientTools.js b/src/lib/clientTools.js index 9dae7f3..345feac 100644 --- a/src/lib/clientTools.js +++ b/src/lib/clientTools.js @@ -149,7 +149,121 @@ function IsValidNameServer(nameServer) { const IsValidURL = function (url) { return /^(http|https):\/\/[^ "]+$/.test(url); }; +function ValidateCronExpression(cronExp) { + // Check if expression is provided and is a string + if (!cronExp || typeof cronExp !== "string") { + return { isValid: false, message: "Cron expression must be a non-empty string" }; + } + // Split the expression into its components + const fields = cronExp.trim().split(/\s+/); + + // Standard cron should have 5 or 6 fields + // minute hour day-of-month month day-of-week [year] + if (fields.length < 5 || fields.length > 6) { + return { + isValid: false, + message: "Cron expression must have 5 or 6 fields" + }; + } + + // Define field constraints + const fieldConstraints = [ + { name: "minute", min: 0, max: 59 }, + { name: "hour", min: 0, max: 23 }, + { name: "day", min: 1, max: 31 }, + { name: "month", min: 1, max: 12 }, + { name: "weekday", min: 0, max: 6 }, + { name: "year", min: 1970, max: 2099 } // Optional field + ]; + + // Valid characters in cron expressions + const validChars = /^[\d/*,\-]+$/; + + // Validate each field + for (let i = 0; i < fields.length; i++) { + const field = fields[i]; + const constraint = fieldConstraints[i]; + + // Check for valid characters + if (!validChars.test(field)) { + return { + isValid: false, + message: `Invalid characters in ${constraint.name} field` + }; + } + + // Handle special characters + if (field === "*") { + continue; // Asterisk is valid for all fields + } + + // Handle lists (comma-separated values) + if (field.includes(",")) { + const values = field.split(","); + for (const value of values) { + if (!isValidRange(value, constraint.min, constraint.max)) { + return { + isValid: false, + message: `Invalid value in ${constraint.name} field: ${value}` + }; + } + } + continue; + } + + // Handle ranges (with hyphens) + if (field.includes("-")) { + const [start, end] = field.split("-").map(Number); + if ( + start == null || + end == null || + start < constraint.min || + end > constraint.max || + start > end + ) { + return { + isValid: false, + message: `Invalid range in ${constraint.name} field: ${field}` + }; + } + continue; + } + + // Handle steps (with forward slash) + if (field.includes("/")) { + const [range, step] = field.split("/"); + if (range !== "*" && !isValidRange(range, constraint.min, constraint.max)) { + return { + isValid: false, + message: `Invalid range in ${constraint.name} field: ${range}` + }; + } + if (!isValidRange(step, 1, constraint.max)) { + return { + isValid: false, + message: `Invalid step value in ${constraint.name} field: ${step}` + }; + } + continue; + } + + // Handle plain numbers + if (!isValidRange(field, constraint.min, constraint.max)) { + return { + isValid: false, + message: `Invalid value in ${constraint.name} field: ${field}` + }; + } + } + + return { isValid: true, message: "Valid cron expression" }; +} + +function isValidRange(value, min, max) { + const num = Number(value); + return !isNaN(num) && num >= min && num <= max; +} export { siteDataExtractFromDb, storeSiteData, @@ -157,5 +271,6 @@ export { ValidateIpAddress, IsValidHost, IsValidNameServer, - IsValidURL + IsValidURL, + ValidateCronExpression }; diff --git a/src/lib/components/manage/monitorSheet.svelte b/src/lib/components/manage/monitorSheet.svelte index d9786df..76c725b 100644 --- a/src/lib/components/manage/monitorSheet.svelte +++ b/src/lib/components/manage/monitorSheet.svelte @@ -11,6 +11,7 @@ import { allRecordTypes, ValidateIpAddress, + ValidateCronExpression, IsValidHost, IsValidNameServer } from "$lib/clientTools.js"; @@ -130,13 +131,11 @@ //validating cron const cron = newMonitor.cron; - const cronRegex = - /^((\*|([0-5]?\d)(-[0-5]?\d)?(\/[1-9]\d*)?|([0-5]?\d(,[0-5]?\d)*))\s+){4}(\*|([0-7](,[0-7])*|([0-7]-[0-7])))$/; - if (!cronRegex.test(cron)) { - invalidFormMessage = "Invalid Cron"; + let cronValidation = ValidateCronExpression(cron); + if (cronValidation.isValid === false) { + invalidFormMessage = "Cron invalid: " + cronValidation.message; return; } - //if monitor type is API if (newMonitor.monitor_type === "API") { //validating url