diff options
author | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2018-05-17 12:29:47 +0200 |
---|---|---|
committer | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2018-05-17 12:29:47 +0200 |
commit | 0ce63efe966840edb6e6184cf1abcef272a24dfc (patch) | |
tree | 5a857a8ba298446325b97992098db99fac1876e8 | |
parent | f52de2f73cc9d26c26fd66c23892ac42bf973b05 (diff) | |
download | gitlab-ce-0ce63efe966840edb6e6184cf1abcef272a24dfc.tar.gz |
Add extended /regexp/ scheme support to untrusted regexp
-rw-r--r-- | lib/gitlab/untrusted_regexp.rb | 23 | ||||
-rw-r--r-- | spec/lib/gitlab/untrusted_regexp_spec.rb | 49 |
2 files changed, 71 insertions, 1 deletions
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index ce1cf737663..70d1a7c6535 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -9,6 +9,8 @@ module Gitlab # 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 + require_dependency 're2' + delegate :===, :source, to: :regexp def initialize(pattern, multiline: false) @@ -52,6 +54,27 @@ module Gitlab Regexp.new(pattern) end + def self.valid?(pattern) + self.fabricate(pattern) + rescue RegexpError + false + end + + def self.fabricate(pattern) + matches = pattern.match(%r{^/(?<regexp>.+)/(?<flags>[ismU]*)$}) + + if matches + expression = matches[:regexp] + flags = matches[:flags] + + expression.prepend("(?#{flags})") if flags.present? + + self.new(expression, multiline: false) + else + self.new(pattern, multiline: false) + end + end + private attr_reader :regexp diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index 0ee7fa1e570..4bca320ac2c 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -1,6 +1,53 @@ -require 'spec_helper' +require 'fast_spec_helper' +require 'support/shared_examples/malicious_regexp_shared_examples' describe Gitlab::UntrustedRegexp do + describe '.valid?' do + it 'returns true if regexp is valid' do + end + + it 'returns true if regexp is invalid' do + end + end + + describe '.fabricate' do + context 'when regexp is using /regexp/ scheme with flags' do + it 'fabricates regexp with a single flag' do + regexp = described_class.fabricate('/something/i') + + expect(regexp).to eq described_class.new('(?i)something') + expect(regexp.scan('SOMETHING')).to be_one + end + + it 'fabricates regexp with multiple flags' do + regexp = described_class.fabricate('/something/im') + + expect(regexp).to eq described_class.new('(?im)something') + end + + it 'fabricates regexp without flags' do + regexp = described_class.fabricate('/something/') + + expect(regexp).to eq described_class.new('something') + end + end + + context 'when regexp is not plain pattern' do + it 'fabricates regexp without flags' do + regexp = described_class.fabricate('something') + + expect(regexp).to eq described_class.new('something') + end + end + + context 'when regexp is invalid' do + it 'raises an error' do + expect { described_class.fabricate('/some ( thing/') } + .to raise_error(RegexpError) + end + end + end + describe '#initialize' do subject { described_class.new(pattern) } |