#!/usr/bin/env bash set -euo pipefail # Sets up gitleaks as a git pre-commit hook in a target repository. # Usage: setup-gitleaks.sh [TARGET_REPO] # TARGET_REPO — path to the git repo to configure (default: current directory) # Idempotent: safe to re-run; always replaces the hook block with the current version. GITLEAKS_VERSION="8.24.2" GITLEAKS_INSTALL_DIR="${GITLEAKS_INSTALL_DIR:-/usr/local/bin}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TARGET="${1:-$(pwd)}" HOOK_FILE="$TARGET/.git/hooks/pre-commit" CONFIG_SRC="$SCRIPT_DIR/gitleaks.toml" CONFIG_DEST="$TARGET/.gitleaks.toml" MARKER="# managed by setup-gitleaks.sh" END_MARKER="# end gitleaks" # --- Install gitleaks if not present --- install_gitleaks() { local os arch tarball url tmp_dir case "$(uname -s)" in Linux) os="linux" ;; Darwin) os="darwin" ;; *) echo "Error: unsupported OS '$(uname -s)' — install gitleaks manually from https://github.com/gitleaks/gitleaks/releases" >&2 exit 1 ;; esac case "$(uname -m)" in x86_64) arch="x64" ;; aarch64 | arm64) arch="arm64" ;; *) echo "Error: unsupported architecture '$(uname -m)' — install gitleaks manually from https://github.com/gitleaks/gitleaks/releases" >&2 exit 1 ;; esac tarball="gitleaks_${GITLEAKS_VERSION}_${os}_${arch}.tar.gz" url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${tarball}" tmp_dir="$(mktemp -d)" trap 'rm -rf "$tmp_dir"' RETURN echo "Installing gitleaks v${GITLEAKS_VERSION}..." curl -fsSL "$url" -o "$tmp_dir/$tarball" tar -xzf "$tmp_dir/$tarball" -C "$tmp_dir" gitleaks install -m 755 "$tmp_dir/gitleaks" "$GITLEAKS_INSTALL_DIR/gitleaks" echo "Installed: $GITLEAKS_INSTALL_DIR/gitleaks" } if ! command -v gitleaks &>/dev/null; then install_gitleaks fi # --- Validate --- if [ ! -d "$TARGET/.git" ]; then echo "Error: $TARGET is not a git repository" >&2 exit 1 fi if [ ! -f "$CONFIG_SRC" ]; then echo "Error: config template not found at $CONFIG_SRC" >&2 exit 1 fi # --- Deploy config --- if [ -f "$CONFIG_DEST" ]; then echo "Skipped: $CONFIG_DEST already exists — edit it directly to customise rules." else cp "$CONFIG_SRC" "$CONFIG_DEST" echo "Wrote: $CONFIG_DEST" echo " Commit this file — it belongs in version control." fi # --- Deploy hook --- hook_block() { cat </dev/null; then gitleaks git --staged --redact -v else echo "Warning: gitleaks not installed — secret scan skipped (https://github.com/gitleaks/gitleaks/releases)" >&2 fi $END_MARKER BLOCK } write_hook() { local hook_file="$1" if grep -qF "$MARKER" "$hook_file"; then # Remove old block (start marker through end marker inclusive) then append current version awk -v start="$MARKER" -v end="$END_MARKER" ' $0 == start { skip=1; next } skip && $0 == end { skip=0; next } !skip { print } ' "$hook_file" > "${hook_file}.tmp" && mv "${hook_file}.tmp" "$hook_file" hook_block >> "$hook_file" echo "Updated: $hook_file (gitleaks block replaced)" else hook_block >> "$hook_file" echo "Updated: $hook_file (gitleaks block appended to existing hook)" fi } if [ -f "$HOOK_FILE" ]; then write_hook "$HOOK_FILE" else { echo '#!/usr/bin/env bash'; echo 'set -euo pipefail'; hook_block; } > "$HOOK_FILE" chmod +x "$HOOK_FILE" echo "Created: $HOOK_FILE" fi echo "" echo "Done. Staged secrets will be scanned on every commit in $TARGET." echo "To skip on a single commit: SKIP=gitleaks git commit ..."