User input
User input handling in RouterOS enables creation of interactive scripts, configuration wizards, and dynamic automation tools that adapt to user requirements.
Input methods overview
Script parameter passing
# Script with parameters - accessed via $1, $2, etc.
/system script add name=user-management source={
# Parameters: $1=username, $2=password, $3=group
:local userName $1;
:local userPassword $2;
:local userGroup $3;
# Validate parameters
:if ([:len $userName] = 0) do={
/log error "Username parameter is required";
:error "Missing username parameter";
};
:if ([:len $userPassword] = 0) do={
/log error "Password parameter is required";
:error "Missing password parameter";
};
# Set default group if not provided
:if ([:len $userGroup] = 0) do={
:set userGroup "read";
};
# Create user with provided parameters
/user add name=$userName password=$userPassword group=$userGroup;
/log info ("User " . $userName . " created with group " . $userGroup);
}
# Execute script with parameters
# /system script run user-management "john" "password123" "full"Environment variable access
# Accessing system and environment information
/system script add name=environment-info source={
# Get system identity
:local systemName [/system identity get name];
:local currentTime [/system clock get time];
:local currentDate [/system clock get date];
# Get current user context (limited in RouterOS)
:local routerboardInfo [/system routerboard get];
:local firmwareVersion ($routerboardInfo->"current-firmware");
# Display environment information
/log info ("System: " . $systemName);
/log info ("Current time: " . $currentDate . " " . $currentTime);
/log info ("Firmware: " . $firmwareVersion);
# Get interface information as "environment"
:local interfaceCount [/interface print count-only];
/log info ("Total interfaces: " . $interfaceCount);
# Get network configuration as input context
:local ipAddresses [/ip address print as-value];
:foreach addr in=$ipAddresses do={
:local interface ($addr->"interface");
:local address ($addr->"address");
/log info ("Interface " . $interface . ": " . $address);
};
}Interactive user prompts
Simple yes/no prompts
# Interactive confirmation script
/system script add name=interactive-reboot source={
# Simulate interactive prompt (RouterOS doesn't have built-in prompts)
# This would typically be handled by external script or manual execution
# Create a simple confirmation mechanism using global variables
:global userConfirmation;
# Check if confirmation was provided
:if ([:typeof $userConfirmation] = "nothing") do={
/log info "Please set global variable 'userConfirmation' to 'yes' to proceed";
/log info "Example: :global userConfirmation yes";
/log info "Then run the script again";
:error "User confirmation required";
};
:if ($userConfirmation = "yes") do={
/log info "User confirmed - proceeding with reboot";
# Clear the confirmation
:set userConfirmation;
/system reboot;
} else={
/log info "Reboot cancelled by user";
:set userConfirmation;
};
}
# Usage pattern:
# :global userConfirmation yes
# /system script run interactive-rebootConfiguration wizard simulation
# Configuration wizard using global variables for input
/system script add name=wifi-setup-wizard source={
# Input variables (set by user before running script)
:global wifiSSID;
:global wifiPassword;
:global wifiChannel;
:global wifiCountry;
# Default values
:if ([:typeof $wifiSSID] = "nothing") do={ :set wifiSSID "MyNetwork"; };
:if ([:typeof $wifiPassword] = "nothing") do={ :set wifiPassword ""; };
:if ([:typeof $wifiChannel] = "nothing") do={ :set wifiChannel "auto"; };
:if ([:typeof $wifiCountry] = "nothing") do={ :set wifiCountry "united_states"; };
/log info "WiFi Setup Wizard Starting...";
/log info ("SSID: " . $wifiSSID);
/log info ("Channel: " . $wifiChannel);
/log info ("Country: " . $wifiCountry);
# Validate input
:if ([:len $wifiSSID] = 0) do={
/log error "WiFi SSID cannot be empty";
:error "Invalid SSID";
};
:if ([:len $wifiPassword] < 8) do={
/log error "WiFi password must be at least 8 characters";
:error "Invalid password";
};
# Configure WiFi (example for new WiFi package)
:do {
# Create security profile
/interface wifi security add name=wizard-security \
authentication-types=wpa2-psk,wpa3-psk \
encryption=ccmp \
passphrase=$wifiPassword;
# Configure WiFi interface
/interface wifi set wifi1 \
disabled=no \
channel=$wifiChannel \
country=$wifiCountry;
# Create WiFi network
/interface wifi add name=wifi-home \
master-interface=wifi1 \
ssid=$wifiSSID \
security=wizard-security \
disabled=no;
/log info "WiFi configuration completed successfully";
# Clear input variables
:set wifiSSID;
:set wifiPassword;
:set wifiChannel;
:set wifiCountry;
} on-error={
/log error "WiFi configuration failed";
};
}
# Usage example:
# :global wifiSSID "MyHomeNetwork"
# :global wifiPassword "SuperSecure123!"
# :global wifiChannel 36
# :global wifiCountry "united_states"
# /system script run wifi-setup-wizardInput validation and sanitization
Basic input validation
# Comprehensive input validation functions
/system script add name=input-validation-demo source={
# Input validation functions
:local validateIP do={
:local ipAddress $1;
# Basic IP address format validation
:if ([:len $ipAddress] = 0) do={
:return false;
};
# Check for valid IP format (simplified)
:local dotCount 0;
:local i 0;
:while ($i < [:len $ipAddress]) do={
:local char [:pick $ipAddress $i ($i + 1)];
:if ($char = ".") do={
:set dotCount ($dotCount + 1);
};
:set i ($i + 1);
};
# Should have exactly 3 dots for IPv4
:return ($dotCount = 3);
};
:local validatePort do={
:local port $1;
# Convert string to number if needed
:local portNum $port;
:if ([:typeof $port] = "str") do={
:do {
:set portNum [:tonum $port];
} on-error={
:return false;
};
};
# Check port range (1-65535)
:return ($portNum >= 1 && $portNum <= 65535);
};
:local validateSSID do={
:local ssid $1;
# SSID validation rules
:if ([:len $ssid] = 0 || [:len $ssid] > 32) do={
:return false;
};
# Check for invalid characters (simplified)
:if ($ssid ~ "[\x00-\x1F]") do={
:return false; # Control characters not allowed
};
:return true;
};
:local validatePassword do={
:local password $1;
:local minLength $2;
# Set default minimum length
:if ([:len $minLength] = 0) do={ :set minLength 8; };
# Length check
:if ([:len $password] < $minLength) do={
:return false;
};
# Check for at least one number (simplified)
:local hasNumber ($password ~ ".*[0-9].*");
# Check for at least one letter (simplified)
:local hasLetter ($password ~ ".*[A-Za-z].*");
:return ($hasNumber && $hasLetter);
};
# Test the validation functions
:local testIP "192.168.1.1";
:local testPort "8080";
:local testSSID "MyNetwork";
:local testPassword "SecurePass123";
:local ipValid [$validateIP $testIP];
:local portValid [$validatePort $testPort];
:local ssidValid [$validateSSID $testSSID];
:local passwordValid [$validatePassword $testPassword 8];
/log info ("IP " . $testIP . " is valid: " . $ipValid);
/log info ("Port " . $testPort . " is valid: " . $portValid);
/log info ("SSID '" . $testSSID . "' is valid: " . $ssidValid);
/log info ("Password is valid: " . $passwordValid);
}Input sanitization
# Input sanitization and cleaning functions
/system script add name=input-sanitization source={
# String sanitization function
:local sanitizeString do={
:local input $1;
:local allowedChars $2; # Optional: specify allowed characters
:local cleaned $input;
# Remove dangerous characters for command injection prevention
:set cleaned [:replace $cleaned ";" ""];
:set cleaned [:replace $cleaned "|" ""];
:set cleaned [:replace $cleaned "&" ""];
:set cleaned [:replace $cleaned "`" ""];
:set cleaned [:replace $cleaned "\$" ""];
:set cleaned [:replace $cleaned "'" ""];
:set cleaned [:replace $cleaned "\"" ""];
# Remove control characters
:set cleaned [:replace $cleaned "\n" ""];
:set cleaned [:replace $cleaned "\r" ""];
:set cleaned [:replace $cleaned "\t" " "];
# Trim whitespace (simplified)
:while ([:pick $cleaned 0 1] = " ") do={
:set cleaned [:pick $cleaned 1 [:len $cleaned]];
};
:while ([:pick $cleaned ([:len $cleaned] - 1) [:len $cleaned]] = " ") do={
:set cleaned [:pick $cleaned 0 ([:len $cleaned] - 1)];
};
:return $cleaned;
};
# Filename sanitization
:local sanitizeFilename do={
:local filename $1;
:local cleaned $filename;
# Remove invalid filename characters
:set cleaned [:replace $cleaned "/" "_"];
:set cleaned [:replace $cleaned "\\" "_"];
:set cleaned [:replace $cleaned ":" "_"];
:set cleaned [:replace $cleaned "*" "_"];
:set cleaned [:replace $cleaned "?" "_"];
:set cleaned [:replace $cleaned "\"" "_"];
:set cleaned [:replace $cleaned "<" "_"];
:set cleaned [:replace $cleaned ">" "_"];
:set cleaned [:replace $cleaned "|" "_"];
# Limit length
:if ([:len $cleaned] > 50) do={
:set cleaned [:pick $cleaned 0 50];
};
:return $cleaned;
};
# IP address normalization
:local normalizeIP do={
:local ip $1;
:local normalized $ip;
# Remove whitespace
:set normalized [:replace $normalized " " ""];
# Validate basic format
:if (!($normalized ~ "^[0-9.]+$")) do={
:return ""; # Invalid format
};
:return $normalized;
};
# Username sanitization for system accounts
:local sanitizeUsername do={
:local username $1;
:local cleaned $username;
# Convert to lowercase
:set cleaned [:replace $cleaned "A" "a"];
:set cleaned [:replace $cleaned "B" "b"];
:set cleaned [:replace $cleaned "C" "c"];
# ... (simplified, full implementation would handle all letters)
# Remove invalid characters for usernames
:set cleaned [:replace $cleaned " " "_"];
:set cleaned [:replace $cleaned "-" "_"];
:set cleaned [:replace $cleaned "." "_"];
# Ensure it starts with a letter (add prefix if needed)
:if ([:pick $cleaned 0 1] ~ "[0-9]") do={
:set cleaned ("user_" . $cleaned);
};
# Limit length
:if ([:len $cleaned] > 20) do={
:set cleaned [:pick $cleaned 0 20];
};
:return $cleaned;
};
# Test sanitization functions
:local dangerousInput "test; /system reboot & dangerous";
:local messyFilename "My File/Name: Test*.txt";
:local malformedIP " 192.168.1.1 ";
:local weirdUsername "123-John.Doe@Company";
:local cleanString [$sanitizeString $dangerousInput];
:local cleanFilename [$sanitizeFilename $messyFilename];
:local cleanIP [$normalizeIP $malformedIP];
:local cleanUsername [$sanitizeUsername $weirdUsername];
/log info ("Original: '" . $dangerousInput . "'");
/log info ("Cleaned: '" . $cleanString . "'");
/log info ("Filename: '" . $messyFilename . "' -> '" . $cleanFilename . "'");
/log info ("IP: '" . $malformedIP . "' -> '" . $cleanIP . "'");
/log info ("Username: '" . $weirdUsername . "' -> '" . $cleanUsername . "'");
}Parameter processing patterns
Command-line style arguments
# Command-line argument parsing simulation
/system script add name=arg-parser-demo source={
# Simulate command line: script-name --host=192.168.1.1 --port=8080 --enable
# Parameters passed as single string and parsed
:local parseArguments do={
:local argString $1;
:local args {};
# Split arguments by spaces (simplified parser)
:local parts {};
:local currentArg "";
:local i 0;
# Simple space-based splitting
:while ($i < [:len $argString]) do={
:local char [:pick $argString $i ($i + 1)];
:if ($char = " ") do={
:if ([:len $currentArg] > 0) do={
:set parts ($parts, $currentArg);
:set currentArg "";
};
} else={
:set currentArg ($currentArg . $char);
};
:set i ($i + 1);
};
# Add last argument
:if ([:len $currentArg] > 0) do={
:set parts ($parts, $currentArg);
};
# Process each part
:foreach part in=$parts do={
:if ([:pick $part 0 2] = "--") do={
# Long option
:local optionPart [:pick $part 2 [:len $part]];
:local equalPos [:find $optionPart "="];
:if ([:typeof $equalPos] != "nothing") do={
# Option with value: --key=value
:local key [:pick $optionPart 0 $equalPos];
:local value [:pick $optionPart ($equalPos + 1) [:len $optionPart]];
:set ($args->$key) $value;
} else={
# Boolean option: --flag
:set ($args->$optionPart) true;
};
} else={
:if ([:pick $part 0 1] = "-") do={
# Short option (simplified)
:local shortOpt [:pick $part 1 [:len $part]];
:set ($args->$shortOpt) true;
};
};
};
:return $args;
};
# Example usage of argument parser
:local testArgs "--host=192.168.1.1 --port=8080 --verbose -f --enable";
:local parsedArgs [$parseArguments $testArgs];
# Access parsed arguments
:local host ($parsedArgs->"host");
:local port ($parsedArgs->"port");
:local verbose ($parsedArgs->"verbose");
:local forceMode ($parsedArgs->"f");
:local enabled ($parsedArgs->"enable");
/log info ("Host: " . $host);
/log info ("Port: " . $port);
/log info ("Verbose: " . $verbose);
/log info ("Force mode: " . $forceMode);
/log info ("Enabled: " . $enabled);
}Configuration file input
# Configuration file processing
/system script add name=config-file-processor source={
# Process configuration from file content
:local processConfigFile do={
:local configContent $1;
:local config {};
# Split content by lines (simplified)
:local lines {};
:local currentLine "";
:local i 0;
:while ($i < [:len $configContent]) do={
:local char [:pick $configContent $i ($i + 1)];
:if ($char = "\n") do={
:if ([:len $currentLine] > 0) do={
:set lines ($lines, $currentLine);
:set currentLine "";
};
} else={
:set currentLine ($currentLine . $char);
};
:set i ($i + 1);
};
# Add last line
:if ([:len $currentLine] > 0) do={
:set lines ($lines, $currentLine);
};
# Process each line
:foreach line in=$lines do={
# Skip comments and empty lines
:local trimmed $line;
# Basic trim (remove leading/trailing spaces)
:if ([:len $trimmed] > 0 && [:pick $trimmed 0 1] != "#") do={
:local equalPos [:find $trimmed "="];
:if ([:typeof $equalPos] != "nothing") do={
:local key [:pick $trimmed 0 $equalPos];
:local value [:pick $trimmed ($equalPos + 1) [:len $trimmed]];
# Remove quotes from value if present
:if ([:pick $value 0 1] = "\"" && [:pick $value ([:len $value] - 1) [:len $value]] = "\"") do={
:set value [:pick $value 1 ([:len $value] - 1)];
};
:set ($config->$key) $value;
};
};
};
:return $config;
};
# Example configuration content
:local sampleConfig "# Network Configuration\nhost_name=router1\nip_address=192.168.1.1\nsubnet_mask=255.255.255.0\ngateway=192.168.1.254\ndns_server=\"8.8.8.8\"\nenable_dhcp=true\n# End of config";
:local parsedConfig [$processConfigFile $sampleConfig];
# Use parsed configuration
:local hostName ($parsedConfig->"host_name");
:local ipAddress ($parsedConfig->"ip_address");
:local gateway ($parsedConfig->"gateway");
:local dnsServer ($parsedConfig->"dns_server");
:local enableDHCP ($parsedConfig->"enable_dhcp");
/log info ("Configuration loaded:");
/log info ("Host: " . $hostName);
/log info ("IP: " . $ipAddress);
/log info ("Gateway: " . $gateway);
/log info ("DNS: " . $dnsServer);
/log info ("DHCP: " . $enableDHCP);
}Secure input handling
Password and sensitive data
# Secure password and sensitive data handling
/system script add name=secure-input-handling source={
# Secure password validation
:local validateSecurePassword do={
:local password $1;
:local minLength 12;
:local maxLength 128;
# Length validation
:if ([:len $password] < $minLength) do={
:return "Password too short (minimum " . $minLength . " characters)";
};
:if ([:len $password] > $maxLength) do={
:return "Password too long (maximum " . $maxLength . " characters)";
};
# Complexity requirements
:local hasUpper ($password ~ ".*[A-Z].*");
:local hasLower ($password ~ ".*[a-z].*");
:local hasDigit ($password ~ ".*[0-9].*");
:local hasSpecial ($password ~ ".*[!@#\$%^&*(),.?\":{}|<>].*");
:local missingRequirements {};
:if (!$hasUpper) do={ :set missingRequirements ($missingRequirements, "uppercase letter"); };
:if (!$hasLower) do={ :set missingRequirements ($missingRequirements, "lowercase letter"); };
:if (!$hasDigit) do={ :set missingRequirements ($missingRequirements, "digit"); };
:if (!$hasSpecial) do={ :set missingRequirements ($missingRequirements, "special character"); };
:if ([:len $missingRequirements] > 0) do={
:local errorMsg "Password missing: ";
:foreach req in=$missingRequirements do={
:set errorMsg ($errorMsg . $req . ", ");
};
:return $errorMsg;
};
# Check for common weak patterns
:if ($password ~ ".*(123|abc|password|admin|qwerty).*") do={
:return "Password contains common weak patterns";
};
:return "valid";
};
# Secure credential storage (limited in RouterOS)
:local storeCredentials do={
:local username $1;
:local password $2;
:local description $3;
# Validate inputs
:if ([:len $username] = 0 || [:len $password] = 0) do={
/log error "Username and password are required";
:return false;
};
# Create user with secure password
:do {
/user add name=$username password=$password group=read comment=$description;
/log info ("User " . $username . " created successfully");
# Clear password variable (security measure)
:set password "";
:return true;
} on-error={
/log error ("Failed to create user: " . $username);
# Clear password variable even on error
:set password "";
:return false;
};
};
# API key validation
:local validateAPIKey do={
:local apiKey $1;
# API key format validation (example: 32-character hex)
:if ([:len $apiKey] != 32) do={
:return false;
};
# Check if it contains only valid hex characters
:if (!($apiKey ~ "^[0-9a-fA-F]+\$")) do={
:return false;
};
:return true;
};
# Certificate validation helper
:local validateCertificateInput do={
:local certData $1;
# Basic PEM format validation
:if (!($certData ~ "-----BEGIN.*CERTIFICATE-----")) do={
:return false;
};
:if (!($certData ~ "-----END.*CERTIFICATE-----")) do={
:return false;
};
# Check for reasonable length
:if ([:len $certData] < 200 || [:len $certData] > 10000) do={
:return false;
};
:return true;
};
# Test secure input functions
:local testPassword "SecureP@ssw0rd123";
:local weakPassword "password123";
:local testAPIKey "1a2b3c4d5e6f7890abcdef1234567890";
:local invalidAPIKey "invalid-key";
:local strongResult [$validateSecurePassword $testPassword];
:local weakResult [$validateSecurePassword $weakPassword];
:local validAPIResult [$validateAPIKey $testAPIKey];
:local invalidAPIResult [$validateAPIKey $invalidAPIKey];
/log info ("Strong password validation: " . $strongResult);
/log info ("Weak password validation: " . $weakResult);
/log info ("Valid API key: " . $validAPIResult);
/log info ("Invalid API key: " . $invalidAPIResult);
# Example of secure user creation
[$storeCredentials "testuser" $testPassword "Test user account"];
}Input processing workflows
Multi-step configuration wizard
# Complete configuration wizard workflow
/system script add name=network-setup-wizard source={
# Global state management for wizard
:global wizardState;
:global wizardStep;
# Initialize wizard if first run
:if ([:typeof $wizardState] = "nothing") do={
:set wizardState {};
:set wizardStep 1;
};
# Wizard step functions
:local stepWelcome do={
/log info "=== Network Configuration Wizard ===";
/log info "This wizard will help you configure basic network settings.";
/log info "Step 1: Basic System Information";
/log info "Please set the following global variables:";
/log info ":global systemHostname \"your-router-name\"";
/log info ":global adminEmail \"admin@company.com\"";
/log info "Then run the script again.";
};
:local stepBasicInfo do={
:global systemHostname;
:global adminEmail;
:if ([:typeof $systemHostname] = "nothing" || [:typeof $adminEmail] = "nothing") do={
/log error "Missing required information. Please set systemHostname and adminEmail.";
:return false;
};
# Validate inputs
:if ([:len $systemHostname] = 0 || [:len $systemHostname] > 63) do={
/log error "Invalid hostname (1-63 characters required)";
:return false;
};
:if (!($adminEmail ~ ".*@.*\\..*")) do={
/log error "Invalid email format";
:return false;
};
# Store validated data
:set ($wizardState->"hostname") $systemHostname;
:set ($wizardState->"email") $adminEmail;
# Apply basic settings
/system identity set name=$systemHostname;
/log info "Step 1 completed. Basic information configured.";
/log info "Step 2: Network Configuration";
/log info "Please set: :global lanNetwork \"192.168.1.0/24\"";
/log info "Please set: :global wanInterface \"ether1\"";
:set systemHostname; # Clear sensitive data
:set adminEmail;
:set wizardStep 2;
:return true;
};
:local stepNetworkConfig do={
:global lanNetwork;
:global wanInterface;
:if ([:typeof $lanNetwork] = "nothing" || [:typeof $wanInterface] = "nothing") do={
/log error "Missing network configuration. Please set lanNetwork and wanInterface.";
:return false;
};
# Validate network format
:if (!($lanNetwork ~ "^[0-9.]+/[0-9]+\$")) do={
/log error "Invalid network format (use CIDR notation like 192.168.1.0/24)";
:return false;
};
# Store network configuration
:set ($wizardState->"lan-network") $lanNetwork;
:set ($wizardState->"wan-interface") $wanInterface;
# Configure basic networking
:do {
# Extract IP and subnet from CIDR
:local slashPos [:find $lanNetwork "/"];
:local networkIP [:pick $lanNetwork 0 $slashPos];
:local subnetBits [:pick $lanNetwork ($slashPos + 1) [:len $lanNetwork]];
# Create bridge and add LAN interface
/interface bridge add name=bridge-lan;
/ip address add address=($networkIP . "/" . $subnetBits) interface=bridge-lan;
/log info "Network configuration applied.";
/log info "Step 3: Security Configuration";
/log info "Please set: :global adminPassword \"your-secure-password\"";
/log info "Please set: :global enableFirewall yes";
:set lanNetwork; # Clear variables
:set wanInterface;
:set wizardStep 3;
:return true;
} on-error={
/log error "Failed to configure network settings";
:return false;
};
};
:local stepSecurity do={
:global adminPassword;
:global enableFirewall;
:if ([:typeof $adminPassword] = "nothing") do={
/log error "Missing security configuration. Please set adminPassword.";
:return false;
};
# Validate password strength
:if ([:len $adminPassword] < 8) do={
/log error "Password too weak (minimum 8 characters)";
:return false;
};
# Apply security settings
:do {
# Change admin password
/user set admin password=$adminPassword;
# Basic firewall rules if requested
:if ($enableFirewall = "yes") do={
/ip firewall filter add chain=input action=accept connection-state=established,related;
/ip firewall filter add chain=input action=accept src-address=($wizardState->"lan-network");
/ip firewall filter add chain=input action=drop;
/ip firewall nat add chain=srcnat out-interface=($wizardState->"wan-interface") action=masquerade;
};
/log info "Security configuration completed.";
/log info "=== Configuration Wizard Complete ===";
# Generate summary
:local summary "Configuration Summary:\n";
:set summary ($summary . "Hostname: " . ($wizardState->"hostname") . "\n");
:set summary ($summary . "LAN Network: " . ($wizardState->"lan-network") . "\n");
:set summary ($summary . "WAN Interface: " . ($wizardState->"wan-interface") . "\n");
:set summary ($summary . "Firewall: " . $enableFirewall . "\n");
/log info $summary;
# Clear wizard state
:set wizardState;
:set wizardStep;
:set adminPassword ""; # Clear sensitive data
:set enableFirewall;
:return true;
} on-error={
/log error "Failed to apply security settings";
:set adminPassword ""; # Clear sensitive data even on error
:return false;
};
};
# Execute appropriate step
:if ($wizardStep = 1) do={
[$stepWelcome];
:if ([/log print count-only where message~"systemHostname"] = 0) do={
[$stepBasicInfo];
};
} else={
:if ($wizardStep = 2) do={
:if ([$stepBasicInfo]) do={
[$stepNetworkConfig];
};
} else={
:if ($wizardStep = 3) do={
[$stepSecurity];
} else={
/log error "Invalid wizard step";
:set wizardState;
:set wizardStep 1;
};
};
};
}Error handling for user input
Input validation errors
# Comprehensive input error handling
/system script add name=input-error-handling source={
# Error reporting system
:local reportError do={
:local errorType $1;
:local errorMessage $2;
:local inputValue $3;
:local timestamp [/system clock get time];
:local fullMessage ("INPUT ERROR [" . $timestamp . "] " . $errorType . ": " . $errorMessage);
:if ([:len $inputValue] > 0) do={
:set fullMessage ($fullMessage . " (Input: '" . $inputValue . "')");
};
/log error $fullMessage;
:return $fullMessage;
};
# Safe input processor with comprehensive error handling
:local processUserInput do={
:local inputType $1;
:local inputValue $2;
:local options $3; # Optional parameters
# Input type validation
:if ($inputType = "ip-address") do={
:if ([:len $inputValue] = 0) do={
:return [$reportError "VALIDATION" "IP address cannot be empty" $inputValue];
};
# IP format validation
:if (!($inputValue ~ "^[0-9.]+\$")) do={
:return [$reportError "FORMAT" "Invalid IP address format" $inputValue];
};
# Validate octets
:local octets {};
:local currentOctet "";
:local dotCount 0;
:for i from=0 to=([:len $inputValue] - 1) do={
:local char [:pick $inputValue $i ($i + 1)];
:if ($char = ".") do={
:set octets ($octets, [:tonum $currentOctet]);
:set currentOctet "";
:set dotCount ($dotCount + 1);
} else={
:set currentOctet ($currentOctet . $char);
};
};
:set octets ($octets, [:tonum $currentOctet]);
:if ($dotCount != 3 || [:len $octets] != 4) do={
:return [$reportError "FORMAT" "IP address must have 4 octets" $inputValue];
};
# Validate octet ranges
:foreach octet in=$octets do={
:if ($octet < 0 || $octet > 255) do={
:return [$reportError "RANGE" "IP octet out of range (0-255)" $inputValue];
};
};
:return "valid";
} else={
:if ($inputType = "port") do={
:if ([:len $inputValue] = 0) do={
:return [$reportError "VALIDATION" "Port number cannot be empty" $inputValue];
};
:local portNum;
:do {
:set portNum [:tonum $inputValue];
} on-error={
:return [$reportError "FORMAT" "Port must be a number" $inputValue];
};
:if ($portNum < 1 || $portNum > 65535) do={
:return [$reportError "RANGE" "Port must be between 1-65535" $inputValue];
};
# Check for reserved ports if specified
:if ([:typeof $options] != "nothing" && ($options->"check-reserved") = true) do={
:local reservedPorts {22; 23; 80; 443; 8291};
:foreach reserved in=$reservedPorts do={
:if ($portNum = $reserved) do={
:return [$reportError "CONFLICT" "Port is reserved for system use" $inputValue];
};
};
};
:return "valid";
} else={
:if ($inputType = "filename") do={
:if ([:len $inputValue] = 0) do={
:return [$reportError "VALIDATION" "Filename cannot be empty" $inputValue];
};
:if ([:len $inputValue] > 255) do={
:return [$reportError "LENGTH" "Filename too long (max 255 characters)" $inputValue];
};
# Check for invalid filename characters
:if ($inputValue ~ "[<>:\"/\\\\|?*]") do={
:return [$reportError "FORMAT" "Filename contains invalid characters" $inputValue];
};
# Check for reserved names
:local reservedNames {"CON"; "PRN"; "AUX"; "NUL"};
:foreach reserved in=$reservedNames do={
:if ($inputValue = $reserved) do={
:return [$reportError "RESERVED" "Filename is a reserved name" $inputValue];
};
};
:return "valid";
} else={
:return [$reportError "SYSTEM" "Unknown input type" $inputType];
};
};
};
};
# Batch input validation
:local validateInputBatch do={
:local inputBatch $1;
:local errors {};
:local validInputs {};
:foreach input in=$inputBatch do={
:local inputType ($input->"type");
:local inputValue ($input->"value");
:local inputName ($input->"name");
:local options ($input->"options");
:local result [$processUserInput $inputType $inputValue $options];
:if ($result = "valid") do={
:set ($validInputs->$inputName) $inputValue;
/log info ("Input '" . $inputName . "' validated successfully");
} else={
:set ($errors->$inputName) $result;
/log error ("Input '" . $inputName . "' validation failed: " . $result);
};
};
:if ([:len $errors] = 0) do={
/log info "All inputs validated successfully";
:return $validInputs;
} else={
/log error ("Validation failed for " . [:len $errors] . " inputs");
:return $errors;
};
};
# Test the error handling system
:local testInputs {
{name="server-ip"; type="ip-address"; value="192.168.1.300"}; # Invalid IP
{name="ssh-port"; type="port"; value="22"; options={check-reserved=true}}; # Reserved port
{name="backup-file"; type="filename"; value="backup/file.rsc"}; # Invalid filename
{name="wan-ip"; type="ip-address"; value="203.0.113.1"}; # Valid IP
{name="web-port"; type="port"; value="8080"} # Valid port
};
:local results [$validateInputBatch $testInputs];
# Report results
:if ([:typeof ($results->"server-ip")] != "nothing") do={
/log info "Validation results processed";
} else={
/log info "Some validations failed - check error log";
};
}Best practices for user input
Security guidelines
Always validate input - Never trust user input without validation
Sanitize data - Remove or escape dangerous characters
Use type checking - Ensure input matches expected data types
Implement length limits - Prevent buffer overflow scenarios
Clear sensitive data - Remove passwords and keys from memory after use
Usability recommendations
Provide clear prompts - Tell users exactly what input is expected
Give helpful error messages - Explain what went wrong and how to fix it
Use defaults wisely - Provide sensible defaults for optional parameters
Validate incrementally - Check input as it's provided, not just at the end
Confirm destructive actions - Always confirm before making irreversible changes
Performance considerations
Cache validation results - Don't re-validate the same input repeatedly
Batch process inputs - Validate multiple inputs together when possible
Use efficient validation - Implement validation logic that scales well
Limit input processing - Set reasonable limits on input size and complexity
Handle errors gracefully - Don't let input errors crash the entire script
Last updated
Was this helpful?