Module: Parse::RegexSecurity

Defined in:
lib/parse/query/constraints.rb

Overview

Security module for validating regex patterns to prevent ReDoS attacks. MongoDB uses PCRE which is susceptible to catastrophic backtracking.

Constant Summary collapse

MAX_PATTERN_LENGTH =

Maximum allowed length for regex patterns

500
DANGEROUS_PATTERNS =

Patterns that can cause exponential backtracking in PCRE

[
  /\(\?\=|\(\?\!|\(\?\<[!=]/,                    # Lookahead/lookbehind assertions
  /\{(\d{3,}|\d+,\d{3,})\}/,                     # Large repetition counts {1000} or {1,1000}
  /(\.\*|\.\+)\s*(\.\*|\.\+)/,                   # Consecutive .* or .+ patterns
  /\([^)]*(\+|\*)[^)]*\)\s*(\+|\*)/,             # Nested quantifiers like (a+)+
  /\(\?[^)]*\([^)]*(\+|\*)[^)]*\)[^)]*(\+|\*)\)/, # More complex nested quantifiers
].freeze

Class Method Summary collapse

Class Method Details

.safe?(pattern, max_length: MAX_PATTERN_LENGTH) ⇒ Boolean

Checks if a pattern is safe without raising an exception.

Parameters:

  • pattern (String, Regexp)

    the pattern to check

Returns:

  • (Boolean)

    true if safe, false if potentially dangerous



57
58
59
60
61
62
# File 'lib/parse/query/constraints.rb', line 57

def safe?(pattern, max_length: MAX_PATTERN_LENGTH)
  validate!(pattern, max_length: max_length)
  true
rescue ArgumentError
  false
end

.validate!(pattern, max_length: MAX_PATTERN_LENGTH) ⇒ String

Validates a regex pattern for potential ReDoS vulnerabilities.

Parameters:

  • pattern (String, Regexp)

    the pattern to validate

  • max_length (Integer) (defaults to: MAX_PATTERN_LENGTH)

    maximum allowed pattern length

Returns:

  • (String)

    the validated pattern string

Raises:

  • (ArgumentError)

    if the pattern is potentially dangerous



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/parse/query/constraints.rb', line 36

def validate!(pattern, max_length: MAX_PATTERN_LENGTH)
  pattern_str = pattern.is_a?(Regexp) ? pattern.source : pattern.to_s

  if pattern_str.length > max_length
    raise ArgumentError, "Regex pattern too long (#{pattern_str.length} chars, max #{max_length}). " \
                         "Long patterns can cause performance issues."
  end

  DANGEROUS_PATTERNS.each do |dangerous|
    if pattern_str.match?(dangerous)
      raise ArgumentError, "Regex pattern contains potentially dangerous constructs that could cause " \
                           "ReDoS (Regular Expression Denial of Service). Pattern: #{pattern_str.inspect}"
    end
  end

  pattern_str
end