using System.Text.RegularExpressions; namespace Bit.Seeder.Migration.Utils; /// /// Validates SQL identifiers (table names, column names, schema names) to prevent SQL injection. /// public static class IdentifierValidator { // Regex pattern for valid SQL identifiers: must start with letter or underscore, // followed by letters, numbers, or underscores private static readonly Regex ValidIdentifierPattern = new Regex( @"^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled ); // Maximum reasonable length for identifiers (most databases have limits around 128-255) private const int MaxIdentifierLength = 128; /// /// Validates a SQL identifier (table name, column name, schema name). /// /// The identifier to validate /// True if the identifier is valid, false otherwise public static bool IsValid(string? identifier) { if (string.IsNullOrWhiteSpace(identifier)) return false; if (identifier.Length > MaxIdentifierLength) return false; return ValidIdentifierPattern.IsMatch(identifier); } /// /// Validates a SQL identifier and throws an exception if invalid. /// /// The identifier to validate /// The type of identifier (e.g., "table name", "column name") /// Thrown when the identifier is invalid public static void ValidateOrThrow(string? identifier, string identifierType = "identifier") { if (!IsValid(identifier)) { throw new ArgumentException( $"Invalid {identifierType}: '{identifier}'. " + $"Identifiers must start with a letter or underscore, " + $"contain only letters, numbers, and underscores, " + $"and be no longer than {MaxIdentifierLength} characters.", nameof(identifier) ); } } /// /// Validates multiple identifiers and throws an exception if any are invalid. /// /// The identifiers to validate /// The type of identifiers (e.g., "column names") /// Thrown when any identifier is invalid public static void ValidateAllOrThrow(IEnumerable identifiers, string identifierType = "identifiers") { foreach (var identifier in identifiers) { ValidateOrThrow(identifier, identifierType); } } /// /// Filters a list of identifiers to only include valid ones. /// /// The identifiers to filter /// A list of valid identifiers public static List FilterValid(IEnumerable identifiers) { return identifiers.Where(IsValid).ToList(); } }