2019-02-25 09:41:52 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
class UntrustedRegexp
|
|
|
|
# This class implements support for Ruby syntax of regexps
|
|
|
|
# and converts that to RE2 representation:
|
|
|
|
# /<regexp>/<flags>
|
|
|
|
class RubySyntax
|
2019-04-04 11:00:56 -04:00
|
|
|
PATTERN = %r{^/(?<regexp>.*)/(?<flags>[ismU]*)$}.freeze
|
2019-02-25 09:41:52 -05:00
|
|
|
|
|
|
|
# Checks if pattern matches a regexp pattern
|
|
|
|
# but does not enforce it's validity
|
|
|
|
def self.matches_syntax?(pattern)
|
|
|
|
pattern.is_a?(String) && pattern.match(PATTERN).present?
|
|
|
|
end
|
|
|
|
|
|
|
|
# The regexp can match the pattern `/.../`, but may not be fabricatable:
|
|
|
|
# it can be invalid or incomplete: `/match ( string/`
|
2019-04-04 11:00:56 -04:00
|
|
|
def self.valid?(pattern, fallback: false)
|
|
|
|
!!self.fabricate(pattern, fallback: fallback)
|
2019-02-25 09:41:52 -05:00
|
|
|
end
|
|
|
|
|
2019-04-04 11:00:56 -04:00
|
|
|
def self.fabricate(pattern, fallback: false)
|
|
|
|
self.fabricate!(pattern, fallback: fallback)
|
2019-02-25 09:41:52 -05:00
|
|
|
rescue RegexpError
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2019-04-04 11:00:56 -04:00
|
|
|
def self.fabricate!(pattern, fallback: false)
|
2019-02-25 09:41:52 -05:00
|
|
|
raise RegexpError, 'Pattern is not string!' unless pattern.is_a?(String)
|
|
|
|
|
|
|
|
matches = pattern.match(PATTERN)
|
|
|
|
raise RegexpError, 'Invalid regular expression!' if matches.nil?
|
|
|
|
|
2019-04-04 11:00:56 -04:00
|
|
|
begin
|
|
|
|
create_untrusted_regexp(matches[:regexp], matches[:flags])
|
|
|
|
rescue RegexpError
|
|
|
|
raise unless fallback &&
|
|
|
|
Feature.enabled?(:allow_unsafe_ruby_regexp, default_enabled: false)
|
2019-02-25 09:41:52 -05:00
|
|
|
|
2019-04-04 11:00:56 -04:00
|
|
|
create_ruby_regexp(matches[:regexp], matches[:flags])
|
|
|
|
end
|
2019-02-25 09:41:52 -05:00
|
|
|
end
|
2019-04-04 11:00:56 -04:00
|
|
|
|
|
|
|
def self.create_untrusted_regexp(pattern, flags)
|
|
|
|
pattern.prepend("(?#{flags})") if flags.present?
|
|
|
|
|
|
|
|
UntrustedRegexp.new(pattern, multiline: false)
|
|
|
|
end
|
|
|
|
private_class_method :create_untrusted_regexp
|
|
|
|
|
|
|
|
def self.create_ruby_regexp(pattern, flags)
|
|
|
|
options = 0
|
|
|
|
options += Regexp::IGNORECASE if flags&.include?('i')
|
|
|
|
options += Regexp::MULTILINE if flags&.include?('m')
|
|
|
|
|
|
|
|
Regexp.new(pattern, options)
|
|
|
|
end
|
|
|
|
private_class_method :create_ruby_regexp
|
2019-02-25 09:41:52 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|