From 14c3ede71a72abb5d88c9f08e442fefd7f5cb230 Mon Sep 17 00:00:00 2001
From: Nick Malcolm <nmalcolm@gitlab.com>
Date: Wed, 21 Dec 2022 19:51:21 +0000
Subject: [PATCH] Clearly test the expectations around MAX_VERSION_LENGTH

In response to MR feedback: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/107335#note_1216705882
---
 lib/gitlab/import_export/version_checker.rb           | 2 +-
 lib/gitlab/version_info.rb                            | 5 ++++-
 spec/lib/gitlab/import_export/version_checker_spec.rb | 2 +-
 spec/lib/gitlab/version_info_spec.rb                  | 8 ++++++++
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb
index 5ec9db00d0a68..ad071a4cbd7a5 100644
--- a/lib/gitlab/import_export/version_checker.rb
+++ b/lib/gitlab/import_export/version_checker.rb
@@ -34,7 +34,7 @@ def verify_version!(version)
       end
 
       def different_version?(version)
-        Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
+        Gitlab::VersionInfo.parse(version) != Gitlab::VersionInfo.parse(Gitlab::ImportExport.version)
       rescue StandardError => e
         Gitlab::Import::Logger.error(
           message: 'Import error',
diff --git a/lib/gitlab/version_info.rb b/lib/gitlab/version_info.rb
index 61de003c28d37..0351c9b30b3ad 100644
--- a/lib/gitlab/version_info.rb
+++ b/lib/gitlab/version_info.rb
@@ -7,11 +7,14 @@ class VersionInfo
     attr_reader :major, :minor, :patch
 
     VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)/.freeze
+    # To mitigate ReDoS, limit the length of the version string we're
+    # willing to check
+    MAX_VERSION_LENGTH = 128
 
     def self.parse(str, parse_suffix: false)
       if str.is_a?(self)
         str
-      elsif str && m = str.match(VERSION_REGEX)
+      elsif str && str.length <= MAX_VERSION_LENGTH && m = str.match(VERSION_REGEX)
         VersionInfo.new(m[1].to_i, m[2].to_i, m[3].to_i, parse_suffix ? m.post_match : nil)
       else
         VersionInfo.new
diff --git a/spec/lib/gitlab/import_export/version_checker_spec.rb b/spec/lib/gitlab/import_export/version_checker_spec.rb
index 14c62edb7868b..b3730d85f13a3 100644
--- a/spec/lib/gitlab/import_export/version_checker_spec.rb
+++ b/spec/lib/gitlab/import_export/version_checker_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe Gitlab::ImportExport::VersionChecker do
+RSpec.describe Gitlab::ImportExport::VersionChecker, feature_category: :import do
   include ImportExport::CommonUtil
 
   let!(:shared) { Gitlab::ImportExport::Shared.new(nil) }
diff --git a/spec/lib/gitlab/version_info_spec.rb b/spec/lib/gitlab/version_info_spec.rb
index 078f952afad78..99c7a762392b3 100644
--- a/spec/lib/gitlab/version_info_spec.rb
+++ b/spec/lib/gitlab/version_info_spec.rb
@@ -92,6 +92,8 @@
     it { expect(described_class.parse("1.0.0-rc1-ee")).to eq(@v1_0_0) }
     it { expect(described_class.parse("git 1.0.0b1")).to eq(@v1_0_0) }
     it { expect(described_class.parse("git 1.0b1")).not_to be_valid }
+    it { expect(described_class.parse("1.1.#{'1' * described_class::MAX_VERSION_LENGTH}")).not_to be_valid }
+    it { expect(described_class.parse(nil)).not_to be_valid }
 
     context 'with parse_suffix: true' do
       let(:versions) do
@@ -182,4 +184,10 @@
     it { expect(@v1_0_1.without_patch).to eq(@v1_0_0) }
     it { expect(@v1_0_1_rc1.without_patch).to eq(@v1_0_0) }
   end
+
+  describe 'MAX_VERSION_LENGTH' do
+    subject { described_class::MAX_VERSION_LENGTH }
+
+    it { is_expected.to eq(128) }
+  end
 end
-- 
GitLab