100 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env ruby
 | |
| 
 | |
| # frozen_string_literal: true
 | |
| 
 | |
| require 'digest'
 | |
| require 'fileutils'
 | |
| 
 | |
| if ENV['NO_COLOR']
 | |
|   SHELL_RED    = ''
 | |
|   SHELL_GREEN  = ''
 | |
|   SHELL_YELLOW = ''
 | |
|   SHELL_CLEAR  = ''
 | |
| else
 | |
|   SHELL_RED    = "\e[1;31m"
 | |
|   SHELL_GREEN  = "\e[1;32m"
 | |
|   SHELL_YELLOW = "\e[1;33m"
 | |
|   SHELL_CLEAR  = "\e[0m"
 | |
| end
 | |
| 
 | |
| HOOK_PATH = File.expand_path("../.git/hooks/pre-push", __dir__)
 | |
| HOOK_DATA = <<~HOOK
 | |
|   #!/bin/bash
 | |
| 
 | |
|   set -e
 | |
| 
 | |
|   url="$2"
 | |
|   harness=`dirname "$0"`/../security_harness
 | |
| 
 | |
|   if [ -e "$harness" ]
 | |
|   then
 | |
|     if [["$url" != *"gitlab-org/security/"*]]
 | |
|     then
 | |
|       echo "Pushing to remotes other than gitlab.com/gitlab-org/security has been disabled!"
 | |
|       echo "Run scripts/security-harness to disable this check."
 | |
|       echo
 | |
| 
 | |
|       exit 1
 | |
|     fi
 | |
|   fi
 | |
| HOOK
 | |
| 
 | |
| def write_hook
 | |
|   FileUtils.mkdir_p(File.dirname(HOOK_PATH))
 | |
|   File.open(HOOK_PATH, 'w') do |file|
 | |
|     file.write(HOOK_DATA)
 | |
|   end
 | |
|   File.chmod(0755, HOOK_PATH)
 | |
| end
 | |
| 
 | |
| # Toggle the harness on or off
 | |
| def toggle
 | |
|   harness_path = File.expand_path('../.git/security_harness', __dir__)
 | |
| 
 | |
|   if File.exist?(harness_path)
 | |
|     FileUtils.rm(harness_path)
 | |
| 
 | |
|     puts "#{SHELL_YELLOW}Security harness removed -- you can now push to all remotes.#{SHELL_CLEAR}"
 | |
|   else
 | |
|     FileUtils.touch(harness_path)
 | |
| 
 | |
|     puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to gitlab.com/gitlab-org/security!#{SHELL_CLEAR}"
 | |
|   end
 | |
| end
 | |
| 
 | |
| # If we were to change the script and then check for a pre-existing hook before
 | |
| # writing, the check would fail even if the user had an unmodified version of
 | |
| # the old hook. Checking previous version hashes allows us to safely overwrite a
 | |
| # script that differs from the current version, as long as it's an old one and
 | |
| # not custom.
 | |
| def previous_version?(dest_sum)
 | |
|   # SHA256 hashes of previous iterations of the script contained in `DATA`
 | |
|   %w[
 | |
|     010bf0363a911ebab2bd5728d80795ed02388da51815f0b2530d08ae8ac574f0
 | |
|   ].include?(dest_sum)
 | |
| end
 | |
| 
 | |
| if !File.exist?(HOOK_PATH)
 | |
|   write_hook
 | |
|   toggle
 | |
| else
 | |
|   # Deal with a pre-existing hook
 | |
|   source_sum = Digest::SHA256.hexdigest(HOOK_DATA)
 | |
|   dest_sum   = Digest::SHA256.file(HOOK_PATH).hexdigest
 | |
| 
 | |
|   if previous_version?(dest_sum)
 | |
|     # Upgrading from a previous version, update in-place
 | |
|     write_hook
 | |
|     toggle
 | |
|   elsif source_sum != dest_sum
 | |
|     # Pre-existing hook we didn't create; do nothing
 | |
|     puts "#{SHELL_RED}#{HOOK_PATH} exists and is different from our hook!"
 | |
|     puts "Remove it and re-run this script to continue.#{SHELL_CLEAR}"
 | |
| 
 | |
|     exit 1
 | |
|   else
 | |
|     # No hook update needed, just toggle
 | |
|     toggle
 | |
|   end
 | |
| end
 |