1+ # Requires -Version 5.1
2+ # Script to install gitleaks globally with pre-commit hooks for all repos (Windows)
3+ # This sets up:
4+ # 1. Gitleaks binary in %LOCALAPPDATA%\gitleaks\bin (added to user PATH)
5+ # 2. Global gitleaks config in %USERPROFILE%\.config\gitleaks\
6+ # 3. Git template directory for automatic hook installation in new repos
7+ # 4. Instructions for updating existing repos
8+ #
9+ # Run in PowerShell: .\install-gitleaks-global.ps1
10+ # If you get execution policy error: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
11+
12+ $ErrorActionPreference = " Stop"
13+ $GITLEAKS_VERSION = " 8.24.2"
14+ $GITLEAKS_BIN_DIR = Join-Path $env: LOCALAPPDATA " gitleaks\bin"
15+ $CONFIG_DIR = Join-Path $env: USERPROFILE " .config\gitleaks"
16+ $TEMPLATE_DIR = Join-Path $env: USERPROFILE " .git-template"
17+ $TEMPLATE_HOOKS = Join-Path $TEMPLATE_DIR " hooks" )
18+
19+ function Write-Step { param ($Message ) Write-Host $Message - ForegroundColor Cyan }
20+ function Write-Ok { param ($Message ) Write-Host " OK $Message " - ForegroundColor Green }
21+ function Write-Warn { param ($Message ) Write-Host " !! $Message " - ForegroundColor Yellow }
22+ function Write-Fail { param ($Message ) Write-Host " X $Message " - ForegroundColor Red }
23+
24+ Write-Host " `n Installing Gitleaks globally...`n " - ForegroundColor Cyan
25+
26+ # Step 1: Check and install gitleaks binary
27+ Write-Step " Step 1: Installing gitleaks binary..."
28+
29+ $SkipBinary = $false
30+ $existing = Get-Command gitleaks - ErrorAction SilentlyContinue
31+ if ($existing ) {
32+ try {
33+ $currentVer = (gitleaks version 2>&1 ) -join " "
34+ Write-Warn " Gitleaks is already installed: $currentVer "
35+ $reply = Read-Host " Reinstall/update to v$GITLEAKS_VERSION ? (y/N)"
36+ if ($reply -notmatch ' ^[Yy]$' ) {
37+ Write-Ok " Keeping existing gitleaks installation"
38+ $SkipBinary = $true
39+ }
40+ } catch {}
41+ }
42+
43+ if (-not $SkipBinary ) {
44+ $zipUrl = " https://github.com/gitleaks/gitleaks/releases/download/v$GITLEAKS_VERSION /gitleaks_${GITLEAKS_VERSION} _windows_x64.zip"
45+ $tempDir = Join-Path $env: TEMP " gitleaks-install-$ ( [Guid ]::NewGuid().ToString(' N' ).Substring(0 , 8 )) " )
46+ New-Item - ItemType Directory - Path $tempDir - Force | Out-Null
47+ try {
48+ Write-Step " Downloading gitleaks v$GITLEAKS_VERSION ..."
49+ $zipPath = Join-Path $tempDir " gitleaks.zip"
50+ Invoke-WebRequest - Uri $zipUrl - OutFile $zipPath - UseBasicParsing
51+ Write-Ok " Downloaded gitleaks"
52+
53+ Expand-Archive - Path $zipPath - DestinationPath $tempDir - Force
54+ $exeSource = Join-Path $tempDir " gitleaks.exe"
55+ if (-not (Test-Path $exeSource )) {
56+ $exeSource = Get-ChildItem - Path $tempDir - Filter " gitleaks.exe" - Recurse - ErrorAction SilentlyContinue | Select-Object - First 1 - ExpandProperty FullName
57+ }
58+ if (-not $exeSource -or -not (Test-Path $exeSource )) {
59+ Write-Fail " gitleaks.exe not found in archive"
60+ exit 1
61+ }
62+
63+ New-Item - ItemType Directory - Path $GITLEAKS_BIN_DIR - Force | Out-Null
64+ Copy-Item - Path $exeSource - Destination (Join-Path $GITLEAKS_BIN_DIR " gitleaks.exe" ) - Force
65+ Write-Ok " Installed gitleaks to $GITLEAKS_BIN_DIR "
66+
67+ # Add to user PATH if not already present
68+ $userPath = [Environment ]::GetEnvironmentVariable(" Path" , " User" )
69+ if ($userPath -notlike " *$GITLEAKS_BIN_DIR *" ) {
70+ [Environment ]::SetEnvironmentVariable(" Path" , " $userPath ;$GITLEAKS_BIN_DIR " , " User" )
71+ $env: Path = " $env: Path ;$GITLEAKS_BIN_DIR "
72+ Write-Ok " Added gitleaks to user PATH (new terminals will pick it up)"
73+ }
74+
75+ # Verify
76+ $ver = & (Join-Path $GITLEAKS_BIN_DIR " gitleaks.exe" ) version 2>&1
77+ Write-Ok " Verified: $ver "
78+ } finally {
79+ Remove-Item - Path $tempDir - Recurse - Force - ErrorAction SilentlyContinue
80+ }
81+ }
82+
83+ Write-Host " "
84+
85+ # Step 2: Global config
86+ Write-Step " Step 2: Setting up global configuration..."
87+ New-Item - ItemType Directory - Path $CONFIG_DIR - Force | Out-Null
88+ $configSource = Join-Path $PSScriptRoot " .gitleaks.toml"
89+ if (Test-Path $configSource ) {
90+ Copy-Item - Path $configSource - Destination (Join-Path $CONFIG_DIR " gitleaks.toml" ) - Force
91+ Write-Ok " Copied gitleaks config to $CONFIG_DIR \gitleaks.toml"
92+ } else {
93+ Write-Warn " .gitleaks.toml not found in script dir; config not copied. Create $CONFIG_DIR \gitleaks.toml manually if needed."
94+ }
95+
96+ Write-Host " "
97+
98+ # Step 3: Git template and hooks
99+ Write-Step " Step 3: Creating git template directory..."
100+ New-Item - ItemType Directory - Path $TEMPLATE_HOOKS - Force | Out-Null
101+
102+ $preCommitHook = @'
103+ #!/bin/bash
104+
105+ # Gitleaks pre-commit hook (Smart Auto-Detecting)
106+ # Prevents committing secrets to git repository
107+ # Automatically detects and adapts to Husky or native Git hooks
108+
109+ # Colors
110+ RED='\033[0;31m'
111+ GREEN='\033[0;32m'
112+ YELLOW='\033[1;33m'
113+ NC='\033[0m' # No Color
114+
115+ # Function to run gitleaks scan
116+ run_gitleaks_scan() {
117+ # Check if gitleaks is installed
118+ if ! command -v gitleaks &> /dev/null; then
119+ echo -e "${RED}Error: gitleaks is not installed${NC}"
120+ echo "Install it from: https://github.com/gitleaks/gitleaks"
121+ echo "Or run: brew install gitleaks (macOS) or go install github.com/gitleaks/gitleaks/v8@latest"
122+ return 1
123+ fi
124+
125+ # Use global config if exists, otherwise use default
126+ GITLEAKS_CONFIG="$HOME/.config/gitleaks/gitleaks.toml"
127+ if [ ! -f "$GITLEAKS_CONFIG" ]; then
128+ GITLEAKS_CONFIG=""
129+ fi
130+
131+ # Run gitleaks on staged changes
132+ echo -e "${YELLOW}🔍 Scanning for secrets with gitleaks...${NC}"
133+
134+ if [ -n "$GITLEAKS_CONFIG" ]; then
135+ gitleaks protect --staged --redact --config="$GITLEAKS_CONFIG" --verbose
136+ else
137+ gitleaks protect --staged --redact --verbose
138+ fi
139+
140+ if [ $? -eq 0 ]; then
141+ echo -e "${GREEN}✓ No secrets detected${NC}"
142+ return 0
143+ else
144+ echo -e "${RED}✗ Secrets detected! Commit blocked.${NC}"
145+ return 1
146+ fi
147+ }
148+
149+ # SMART DETECTION: Check if Husky is managing hooks
150+ if [ -d ".husky" ] && [ -f ".husky/pre-commit" ]; then
151+ # Husky detected - check if it already has gitleaks
152+ if grep -q "gitleaks" ".husky/pre-commit" 2>/dev/null; then
153+ # Husky already has gitleaks, let it handle everything
154+ exit 0
155+ else
156+ # Husky exists but doesn't have gitleaks - run scan here
157+ run_gitleaks_scan
158+ exit $?
159+ fi
160+ else
161+ # No Husky detected - run gitleaks in native mode
162+ run_gitleaks_scan
163+ exit $?
164+ fi
165+ '@
166+
167+ $commitMsgHook = @'
168+ #!/bin/bash
169+ # Gitleaks commit-msg hook (Smart Auto-Detecting)
170+ # This is a secondary check in case pre-commit was bypassed
171+
172+ # Skip if gitleaks not installed
173+ if ! command -v gitleaks &> /dev/null; then
174+ exit 0
175+ fi
176+
177+ # Skip if Husky is managing hooks and already has gitleaks configured
178+ if [ -d ".husky" ] && [ -f ".husky/pre-commit" ]; then
179+ if grep -q "gitleaks" ".husky/pre-commit" 2>/dev/null; then
180+ # Husky is handling gitleaks, no need to run again
181+ exit 0
182+ fi
183+ fi
184+
185+ # Run gitleaks check
186+ GITLEAKS_CONFIG="$HOME/.config/gitleaks/gitleaks.toml"
187+ if [ ! -f "$GITLEAKS_CONFIG" ]; then
188+ GITLEAKS_CONFIG=""
189+ fi
190+
191+ # Silent check on commit
192+ if [ -n "$GITLEAKS_CONFIG" ]; then
193+ gitleaks protect --staged --redact --config="$GITLEAKS_CONFIG" > /dev/null 2>&1
194+ else
195+ gitleaks protect --staged --redact > /dev/null 2>&1
196+ fi
197+
198+ if [ $? -ne 0 ]; then
199+ echo "Error: Secrets detected in commit. Aborting."
200+ exit 1
201+ fi
202+
203+ exit 0
204+ '@
205+
206+ # Write hooks with LF line endings (Git for Windows runs them with bash)
207+ $preCommitPath = Join-Path $TEMPLATE_HOOKS " pre-commit"
208+ $commitMsgPath = Join-Path $TEMPLATE_HOOKS " commit-msg"
209+ [System.IO.File ]::WriteAllText($preCommitPath , $preCommitHook.Replace (" `r`n " , " `n " ))
210+ [System.IO.File ]::WriteAllText($commitMsgPath , $commitMsgHook.Replace (" `r`n " , " `n " ))
211+ Write-Ok " Created pre-commit and commit-msg hooks in $TEMPLATE_HOOKS "
212+
213+ Write-Host " "
214+
215+ # Step 4: Configure git template
216+ Write-Step " Step 4: Configuring git to use template directory..."
217+ $templateDirNorm = $TEMPLATE_DIR -replace ' \\' , ' /'
218+ git config -- global init.templateDir $templateDirNorm
219+ Write-Ok " Set global git template directory"
220+
221+ Write-Host " "
222+ Write-Host " ========================================" - ForegroundColor Green
223+ Write-Host " Installation Complete!" - ForegroundColor Green
224+ Write-Host " ========================================`n " - ForegroundColor Green
225+
226+ Write-Host " What was installed:" - ForegroundColor Cyan
227+ Write-Host " * Gitleaks binary: $GITLEAKS_BIN_DIR \gitleaks.exe"
228+ Write-Host " * Global config: $CONFIG_DIR \gitleaks.toml"
229+ Write-Host " * Git template: $TEMPLATE_HOOKS "
230+ Write-Host " * Hooks: pre-commit, commit-msg"
231+
232+ Write-Host " `n Next steps:" - ForegroundColor Cyan
233+ Write-Host " 1. All NEW git repositories will automatically get gitleaks hooks."
234+ Write-Host " 2. For EXISTING repos, run: .\update-all-repos.ps1 [directory]"
235+ Write-Host " 3. Or in each repo: git init (re-run to apply template)"
236+ Write-Host " `n Note: If gitleaks is not found in a new terminal, close and reopen PowerShell, or run:"
237+ Write-Host " `$ env:Path += `" ;$GITLEAKS_BIN_DIR `" "
238+ Write-Host " "
0 commit comments