From dfe0f9eedf040c6c266161239b6e2ede77c2b7dc Mon Sep 17 00:00:00 2001
From: Douwe Maan <douwe@gitlab.com>
Date: Tue, 24 Mar 2015 14:55:14 +0100
Subject: [PATCH] Use more specific regexes.

---
 app/models/namespace.rb              |  8 ++--
 app/models/project.rb                |  6 +--
 app/models/snippet.rb                |  4 +-
 app/models/user.rb                   |  4 +-
 app/services/files/create_service.rb |  4 +-
 lib/gitlab/regex.rb                  | 64 +++++++++++++++-------------
 spec/requests/api/projects_spec.rb   |  2 +-
 7 files changed, 48 insertions(+), 44 deletions(-)

diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 0c0252ed8ee..07d85d05bb4 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -24,8 +24,8 @@ class Namespace < ActiveRecord::Base
   validates :name,
     presence: true, uniqueness: true,
     length: { within: 0..255 },
-    format: { with: Gitlab::Regex.name_regex,
-              message: Gitlab::Regex.name_regex_message }
+    format: { with: Gitlab::Regex.namespace_name_regex,
+              message: Gitlab::Regex.namespace_name_regex_message }
 
   validates :description, length: { within: 0..255 }
   validates :path,
@@ -33,8 +33,8 @@ class Namespace < ActiveRecord::Base
     presence: true,
     length: { within: 1..255 },
     exclusion: { in: Gitlab::Blacklist.path },
-    format: { with: Gitlab::Regex.path_regex,
-              message: Gitlab::Regex.path_regex_message }
+    format: { with: Gitlab::Regex.namespace_regex,
+              message: Gitlab::Regex.namespace_regex_message }
 
   delegate :name, to: :owner, allow_nil: true, prefix: true
 
diff --git a/app/models/project.rb b/app/models/project.rb
index c50b8a12621..79572f255db 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -124,12 +124,12 @@ class Project < ActiveRecord::Base
     presence: true,
     length: { within: 0..255 },
     format: { with: Gitlab::Regex.project_name_regex,
-              message: Gitlab::Regex.project_regex_message }
+              message: Gitlab::Regex.project_name_regex_message }
   validates :path,
     presence: true,
     length: { within: 0..255 },
-    format: { with: Gitlab::Regex.path_regex,
-              message: Gitlab::Regex.path_regex_message }
+    format: { with: Gitlab::Regex.project_path_regex,
+              message: Gitlab::Regex.project_path_regex_message }
   validates :issues_enabled, :merge_requests_enabled,
             :wiki_enabled, inclusion: { in: [true, false] }
   validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 3fb2ec1d66c..b35e72c4bdb 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -33,8 +33,8 @@ class Snippet < ActiveRecord::Base
   validates :file_name,
     presence: true,
     length: { within: 0..255 },
-    format: { with: Gitlab::Regex.path_regex,
-              message: Gitlab::Regex.path_regex_message }
+    format: { with: Gitlab::Regex.file_name_regex,
+              message: Gitlab::Regex.file_name_regex_message }
   validates :content, presence: true
   validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 3f3a8394d1f..515f29ea0ba 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -129,8 +129,8 @@ class User < ActiveRecord::Base
     presence: true,
     uniqueness: { case_sensitive: false },
     exclusion: { in: Gitlab::Blacklist.path },
-    format: { with: Gitlab::Regex.username_regex,
-              message: Gitlab::Regex.username_regex_message }
+    format: { with: Gitlab::Regex.namespace_regex,
+              message: Gitlab::Regex.namespace_regex_message }
 
   validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
   validate :namespace_uniq, if: ->(user) { user.username_changed? }
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index eeafefc25af..23833aa78ec 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -12,10 +12,10 @@ module Files
       file_name = File.basename(path)
       file_path = path
 
-      unless file_name =~ Gitlab::Regex.path_regex
+      unless file_name =~ Gitlab::Regex.file_name_regex
         return error(
           'Your changes could not be committed, because the file name ' +
-          Gitlab::Regex.path_regex_message
+          Gitlab::Regex.file_name_regex_message
         )
       end
 
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index cf6e260f257..08fcf9c6dc0 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -2,49 +2,64 @@ module Gitlab
   module Regex
     extend self
 
-    def username_regex
-      default_regex
+    def namespace_regex
+      @namespace_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/.freeze
     end
 
-    def username_regex_message
-      default_regex_message
+    def namespace_regex_message
+      "can contain only letters, digits, '_', '-' and '.'. " \
+      "Cannot start with '-' or end in '.git'" \
+    end
+
+
+    def namespace_name_regex
+      @namespace_name_regex ||= /\A[a-zA-Z0-9_\-\. ]*\z/.freeze
     end
 
+    def namespace_name_regex_message
+      "can contain only letters, digits, '_', '-', '.' and space."
+    end
+
+
     def project_name_regex
-      /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\. ]*\z/
+      @project_name_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\. ]*\z/.freeze
     end
 
-    def project_regex_message
-      "can contain only letters, digits, '_', '-' and '.' and space. " \
+    def project_name_regex_message
+      "can contain only letters, digits, '_', '-', '.' and space. " \
       "It must start with letter, digit or '_'."
     end
 
-    def name_regex
-      /\A[a-zA-Z0-9_\-\. ]*\z/
+
+    def project_path_regex
+      @project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/.freeze
     end
 
-    def name_regex_message
-      "can contain only letters, digits, '_', '-' and '.' and space."
+    def project_path_regex_message
+      "can contain only letters, digits, '_', '-' and '.'. " \
+      "Cannot start with '-' or end in '.git'" \
     end
 
-    def path_regex
-      default_regex
+
+    def file_name_regex
+      @file_name_regex ||= /\A[a-zA-Z0-9_\-\.]*\z/.freeze
     end
 
-    def path_regex_message
-      default_regex_message
+    def file_name_regex_message
+      "can contain only letters, digits, '_', '-' and '.'. "
     end
 
+
     def archive_formats_regex
-      #|zip|tar|    tar.gz    |         tar.bz2         |
-      /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/
+      #                           |zip|tar|    tar.gz    |         tar.bz2         |
+      @archive_formats_regex ||= /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/.freeze
     end
 
     def git_reference_regex
       # Valid git ref regex, see:
       # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
 
-      %r{
+      @git_reference_regex ||= %r{
         (?!
            (?# doesn't begins with)
            \/|                    (?# rule #6)
@@ -60,18 +75,7 @@ module Gitlab
         (?# doesn't end with)
         (?<!\.lock)               (?# rule #1)
         (?<![\/.])                (?# rule #6-7)
-      }x
-    end
-
-    protected
-
-    def default_regex_message
-      "can contain only letters, digits, '_', '-' and '.'. " \
-      "Cannot start with '-' or end in '.git'" \
-    end
-
-    def default_regex
-      /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
+      }x.freeze
     end
   end
 end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index f28dfea3ccf..9868b8694cb 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -247,7 +247,7 @@ describe API::API, api: true  do
       expect(json_response['message']['name']).to eq([
         'can\'t be blank',
         'is too short (minimum is 0 characters)',
-        Gitlab::Regex.project_regex_message
+        Gitlab::Regex.project_name_regex_message
       ])
       expect(json_response['message']['path']).to eq([
         'can\'t be blank',
-- 
GitLab