summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2017-06-22 15:33:17 +0000
committerMike Greiling <mike@pixelcog.com>2017-07-19 22:28:41 -0500
commitba60d4f6e4f3a6d3cb56c9320f475bee8f0b38da (patch)
tree51245b9a3f7df6df3e396f3335f88d79f5d2f328 /lib
parentceda6bd5a6d5e7b24f0ec003ce2e7b446d0917c0 (diff)
downloadgitlab-ce-ba60d4f6e4f3a6d3cb56c9320f475bee8f0b38da.tar.gz
Merge branch '24570-use-re2-for-user-supplied-regexp-9-3' into 'security-9-3'
24570 use re2 for user supplied regexp 9 3 See merge request !2129
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/ci/trace/stream.rb4
-rw-r--r--lib/gitlab/route_map.rb8
-rw-r--r--lib/gitlab/untrusted_regexp.rb53
3 files changed, 61 insertions, 4 deletions
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index c4c0623df6c..5d6977106d6 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -69,12 +69,12 @@ module Gitlab
return unless valid?
return unless regex
- regex = Regexp.new(regex)
+ regex = Gitlab::UntrustedRegexp.new(regex)
match = ""
reverse_line do |line|
- matches = line.scan(regex)
+ matches = regex.scan(line)
next unless matches.is_a?(Array)
next if matches.empty?
diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb
index 877aa6e6a28..f3952657983 100644
--- a/lib/gitlab/route_map.rb
+++ b/lib/gitlab/route_map.rb
@@ -18,7 +18,11 @@ module Gitlab
mapping = @map.find { |mapping| mapping[:source] === path }
return unless mapping
- path.sub(mapping[:source], mapping[:public])
+ if mapping[:source].is_a?(String)
+ path.sub(mapping[:source], mapping[:public])
+ else
+ mapping[:source].replace(path, mapping[:public])
+ end
end
private
@@ -35,7 +39,7 @@ module Gitlab
source_pattern = source_pattern[1...-1].gsub('\/', '/')
begin
- source_pattern = /\A#{source_pattern}\z/
+ source_pattern = Gitlab::UntrustedRegexp.new('\A' + source_pattern + '\z')
rescue RegexpError => e
raise FormatError, "Route map entry source is not a valid regular expression: #{e}"
end
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
new file mode 100644
index 00000000000..8b43f0053d6
--- /dev/null
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -0,0 +1,53 @@
+module Gitlab
+ # An untrusted regular expression is any regexp containing patterns sourced
+ # from user input.
+ #
+ # Ruby's built-in regular expression library allows patterns which complete in
+ # exponential time, permitting denial-of-service attacks.
+ #
+ # Not all regular expression features are available in untrusted regexes, and
+ # there is a strict limit on total execution time. See the RE2 documentation
+ # at https://github.com/google/re2/wiki/Syntax for more details.
+ class UntrustedRegexp
+ delegate :===, to: :regexp
+
+ def initialize(pattern)
+ @regexp = RE2::Regexp.new(pattern, log_errors: false)
+
+ raise RegexpError.new(regexp.error) unless regexp.ok?
+ end
+
+ def replace_all(text, rewrite)
+ RE2.GlobalReplace(text, regexp, rewrite)
+ end
+
+ def scan(text)
+ scan_regexp.scan(text).map do |match|
+ if regexp.number_of_capturing_groups == 0
+ match.first
+ else
+ match
+ end
+ end
+ end
+
+ def replace(text, rewrite)
+ RE2.Replace(text, regexp, rewrite)
+ end
+
+ private
+
+ attr_reader :regexp
+
+ # RE2 scan operates differently to Ruby scan when there are no capture
+ # groups, so work around it
+ def scan_regexp
+ @scan_regexp ||=
+ if regexp.number_of_capturing_groups == 0
+ RE2::Regexp.new('(' + regexp.source + ')')
+ else
+ regexp
+ end
+ end
+ end
+end