Skip to content

Instantly share code, notes, and snippets.

@drgsn
Created March 2, 2023 21:37
Show Gist options
  • Save drgsn/900559fc321b43d4a7ea14a8f4345c71 to your computer and use it in GitHub Desktop.
Save drgsn/900559fc321b43d4a7ea14a8f4345c71 to your computer and use it in GitHub Desktop.
[git secrets install] install git secrets and do a basic config
#!/bin/bash
pretty_print() {
# BLACK=$(tput setaf 0)
# RED=$(tput setaf 1)
# GREEN=$(tput setaf 2)
# YELLOW=$(tput setaf 3)
# LIME_YELLOW=$(tput setaf 190)
# POWDER_BLUE=$(tput setaf 153)
# BLUE=$(tput setaf 4)
# MAGENTA=$(tput setaf 5)
# CYAN=$(tput setaf 6)
# WHITE=$(tput setaf 7)
# BRIGHT=$(tput bold)
# NORMAL=$(tput sgr0)
# BLINK=$(tput blink)
# REVERSE=$(tput smso)
# UNDERLINE=$(tput smul)
printf "\n%b\n" "$1"
}
function info() {
pretty_print "$(date) INFO: $1"
}
function error() {
local RED=$(tput setaf 1)
local NORMAL=$(tput sgr0)
pretty_print "$(date): ${RED}ERR: ${NORMAL} $1" >&2
}
function halt() {
pretty_print "$(date) HALT: $1" >&2
error "$1"
exit 1
}
function updateSystem() {
if [[ $1 == $MAC_OS ]]; then
# check if homebrew is installed and install if not or update if it is.
homebrewInstall
elif [[ $1 == "$LINUX" ]]; then
info "Updating system..."
sudo apt-get update
sudo apt-get upgrade -y
else
halt "$UNSUPPORTED_OS"
fi
}
function homebrewInstall() {
if ! command -v brew &>/dev/null; then
info "Installing Homebrew, an OSX package manager, follow the instructions..."
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
if ! grep -qs "recommended by brew doctor" ~/.zshrc; then
pretty_print "Put Homebrew location earlier in PATH ..."
printf '\n# recommended by brew doctor\n' >>~/.zshrc
printf 'export PATH="/usr/local/bin:$PATH"\n' >>~/.zshrc
export PATH="/usr/local/bin:$PATH"
fi
else
info "Updating Homebrew..."
brew update
info "Upgrading Homebrew..."
brew upgrade
fi
}
function checkAndInstallPackage() {
local successMessage="$2 is installed..."
local errorMessage="$2 is not installed. Installing it now!"
if [[ $1 == "$MAC_OS" ]]; then
if [[ $(brew list | grep $2) ]]; then
info "$successMessage"
else
info "$errorMessage"
installPackage $1 $2
fi
elif [[ $1 == "$LINUX" ]]; then
if [[ $(dpkg -l | grep $2) ]]; then
info "$successMessage"
else
info "$errorMessage"
installPackage $1 $2
fi
else
halt "$UNSUPPORTED_OS"
fi
}
function installPackage() {
info "Installing $2"
if [[ $1 == "$MAC_OS" ]]; then
brew install $2
elif [[ $1 == "$LINUX" ]]; then
sudo apt-get install -y $2
else
halt "$UNSUPPORTED_OS"
fi
}
function install_git_hook() {
cd "$1" || exit
git secrets --install
}
function register_rules() {
# $1 = Path to git repo
# $2 = Selected rules
# $3 = Available rules
local path=$1
IFS=' ' read -ra selected <<<"${2}"
IFS=' ' read -ra options <<<"${3}"
cd "$path" || exit
for i in "${!selected[@]}"; do
if [ "${selected[$i]}" == "true" ]; then
info "Trying to register rule: ${options[$i]}"
case "${options[$i]}" in
AWS*) register_aws_rule ;;
GENERIC_PASSWORD*) register_generic_password_rule ;;
GCP_API_KEY*) register_gcp_api_key_rule ;;
SSH_KEY*) register_ssh_key_rule ;;
*) error "no handler defined for ${options[$i]}" ;;
esac
fi
done
}
function register_aws_rule() {
# Adds common AWS patterns to the git config and ensures that keys present
# in ~/.aws/credentials are not found in any commit. The following checks are added:
#
# AWS Access Key IDs via (A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
# AWS Secret Access Key assignments via ":" or "=" surrounded by optional quotes
# AWS account ID assignments via ":" or "=" surrounded by optional quotes
# Allowed patterns for example AWS keys (AKIAIOSFODNN7EXAMPLE and wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY)
# Known credentials from ~/.aws/credentials
git secrets --register-aws
info "AWS rule added"
}
function register_generic_password_rule() {
local pattern="'password\s*=\s*.+'"
git secrets --add "$pattern"
info "Password rule added"
}
function register_gcp_api_key_rule() {
local pattern="AIza[0-9A-Za-z\\-_]{35}"
git secrets --add "$pattern"
info "Password GCP API Key added"
}
function register_ssh_key_rule() {
local pattern1="-----BEGIN RSA PRIVATE KEY-----"
local pattern2="-----BEGIN DSA PRIVATE KEY-----"
local pattern3="-----BEGIN EC PRIVATE KEY-----"
local pattern4="-----BEGIN PGP PRIVATE KEY BLOCK-----"
git secrets --add "$pattern1"
git secrets --add "$pattern2"
git secrets --add "$pattern3"
git secrets --add "$pattern4"
info "SSH Key rule added"
}
function scan_history() {
cd "$1" || exit
git secrets --scan-history
}
function detect_os() {
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) machine=Linux ;;
Darwin*) machine=Mac ;;
CYGWIN*) machine=Cygwin ;;
MINGW*) machine=MinGw ;;
*) machine="UNKNOWN:${unameOut}" ;;
esac
info "Detected OS: $machine"
}
function project_path() {
info "provided path $1"
if [[ "$1" && -d "$1/.git" ]]; then
info "Using project path: $1"
PROJECT_PATH=$1
elif [ "$1" ]; then
info "Provided path is not a git project: $1"
project_path
else
read -r -p "Please enter git project path:" r1
if [[ -d $r1 && -d "$r1/.git" ]]; then
info "Installing git-secrets for $r1"
PROJECT_PATH=$r1
elif [[ -d $r1 ]]; then
error "$r1 is not a git repository"
project_path
else
error "$r1 is not a valid path"
project_path
fi
fi
}
function parse_args() {
# https://unix.stackexchange.com/questions/129391/passing-named-arguments-to-shell-scripts
while getopts ":p:" opt; do
case $opt in
p)
# info "Using project path: $OPTARG"
PROJECT_PATH="$OPTARG"
;;
\?)
error "Invalid option -$OPTARG will prompt for project path!"
# exit 1 # uncomment this line to exit if invalid option is passed. for the moment this is non fatal.
;;
esac
case $OPTARG in
-*)
error "Option $opt needs a valid argument"
# exit 1 # uncomment this line to exit if invalid option is passed. for the moment this is non fatal.
;;
esac
done
}
function main() {
# ===== vars =====
MAC_OS="Mac"
LINUX="Linux"
UNSUPPORTED_OS="Unsupported OS"
local OPTIONS=("AWS" "GENERIC_PASSWORD" "GCP_API_KEY")
local OPTIONS_STRING=$(
IFS=';'
echo "${OPTIONS[*]}"
)
error "OPTIONS_STRING: $OPTIONS_STRING"
# ===== parse cmd args =====
parse_args "$@"
# ===== detect os =====
detect_os
# ===== check if supported os =====
if [[ $machine != "$MAC_OS" && $machine != "$LINUX" ]]; then
halt "OS is not supported"
fi
# ===== check if git project else get path =====
project_path "$PROJECT_PATH"
# ===== update system =====
updateSystem "$machine"
# ===== install git secrets =====
info "Installing git-secrets for $PROJECT_PATH"
checkAndInstallPackage "$machine" "git-secrets"
prompt_for_multiselect SELECTED "$OPTIONS_STRING"
# ===== install git hook =====
install_git_hook "$PROJECT_PATH"
# ===== register aws rule =====
register_rules "$PROJECT_PATH" "${SELECTED[*]}" "${OPTIONS[*]}"
# ===== scan repo history =====
scan_history "$PROJECT_PATH"
info "For more info checkout https://github.com/awslabs/git-secrets#options-for-add-provider"
info "todo: https://gist.github.com/tehryanx/44657fcad0b97589d99ea3b282e411e7"
info "todo: https://github.com/odomojuli/RegExAPI"
info "https://github.com/zricethezav/gitleaks/blob/master/config/gitleaks.toml"
}
function prompt_for_multiselect {
echo "Navigate using ARROWS"
echo "Use SPACE to select"
echo "Use ENTER to finish"
# little helpers for terminal print control and key input
GREEN='\033[00;32m'
YELLOW='\033[00;33m'
RESTORE='\033[0m'
ESC=$(printf "\033")
# shellcheck disable=SC2059
# shellcheck disable=SC1087
cursor_blink_on() { printf "$ESC[?25h"; }
# shellcheck disable=SC2059
# shellcheck disable=SC1087
cursor_blink_off() { printf "$ESC[?25l"; }
# shellcheck disable=SC2059
# shellcheck disable=SC1087
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
# shellcheck disable=SC2059
# shellcheck disable=SC1087
print_inactive() { printf "$2 $1 $RESTORE"; }
# shellcheck disable=SC2059
# shellcheck disable=SC1087
print_active() { printf "$YELLOW$2 $ESC[7m $1 $RESTORE$ESC[27m"; }
get_cursor_row() {
# shellcheck disable=SC2162
IFS=';' read -sdR -p $'\E[6n' ROW COL
echo "${ROW#*[}"
}
key_input() {
local key
IFS= read -rsn1 key 2>/dev/null >&2
if [[ $key = "" ]]; then echo enter; fi
if [[ $key = $'\x20' ]]; then echo space; fi
if [[ $key = $'\x1b' ]]; then
read -rsn2 key
if [[ $key = [A ]]; then echo up; fi
if [[ $key = [B ]]; then echo down; fi
fi
}
toggle_option() {
local arr_name=$1
eval "local arr=(\"\${${arr_name}[@]}\")"
local option=$2
if [[ ${arr[option]} == true ]]; then
arr[option]=
else
arr[option]=true
fi
eval $arr_name='("${arr[@]}")'
}
local retval=$1
local options
local defaults
IFS=';' read -r -a options <<<"$2"
if [[ -z $3 ]]; then
defaults=()
else
IFS=';' read -r -a defaults <<<"$3"
fi
local selected=()
for ((i = 0; i < ${#options[@]}; i++)); do
selected+=("${defaults[i]:-false}")
printf "\n"
done
# determine current screen position for overwriting the options
local lastrow=$(get_cursor_row)
local startrow=$(($lastrow - ${#options[@]}))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
local active=0
while true; do
# print options by overwriting the last lines
local idx=0
for option in "${options[@]}"; do
local prefix="$RESET ◻︎"
if [[ ${selected[idx]} == true ]]; then
prefix="$GREEN ◼︎"
fi
cursor_to $(($startrow + $idx))
if [ $idx -eq $active ]; then
print_active "$option" "$prefix"
else
print_inactive "$option" "$prefix"
fi
((idx++))
done
# user key control
case $(key_input) in
space) toggle_option selected $active ;;
enter) break ;;
up)
((active--))
if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi
;;
down)
((active++))
if [ $active -ge ${#options[@]} ]; then active=0; fi
;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
eval $retval='("${selected[@]}")'
}
# running main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment