diff --git a/.husky/pre-commit b/.husky/pre-commit index 8af5aa08..2af5759b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,28 +1,3 @@ # Run lint-staged (lint + test changed files) npx lint-staged - -# Check for secrets (only if Docker daemon is running) -if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then - echo "🔍 Checking for secrets with Gitleaks..." - npm run validate:secrets || { - echo "⚠️ Secrets detected! Please remove sensitive data before committing." - exit 1 - } -else - # Docker not available - provide helpful hints based on installed alternatives - if command -v colima >/dev/null 2>&1; then - echo "⚠️ Docker daemon not running - Colima is installed" - echo "💡 Run 'colima start' to enable secrets detection locally" - elif command -v podman >/dev/null 2>&1; then - echo "⚠️ Docker daemon not running - Podman is installed" - echo "💡 Run 'podman machine start' to enable secrets detection locally" - elif command -v orbstack >/dev/null 2>&1; then - echo "⚠️ Docker daemon not running - OrbStack is installed" - echo "💡 Start OrbStack to enable secrets detection locally" - elif command -v docker >/dev/null 2>&1; then - echo "⚠️ Docker installed but daemon not running" - echo "💡 Start Docker Desktop to enable secrets detection locally" - else - echo "⚠️ Docker not available - skipping secrets detection (will run in CI)" - fi -fi +npm run validate:secrets diff --git a/scripts/validate-secrets.js b/scripts/validate-secrets.js index 82be1ffa7..1175e197 100755 --- a/scripts/validate-secrets.js +++ b/scripts/validate-secrets.js @@ -1,54 +1,107 @@ #!/usr/bin/env node /** - * Cross-platform secrets detection using Gitleaks - * Works on Windows, macOS, and Linux + * Cross-platform secrets detection using Gitleaks. + * Supports Docker and Podman (including Colima, OrbStack, Podman Machine). + * Works on Windows, macOS, and Linux. * - * This script runs Gitleaks in a Docker container for local validation. - * CI uses the official gitleaks-action@v2 for better GitHub integration. - * Both share the same .gitleaks.toml configuration. + * Usage: + * node scripts/validate-secrets.js # scan working directory */ -import { spawn } from 'child_process'; -import { platform } from 'os'; -import { resolve } from 'path'; -import { existsSync } from 'fs'; +import { spawn, execSync } from 'child_process' +import { existsSync } from 'fs' +import { platform } from 'os' +import { resolve } from 'path' -const isWindows = platform() === 'win32'; -const projectPath = resolve(process.cwd()); +const GITLEAKS_IMAGE = 'ghcr.io/gitleaks/gitleaks:v8.30.1' +const isWindows = platform() === 'win32' +const projectPath = resolve(process.cwd()) +const configPath = resolve(projectPath, '.gitleaks.toml') +const hasConfig = existsSync(configPath) -// Check if .gitleaks.toml exists -const configPath = resolve(projectPath, '.gitleaks.toml'); -const hasConfig = existsSync(configPath); +function commandExists(cmd) { + try { + execSync(isWindows ? `where ${cmd}` : `which ${cmd}`, { stdio: 'ignore' }) + return true + } catch { + return false + } +} + +function daemonRunning(engine) { + try { + execSync(`${engine} info`, { stdio: 'ignore' }) + return true + } catch { + return false + } +} + +function detectEngine() { + for (const engine of ['docker', 'podman']) { + if (commandExists(engine)) return engine + } + return null +} + +function hintForStoppedDaemon(engine) { + if (engine === 'docker') { + if (commandExists('colima')) return "Run 'colima start' to enable secrets detection locally" + if (commandExists('orbstack')) return 'Start OrbStack to enable secrets detection locally' + } + if (engine === 'podman') { + return "Run 'podman machine start' to enable secrets detection locally" + } + return 'Start your container engine to enable secrets detection locally' +} + +const engine = detectEngine() + +if (!engine) { + console.log('No container engine found (docker/podman) - skipping secrets detection') + console.log('Install Docker or Podman to enable local secrets scanning') + process.exit(1) +} + +if (!daemonRunning(engine)) { + const engineLabel = engine.charAt(0).toUpperCase() + engine.slice(1) + console.error(`${engineLabel} daemon is not running`) + console.error(hintForStoppedDaemon(engine)) + process.exit(1) +} const args = [ 'run', '--rm', '-v', - `${projectPath}:/path`, - 'ghcr.io/gitleaks/gitleaks:v8.30.1', - 'detect', - '--source=/path', + `${projectPath}:/workspace`, + GITLEAKS_IMAGE, + 'dir', + '--no-banner', '--verbose', - '--no-git' -]; +] -// Add config file if it exists if (hasConfig) { - args.push('--config=/path/.gitleaks.toml'); + args.push('--config=/workspace/.gitleaks.toml') } -console.log('Running Gitleaks secrets detection...'); +args.push('/workspace') + +console.log('Checking for secrets with Gitleaks...') -const gitleaks = spawn('docker', args, { +const gitleaks = spawn(engine, args, { stdio: 'inherit', - shell: isWindows -}); + shell: isWindows, +}) gitleaks.on('close', (code) => { - process.exit(code); -}); + if (code !== 0) { + console.error('Secrets detected! Please remove sensitive data before committing.') + } + process.exit(code) +}) gitleaks.on('error', (err) => { - console.error('Failed to run Gitleaks:', err.message); - process.exit(1); -}); + console.error(`Failed to run Gitleaks via ${engine}:`, err.message) + process.exit(1) +})