Module: Odin::Validation::ReDoSProtection
- Defined in:
- lib/odin/validation/redos_protection.rb
Constant Summary collapse
- MAX_PATTERN_LENGTH =
1000- MAX_QUANTIFIER_NESTING =
3- MAX_PATTERN_STRING_LENGTH =
10_000
Class Method Summary collapse
-
.check_pattern(pattern) ⇒ Object
Check if a regex pattern is potentially dangerous Returns :safe, :too_long, :nested_quantifiers, or :dangerous.
-
.compile_safe(pattern) ⇒ Object
Compile a pattern after safety check, or raise.
- .safe?(pattern) ⇒ Boolean
-
.safe_test(regex, value) ⇒ Object
Test a regex against a value with length protection.
Class Method Details
.check_pattern(pattern) ⇒ Object
Check if a regex pattern is potentially dangerous Returns :safe, :too_long, :nested_quantifiers, or :dangerous
12 13 14 15 16 17 |
# File 'lib/odin/validation/redos_protection.rb', line 12 def self.check_pattern(pattern) return :too_long if pattern.length > MAX_PATTERN_LENGTH return :nested_quantifiers if nested_quantifier_depth(pattern) > MAX_QUANTIFIER_NESTING return :dangerous if dangerous_pattern?(pattern) :safe end |
.compile_safe(pattern) ⇒ Object
Compile a pattern after safety check, or raise
24 25 26 27 28 29 30 31 32 33 |
# File 'lib/odin/validation/redos_protection.rb', line 24 def self.compile_safe(pattern) status = check_pattern(pattern) unless status == :safe raise Errors::OdinError.new( "REDOS", "Potentially dangerous regex pattern rejected: #{status}" ) end Regexp.new(pattern) end |
.safe?(pattern) ⇒ Boolean
19 20 21 |
# File 'lib/odin/validation/redos_protection.rb', line 19 def self.safe?(pattern) check_pattern(pattern) == :safe end |
.safe_test(regex, value) ⇒ Object
Test a regex against a value with length protection
36 37 38 39 40 41 42 |
# File 'lib/odin/validation/redos_protection.rb', line 36 def self.safe_test(regex, value) return { matched: false, reason: :value_too_long } if value.length > MAX_PATTERN_STRING_LENGTH start = Process.clock_gettime(Process::CLOCK_MONOTONIC) matched = regex.match?(value) elapsed_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000 { matched: matched, timed_out: elapsed_ms > 100, execution_time_ms: elapsed_ms } end |