using System.Text.RegularExpressions; namespace Cleanuparr.Shared.Helpers; /// /// Validates BASE_PATH values to ensure security and proper formatting /// public static class BasePathValidator { private const int MaxLength = 100; private static readonly Regex ValidPathRegex = new(@"^/[a-zA-Z0-9_\-]+(/[a-zA-Z0-9_\-]+)*$", RegexOptions.Compiled); /// /// Validates a BASE_PATH value /// /// The base path to validate /// ValidationResult containing success status and error message if invalid public static ValidationResult Validate(string? basePath) { // Empty or null is valid (no base path) if (string.IsNullOrWhiteSpace(basePath)) { return ValidationResult.Success(); } // Trim whitespace basePath = basePath.Trim(); // Check for just root path if (basePath == "/") { return ValidationResult.Failure("BASE_PATH cannot be just '/' (conflicts with root)"); } // Check length if (basePath.Length > MaxLength) { return ValidationResult.Failure($"BASE_PATH cannot be longer than {MaxLength} characters"); } // Must start with / if (!basePath.StartsWith('/')) { return ValidationResult.Failure("BASE_PATH must start with '/'"); } // No dots allowed (prevents path traversal) if (basePath.Contains('.')) { return ValidationResult.Failure("BASE_PATH cannot contain dots (.) for security reasons"); } // No double slashes if (basePath.Contains("//")) { return ValidationResult.Failure("BASE_PATH cannot contain double slashes (//)"); } // Must not end with slash (except root) if (basePath.EndsWith('/') && basePath != "/") { return ValidationResult.Failure("BASE_PATH cannot end with '/' (except root)"); } // Validate format using regex (alphanumeric, hyphens, underscores only) if (!ValidPathRegex.IsMatch(basePath)) { return ValidationResult.Failure("BASE_PATH can only contain letters, numbers, hyphens (-), and underscores (_)"); } return ValidationResult.Success(); } /// /// Normalizes a BASE_PATH value (removes trailing slash, trims whitespace) /// /// The base path to normalize /// Normalized base path or empty string if invalid public static string Normalize(string? basePath) { if (string.IsNullOrWhiteSpace(basePath)) { return string.Empty; } basePath = basePath.Trim(); // Remove trailing slash unless it's just "/" if (basePath.EndsWith('/') && basePath != "/") { basePath = basePath.TrimEnd('/'); } return basePath; } } /// /// Validates PORT values to ensure they are valid and within acceptable ranges /// public static class PortValidator { private const int MinPort = 1; private const int MaxPort = 65535; private const int DefaultPort = 11011; /// /// Validates a PORT value /// /// The port string to validate /// ValidationResult containing success status and error message if invalid public static ValidationResult Validate(string? portString) { // Null or empty uses default - this is valid if (string.IsNullOrWhiteSpace(portString)) { return ValidationResult.Success(); } // Trim whitespace portString = portString.Trim(); // Try to parse as integer if (!int.TryParse(portString, out int port)) { return ValidationResult.Failure($"PORT must be a valid integer, got: '{portString}'"); } // Check range if (port < MinPort || port > MaxPort) { return ValidationResult.Failure($"PORT must be between {MinPort} and {MaxPort}, got: {port}"); } // Check for commonly problematic ports if (IsWellKnownPort(port)) { return ValidationResult.Failure($"PORT {port} is a well-known system port and should not be used"); } return ValidationResult.Success(); } /// /// Normalizes a PORT value (returns default if null/empty, validates range) /// /// The port string to normalize /// Valid port number or default port public static int Normalize(string? portString) { if (string.IsNullOrWhiteSpace(portString)) { return DefaultPort; } portString = portString.Trim(); if (int.TryParse(portString, out int port)) { // Validate range if (port >= MinPort && port <= MaxPort && !IsWellKnownPort(port)) { return port; } } // If invalid, return default return DefaultPort; } /// /// Gets the default port /// public static int GetDefaultPort() => DefaultPort; /// /// Checks if a port is a well-known system port that should be avoided /// private static bool IsWellKnownPort(int port) { // Common system ports that should be avoided int[] wellKnownPorts = { 21, 22, 23, 25, 53, 80, 110, 143, 443, 993, 995 }; return wellKnownPorts.Contains(port) || port < 1024; // All ports < 1024 are generally reserved } } /// /// Represents the result of a validation operation /// public class ValidationResult { public bool IsValid { get; } public string ErrorMessage { get; } private ValidationResult(bool isValid, string errorMessage = "") { IsValid = isValid; ErrorMessage = errorMessage; } public static ValidationResult Success() => new(true); public static ValidationResult Failure(string errorMessage) => new(false, errorMessage); }