Module: LanguageOperator::Validators
- Defined in:
- lib/language_operator/validators.rb
Overview
Common parameter validation utilities
Provides reusable validators for common parameter validation patterns across tools. All validators follow the same convention:
- Return
nilif the value is valid - Return an error string if the value is invalid
This allows for consistent usage in tools:
error = LanguageOperator::Validators.http_url(params['url'])
next error if error
Class Method Summary collapse
-
.email(email) ⇒ String?
Validate email address format (basic).
-
.http_url(url) ⇒ String?
Validate HTTP/HTTPS URL format.
-
.not_empty(value, field_name) ⇒ String?
Validate that a value is not nil or empty.
-
.numeric_range(value, min: nil, max: nil, field_name: 'value') ⇒ String?
Validate numeric range.
-
.one_of(value, allowed, field_name) ⇒ String?
Validate that a value is one of the allowed options.
-
.safe_path(path) ⇒ String?
Validate that a path doesn't contain directory traversal attempts.
Class Method Details
.email(email) ⇒ String?
Validate email address format (basic)
Note: This is a basic format check, not full RFC 5322 validation. It checks for: [email protected]
136 137 138 139 140 141 |
# File 'lib/language_operator/validators.rb', line 136 def self.email(email) return 'Error: Email address cannot be empty' if email.nil? || email.strip.empty? return nil if email =~ /\A[^@\s]+@[^@\s]+\.[^@\s]+\z/ 'Error: Invalid email address format' end |
.http_url(url) ⇒ String?
Validate HTTP/HTTPS URL format
43 44 45 46 47 48 |
# File 'lib/language_operator/validators.rb', line 43 def self.http_url(url) return 'Error: URL cannot be empty' if url.nil? || url.strip.empty? return nil if url =~ %r{^https?://.+} 'Error: Invalid URL. Must start with http:// or https://' end |
.not_empty(value, field_name) ⇒ String?
Validate that a value is not nil or empty
64 65 66 67 68 |
# File 'lib/language_operator/validators.rb', line 64 def self.not_empty(value, field_name) return nil if value && !value.to_s.strip.empty? LanguageOperator::Errors.empty_field(field_name) end |
.numeric_range(value, min: nil, max: nil, field_name: 'value') ⇒ String?
Validate numeric range
110 111 112 113 114 115 116 117 118 |
# File 'lib/language_operator/validators.rb', line 110 def self.numeric_range(value, min: nil, max: nil, field_name: 'value') return "Error: #{field_name} must be a number" unless value.is_a?(Numeric) return "Error: #{field_name} must be at least #{min}" if min && value < min return "Error: #{field_name} must be at most #{max}" if max && value > max nil end |
.one_of(value, allowed, field_name) ⇒ String?
Validate that a value is one of the allowed options
83 84 85 86 87 88 89 90 91 |
# File 'lib/language_operator/validators.rb', line 83 def self.one_of(value, allowed, field_name) return nil if allowed.include?(value) LanguageOperator::Errors.invalid_parameter( field_name, value, "one of: #{allowed.join(', ')}" ) end |
.safe_path(path) ⇒ String?
Validate that a path doesn't contain directory traversal attempts
Checks for:
- Parent directory references (..)
- Null bytes (\0)
Note: This is a basic safety check. Tool-specific path validation (like workspace sandboxing) should still be implemented in the tools.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/language_operator/validators.rb', line 164 def self.safe_path(path) return 'Error: Path cannot be empty' if path.nil? || path.strip.empty? # Check for null bytes first (before any decoding) return 'Error: Path contains invalid characters or directory traversal' if path.include?("\0") begin # Decode URL-encoded paths to catch encoded traversal attempts # Handle multiple layers of encoding by repeatedly decoding decoded_path = path 3.times do # Limit to prevent infinite loops new_decoded = URI::DEFAULT_PARSER.unescape(decoded_path) break if new_decoded == decoded_path # No more changes decoded_path = new_decoded rescue ArgumentError, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError # Invalid byte sequence - treat as potential attack return 'Error: Path contains invalid characters or directory traversal' end # Check for directory traversal in both original and decoded paths [path, decoded_path].each do |check_path| # Check for obvious traversal patterns return 'Error: Path contains invalid characters or directory traversal' if check_path.include?('..') # Check for overlong UTF-8 sequences that decode to dangerous characters # These are common in directory traversal attacks if check_path.include?("\xC0\xAE") || check_path.include?("\xC0\xAF") || check_path.bytes.each_cons(2).any? { |a, b| a == 0xC0 && (0x80..0xBF).cover?(b) } return 'Error: Path contains invalid characters or directory traversal' end # Canonicalize path to detect complex traversal attempts begin # Use current directory as base for relative paths canonical_path = File.(check_path, Dir.pwd) # For relative paths, ensure they don't escape current directory unless check_path.start_with?('/') current_dir_canonical = File.(Dir.pwd) return 'Error: Path contains invalid characters or directory traversal' unless canonical_path.start_with?(current_dir_canonical) end # Additional check: ensure canonical path doesn't contain dangerous patterns return 'Error: Path contains invalid characters or directory traversal' if canonical_path.include?('/../') || canonical_path.end_with?('/..') rescue ArgumentError, Errno::ENOENT # Path canonicalization failed, likely due to invalid characters return 'Error: Path contains invalid characters or directory traversal' end end rescue URI::InvalidURIError # URL decoding failed, path might contain invalid sequences return 'Error: Path contains invalid characters or directory traversal' end nil end |