What Are Pre-commit Hooks
Pre-commit hooks are scripts that run automatically before Git records a commit. They live in the .git/hooks/ directory and execute every time a developer runs git commit, before the commit is written to history. If the hook exits with a non-zero status, the commit is rejected and the developer sees the output explaining what failed.
Git supports multiple hook events. The pre-commit hook is the most valuable for security because it runs against staged changes — the exact code that is about to enter the repository. Unlike CI/CD pipelines, pre-commit hooks run locally on every developer's machine before code ever leaves their environment.
The security benefit is fundamental: a secret caught before commit never reaches the repository. A SQL injection pattern caught before commit cannot be deployed to production. The earlier a vulnerability is caught in the development cycle, the cheaper it is to fix. Pre-commit is earlier than any other automated gate.
Why Pre-commit Is the Earliest Security Gate
Security gates in a typical development workflow run at four points:
- IDE (real-time): Linter warnings as you type — fast but limited to style and obvious errors
- Pre-commit (local, before push): Security scanning against staged changes — catches vulnerabilities before they enter the repository
- CI/CD (remote, after push): Full test suite and SAST scanning — catches issues but requires a push, meaning the vulnerable code is already in the repository
- Production monitoring: Runtime detection — too late, the vulnerability is already live
Pre-commit occupies the critical position where code has been written and reviewed by the developer but has not yet entered shared history. At this point:
- The developer still has full context of the change
- No other developer has pulled the code yet
- Secrets have not been logged by any CI system
- The fix cost is minimal — the developer can fix and recommit immediately
After a secret or vulnerability enters the repository, the remediation cost rises sharply: git history must be rewritten, secrets must be rotated, all repository clones must be updated, and a post-incident review may be required.
Setting Up git Hooks Manually
Git hooks are shell scripts in the .git/hooks/ directory. To create a pre-commit hook manually:
# Create the hook file
touch .git/hooks/pre-commit
# Make it executable
chmod +x .git/hooks/pre-commit
A minimal hook that runs a security scan:
#!/bin/sh
# .git/hooks/pre-commit
echo "Running security checks..."
# Run CodeSlick on staged files
npx codeslick analyze --staged --fail-on critical
if [ $? -ne 0 ]; then
echo "❌ Security check failed. Fix the issues above before committing."
exit 1
fi
echo "✅ Security checks passed."
exit 0
Limitation: Files in .git/hooks/ are not committed to the repository. Each developer must set up the hook manually on their machine, making enforcement inconsistent across teams.
Husky and lint-staged Integration
Husky solves the sharing problem by storing hook configuration in package.json and installing hooks automatically when developers run npm install. Hooks are committed to the repository and enforced consistently across all machines.
Setup
# Install Husky and lint-staged
npm install --save-dev husky lint-staged codeslick-cli
# Enable Husky
npx husky init
This creates a .husky/ directory with a pre-commit script and adds a prepare script to package.json that installs hooks on npm install.
Configure the pre-commit hook
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
Configure lint-staged to run CodeSlick
Add to package.json:
{
"lint-staged": {
"*.{js,ts,jsx,tsx,py,java,go}": [
"npx codeslick analyze --fail-on critical"
]
}
}
lint-staged runs the configured commands only against files that are staged for commit. This keeps pre-commit hooks fast — a commit that touches two TypeScript files only scans those two files, not the entire codebase.
What to Check at Commit Time
The best pre-commit security checks are fast, have low false-positive rates, and catch issues that are expensive to remediate after the fact:
1. Secrets detection (always run)
Hardcoded API keys, tokens, and passwords are the highest-priority pre-commit check. A committed secret requires key rotation regardless of whether the commit is later reverted. CodeSlick scans for 38 secret patterns including AWS keys, Stripe tokens, GitHub tokens, and database connection strings.
2. SAST scanning (run on staged files)
Static analysis catches injection vulnerabilities, missing security headers, insecure cryptography, and other code-level security issues. Running on staged files only (via lint-staged) keeps this fast enough for pre-commit use — typically under 3 seconds per file.
3. Dependency audit (run when lockfile changes)
Run npm audit or pip-audit only when package-lock.json or requirements.txt is staged. New dependencies introduced in a commit should be checked before they enter the repository.
What NOT to run at pre-commit
- Full test suite: Too slow for pre-commit. Reserve for CI/CD.
- Build compilation: Adds 30–120 seconds. Use pre-push hooks instead.
- Complete SBOM generation: Resource-intensive. Run in CI/CD.
How CodeSlick CLI Works as a Pre-commit Hook
The CodeSlick CLI is designed for pre-commit use: it exits with code 1 when critical or high findings are detected, enabling clean integration with git hooks.
Installation
npm install --save-dev codeslick-cli
Basic pre-commit integration
# .husky/pre-commit
npx codeslick analyze --staged --fail-on critical
The --staged flag analyzes only the files staged for commit. The --fail-on critical flag exits with code 1 if any Critical severity findings are detected, blocking the commit.
Pass/fail threshold configuration
Configure thresholds via .codeslick.yml in the project root:
# .codeslick.yml
thresholds:
fail_on: critical # Block commits with Critical findings
warn_on: high # Warn (but allow) on High findings
secrets: block # Always block hardcoded secrets
ai_code: warn # Warn on detected AI-generated code
What the CLI checks
- 308 security checks across JavaScript, TypeScript, Python, Java, and Go
- 38 secret detection patterns — AWS keys, Stripe tokens, GitHub tokens, database connection strings
- 164 AI code detection signals — flags code from Copilot, ChatGPT, Claude for mandatory review
- Dependency scanning — checks
package-lock.json,requirements.txt,pom.xml,go.sumagainst CVE databases
All findings include CWE classification, CVSS severity, and fix suggestions. The pre-commit output is designed for developer readability — the finding, the file, the line, and the recommended fix appear directly in the terminal.
Add CodeSlick to your pre-commit workflow and catch SQL injection, hardcoded secrets, and XSS before every commit.