1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
#!/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
LEFTHOOK_GLOBAL_CONFIG_PATH = File.expand_path("../lefthook.yml", __dir__)
HOOK_PATH = `git rev-parse --path-format=absolute --git-path hooks/pre-push`.split.last
HOOK_DATA = <<~HOOK
#!/usr/bin/env bash
set -e
url="$2"
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
HOOK
def hook_exist?
File.exist?(HOOK_PATH)
end
def lefthook_hook_in_place?
hook_exist? && File.foreach(HOOK_PATH).grep(/lefthook/i).any?
end
def lefthook_available?
system('bundle exec lefthook run prepare-commit-msg &>/dev/null')
end
def uninstall_lefthook
return unless lefthook_available?
system('bundle exec lefthook uninstall')
# `bundle exec lefthook uninstall` removes the `lefthook.yml` file so we checkout it again
system("git checkout -- #{LEFTHOOK_GLOBAL_CONFIG_PATH}") # rubocop:disable GitlabSecurity/SystemCommandInjection
puts "#{SHELL_YELLOW}Lefthook was uninstalled to let the security harness work properly.#{SHELL_CLEAR}"
end
def install_lefthook
return unless lefthook_available?
system('bundle exec lefthook install')
puts "#{SHELL_GREEN}Lefthook was re-installed.#{SHELL_CLEAR}"
end
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)
puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to gitlab.com/gitlab-org/security!#{SHELL_CLEAR}"
end
def delete_hook
FileUtils.rm(HOOK_PATH)
system("git checkout master")
puts "#{SHELL_YELLOW}Security harness removed -- you can now push to all remotes.#{SHELL_CLEAR}"
end
def hook_file_sum
Digest::SHA256.file(HOOK_PATH).hexdigest
end
def hook_data_sum
Digest::SHA256.hexdigest(HOOK_DATA)
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 upgrade_available?
# SHA256 hashes of previous iterations of the script contained in `HOOK_DATA`
%w[
010bf0363a911ebab2bd5728d80795ed02388da51815f0b2530d08ae8ac574f0
d9866fc672f373d631eed9cd8dc9c920fa3d36ff26d956fb96a4082a0931b371
].include?(hook_file_sum)
end
def current_version?
hook_data_sum == hook_file_sum
end
# Uninstall Lefthook if it's in place
uninstall_lefthook if lefthook_hook_in_place?
if hook_exist?
# Deal with a pre-existing hook
if upgrade_available?
# Upgrading from a previous version, update in-place
write_hook
elsif current_version?
# Delete the hook if we're already using the current version
delete_hook
# Re-install Lefthook pre-push hook
install_lefthook
else
# 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
end
else
write_hook
end
|