Skip to content

grosser/safe_regexp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ruby Regex Timeout / Backtracking Bomb Safety

Don't let untrusted regular expressions kill your servers (cannot be caught with a Timeout).

DEPRECATED: Ruby 3.2+ supports this natively

Install

gem install safe_regexp

Usage

# normal
/a/.match?('a') # -> true in 0.0001ms
SafeRegexp.execute(/a/, :match?, 'a') # -> true in 0.13568ms

# bomb
require "safe_regexp"
regex = /aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?aa?/
value = "a" * 46
regex.match? value # false in ~60s
SafeRegexp.execute(regex, :match?, value) # -> SafeRegexp::RegexpTimeout

# methods without arguments
regex = "^(The '(?<first_group>.*)' parameter of the (?<second_group>.*))$"
SafeRegexp.execute(regex, :names) # -> ["first_group", "second_group"]

Behind the scenes

  • not using Thread or Timeout
  • spawns a co-processor and kill -9 it if it takes too long, shuts down after 10s of not being used (to avoid process boot cost), use keepalive: 0 to shutdown immediately
  • defaults to 1s timeout
  • uses 1 co-processor per thread
  • any MatchData object is returned as Array since it cannot be dumped

Author

Michael Grosser
[email protected]
License: MIT
Build Status

About

Ruby Regex Timeout / Backtracking Bomb Safety

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages