A lightweight pre-commit hook that runs a fast subset of the DSGAI scan (DSGAI02 hardcoded LLM API key detection) to block credentials from being committed. This catches the highest-impact, highest-frequency mistake before it reaches git history.
This is not a substitute for the full scan — it's a fast gate that runs on every commit (sub-second), against staged changes only. Use the full skill (/dsgai_scanner_tool) and/or the GitHub Action for complete coverage.
Two options, in order of preference:
- gitleaks with the DSGAI rule pack (recommended) — entropy-aware, battle-tested, cross-platform, no bash quirks. Use this unless you specifically can't add a binary.
- The zero-dependency ripgrep script — a portable fallback (bash 3.2 / BSD safe) for environments where installing gitleaks isn't an option.
Both detect quote-optional named credentials and raw token prefixes (Slack xoxb-, GitHub ghp_, Google AIza, AWS AKIA, Anthropic/OpenAI project keys) regardless of variable name.
Install gitleaks (brew install gitleaks, choco install gitleaks, or a release binary) and copy gitleaks/dsgai.toml into your repo.
pre-commit framework — add to .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: dsgai-gitleaks
name: DSGAI02 — gitleaks credential scan
entry: gitleaks protect --staged --config integrations/gitleaks/dsgai.toml --no-banner
language: system
pass_filenames: falsePlain git hook — .git/hooks/pre-commit (chmod +x):
#!/usr/bin/env sh
exec gitleaks protect --staged --config integrations/gitleaks/dsgai.toml --no-bannerFull-tree audit (not just staged): gitleaks dir . --config integrations/gitleaks/dsgai.toml. The pack allowlists tests/fixtures/**, lockfiles, minified JS, and snapshots.
The full DSGAI scan takes minutes — too slow for pre-commit. But the most common mistake (hardcoded sk-... or sk-ant-... keys, or a raw xoxb-/ghp_ token, in .env or config.py) can be caught in under a second. Both options above do exactly that and nothing more.
If you can't use gitleaks, use the portable ripgrep script. It ships in this repo as dsgai-secret-scan.sh — copy it to scripts/dsgai-secret-scan.sh and chmod +x it. It is bash-3.2 / BSD safe (no mapfile, no grep -z, no ${var,,}), catches quote-optional named credentials and raw token prefixes, and needs nothing beyond ripgrep. Key detection logic:
# Collect staged files with a NUL-delimited read loop (bash-3.2 safe — no mapfile),
# filter extensions with a case statement (no `grep -z`, which BSD grep lacks).
STAGED=()
while IFS= read -r -d '' f; do
case "$f" in
*.py|*.ts|*.js|*.java|*.kt|*.go|*.yaml|*.yml|*.json|*.toml|*.cfg|*.env|*.env.*)
STAGED+=("$f") ;;
esac
done < <(git diff --cached --name-only --diff-filter=ACM -z)See the committed file for the full quote-optional NAMED pattern and the token-PREFIX pattern (Slack/GitHub/Google/AWS/Anthropic/OpenAI). The script is self-contained — no external dependencies beyond ripgrep.
Pick one of the three integration options that matches your stack.
If you use pre-commit, add this to .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: dsgai-secret-scan
name: DSGAI02 — block hardcoded LLM/cloud credentials
entry: scripts/dsgai-secret-scan.sh
language: system
files: \.(py|ts|js|java|kt|go|yaml|yml|json|toml|env|cfg)$
pass_filenames: false # the script reads staged files via gitInstall:
pip install pre-commit
pre-commit install# from your repo root
ln -s ../../scripts/dsgai-secret-scan.sh .git/hooks/pre-commitIn .husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
scripts/dsgai-secret-scan.shThen chmod +x .husky/pre-commit.
This hook only catches DSGAI02 hardcoded credentials. It does not check:
- DSGAI04 — unsafe
torch.load()withoutweights_only=True - DSGAI06 — MCP servers over
http://without auth - DSGAI11 — missing tenant filters in vector queries
- DSGAI12 — raw LLM-output SQL execution
- DSGAI13 — vector store auth gaps
- DSGAI14 — verbose telemetry logging
- ... or any of the other 17 DSGAI risks
Run the full skill (/dsgai_scanner_tool in Claude Code) periodically — at minimum before every release, ideally on every PR via the GitHub Action template.
The named pattern matches <KEY>\s*[:=]\s*<16+ key-like chars> (quote optional). Because the value must be 16+ contiguous [A-Za-z0-9_-], ordinary env reads like KEY = os.getenv("KEY") or KEY = os.environ["KEY"] do not match (the ./( break the run). If a legitimate constant still matches, choose one of:
- Rename the test fixture so it doesn't start with the real key name (e.g.
_TEST_OPENAI_KEY_PLACEHOLDER), or for gitleaks add it to the[allowlist]indsgai.toml - Exclude the file by adjusting the
files:glob / gitleaks allowlist, or by gitignoring it if it contains real local-dev secrets - One-time bypass with
git commit --no-verifyand a comment explaining why
Never lower the {16,} minimum length. Real LLM API keys are always 20+ characters; relaxing the length will produce noisy false positives without catching shorter real keys (which don't exist).
CC BY-SA 4.0 — part of the OWASP GenAI Data Security Initiative.