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..102e7127 100755 --- a/scripts/validate-secrets.js +++ b/scripts/validate-secrets.js @@ -1,54 +1,104 @@ #!/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 { spawn, execSync } from 'child_process'; import { platform } from 'os'; import { resolve } from 'path'; import { existsSync } from 'fs'; +const GITLEAKS_IMAGE = 'ghcr.io/gitleaks/gitleaks:v8.30.1'; const isWindows = platform() === 'win32'; const projectPath = resolve(process.cwd()); - -// 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', + 'run', '--rm', + '-v', `${projectPath}:/path`, + GITLEAKS_IMAGE, + 'dir', + '--no-banner', '--verbose', - '--no-git' + '/path' ]; -// Add config file if it exists if (hasConfig) { args.push('--config=/path/.gitleaks.toml'); } -console.log('Running Gitleaks secrets detection...'); +console.log('Checking for secrets with Gitleaks...'); -const gitleaks = spawn('docker', args, { +const gitleaks = spawn(engine, args, { stdio: 'inherit', shell: isWindows }); gitleaks.on('close', (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); + console.error(`Failed to run Gitleaks via ${engine}:`, err.message); process.exit(1); });