Merge branch '3351-keep-timeout' closes #3351
[arvados.git] / services / api / app / models / commit.rb
index 0e9ae11705866c6daa4ea9ba801796dc755a92cc..8735741ce9bafc1d54a6b505d27983ebc1fcd60a 100644 (file)
@@ -1,11 +1,19 @@
 class Commit < ActiveRecord::Base
   require 'shellwords'
 
+  def self.git_check_ref_format(e)
+    if !e or e.empty? or e[0] == '-' or e[0] == '$'
+      # definitely not valid
+      false
+    else
+      `git check-ref-format --allow-onelevel #{e.shellescape}`
+      $?.success?
+    end
+  end
+
   def self.find_commit_range(current_user, repository, minimum, maximum, exclude)
-    # disallow starting with '-' so verision strings can't be interpreted as command line options
-    valid_pattern = /[A-Za-z0-9_][A-Za-z0-9_-]/
-    if (minimum and !minimum.match valid_pattern) or !maximum.match valid_pattern
-      logger.warn "find_commit_range called with string containing invalid characters: '#{minimum}', '#{maximum}'"
+    if (minimum and !git_check_ref_format(minimum)) or !git_check_ref_format(maximum)
+      logger.warn "find_commit_range called with invalid minimum or maximum: '#{minimum}', '#{maximum}'"
       return nil
     end
 
@@ -28,39 +36,34 @@ class Commit < ActiveRecord::Base
       readable = readable.where(name: repository)
     end
 
-    #puts "min #{minimum}"
-    #puts "max #{maximum}"
-    #puts "rep #{repository}"
-
     commits = []
     readable.each do |r|
       if on_disk_repos[r.name]
         ENV['GIT_DIR'] = on_disk_repos[r.name][:git_dir]
 
-        #puts "dir #{on_disk_repos[r.name][:git_dir]}"
-
         # We've filtered for invalid characters, so we can pass the contents of
         # minimum and maximum safely on the command line
 
-        #puts "git rev-list --max-count=1 #{maximum}"
-
         # Get the commit hash for the upper bound
         max_hash = nil
-        IO.foreach("|git rev-list --max-count=1 #{maximum}") do |line|
+        IO.foreach("|git rev-list --max-count=1 #{maximum.shellescape} --") do |line|
           max_hash = line.strip
         end
 
         # If not found or string is invalid, nothing else to do
-        next if !max_hash or !max_hash.match valid_pattern
+        next if !max_hash or !git_check_ref_format(max_hash)
 
         resolved_exclude = nil
         if exclude
           resolved_exclude = []
           exclude.each do |e|
-            if e.match valid_pattern
-              IO.foreach("|git rev-list --max-count=1 #{e}") do |line|
+            if git_check_ref_format(e)
+              IO.foreach("|git rev-list --max-count=1 #{e.shellescape} --") do |line|
                 resolved_exclude.push(line.strip)
               end
+            else
+              logger.warn "find_commit_range called with invalid exclude invalid characters: '#{exclude}'"
+              return nil
             end
           end
         end
@@ -68,16 +71,15 @@ class Commit < ActiveRecord::Base
         if minimum
           # Get the commit hash for the lower bound
           min_hash = nil
-          IO.foreach("|git rev-list --max-count=1 #{minimum}") do |line|
+          IO.foreach("|git rev-list --max-count=1 #{minimum.shellescape} --") do |line|
             min_hash = line.strip
           end
 
           # If not found or string is invalid, nothing else to do
-          next if !min_hash or !min_hash.match valid_pattern
+          next if !min_hash or !git_check_ref_format(min_hash)
 
           # Now find all commits between them
-          #puts "git rev-list #{min_hash}..#{max_hash}"
-          IO.foreach("|git rev-list #{min_hash}..#{max_hash}") do |line|
+          IO.foreach("|git rev-list #{min_hash.shellescape}..#{max_hash.shellescape} --") do |line|
             hash = line.strip
             commits.push(hash) if !resolved_exclude or !resolved_exclude.include? hash
           end
@@ -137,6 +139,7 @@ class Commit < ActiveRecord::Base
       next if repo.match /^\./
       git_dir = File.join(@gitdirbase,
                           repo.match(/\.git$/) ? repo : File.join(repo, '.git'))
+      next if git_dir == Rails.configuration.git_internal_dir
       repo_name = repo.sub(/\.git$/, '')
       @repositories[repo_name] = {git_dir: git_dir}
     end