Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand Regex DoS check to include String#match and #match? coercion #1715

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

bensheldon
Copy link

@bensheldon bensheldon commented Jun 8, 2022

Closes #1714.

I added additional behavior to the existing check_regex_dos.rb file. I thought it seemed like the correct location because it is checking for ReDoS, though it's arguably a bit different because it's checking methods on String rather than regex literals.

I also am not sure whether I should also validate that the object being passed to match/match? is a String; it seems like the expectation is that any user_input will be a string, but I'm not confident about that.

@bensheldon bensheldon changed the title 3.2Expand Regex DoS check to include String#match and #match? coercion Expand Regex DoS check to include String#match and #match? coercion Jun 8, 2022
@bensheldon
Copy link
Author

oops, sorry this got submitted in a half-written state. Cat on keyboard problems 😸


#Process calls
def run_check
Brakeman.debug "Finding dynamic regexes"
calls = tracker.find_call :method => [:brakeman_regex_interp]
COERCES_STRING_TO_REGEX.each do |coercion_method|
calls.concat tracker.find_call(:method => [coercion_method]).select { |call| string?(call[:target]) }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could simplify this to

tracker.find_call(:methods => CEORCES_STRING_TO_REGEX).select { ... }

@bensheldon
Copy link
Author

@presidentbeef I updated the code based on your feedback. Thank you! 🙏🏻

I also made one behavior change: I added a method called string_or_modified_string? that recurses down the target to see if the initial call was a String. I added this because I found making a trivial modification like downcase would not trigger the check (e.g. "haystack".downcase.match(params[:unsafe])). I couldn't find an existing recursive method, but maybe I overlooked one.

@presidentbeef
Copy link
Owner

In real code, would we expect to see string literals like the test cases? 🤔

@bensheldon
Copy link
Author

In real code, would we expect to see string literals like the test cases? 🤔

This was the code we discovered (lightly anonymized):

result = @active_record_resource.a_hash_value
  .keys
  .select { |key| key.downcase.match?(unsafe_string_query) }
  .sort

I'd love advice for better ways to identify [String]#match?([String]).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Check: REDoS from match/match? coercing unsafe strings to regular expressions
2 participants