From security
Set up gitleaks secret-scanning on a repo. Scans history for existing leaks first — stops if dirty, because installing CI on top of a polluted history makes CI permanently red. If history is clean, scaffolds .gitleaks.toml, a local pre-commit hook, and a pinned CI workflow that scans both git history and working tree. User-triggered only — activate when the user invokes `/security:gitleaks`.
How this skill is triggered — by the user, by Claude, or both
Slash command
/security:gitleaksThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are adding [gitleaks](https://github.com/gitleaks/gitleaks) secret-scanning to the current repo. The order matters:
You are adding gitleaks secret-scanning to the current repo. The order matters:
gitleaks already finds things in HEAD or in history, stop. Adding a pre-commit hook locks the user out of committing legit work until those findings are resolved; adding CI turns the default branch permanently red. The user has to triage (rotate? scrub history? allowlist a false positive?) before any of this is installed.Do not commit on the user's behalf. Write the files, show diffs, let them stage and commit.
User-triggered only. Activate when the user invokes /security:gitleaks. Do not self-activate on related natural-language phrasing ("add gitleaks", "set up secret scanning", a mention of a leaked credential, etc.) — surface the slash command to the user instead and let them decide.
# 1. We're in a git repo
git rev-parse --show-toplevel >/dev/null 2>&1 \
|| { echo "Not a git repo. Abort."; exit 1; }
# 2. gitleaks is installed locally (needed for the baseline scan and hook)
command -v gitleaks >/dev/null 2>&1 \
|| { echo "gitleaks not installed. Run: brew install gitleaks"; exit 1; }
# 3. Capture default branch — needed for the CI workflow
gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null \
|| { git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||' | grep . ; } \
|| echo "main"
If gitleaks isn't installed, instruct the user to run brew install gitleaks (or the platform equivalent — see https://github.com/gitleaks/gitleaks#installing) and re-invoke. Don't try to install it yourself; this is a tool the user runs daily, they should know what's on their machine.
Run two scans. Both, every time — they cover different surfaces:
# History: every commit reachable from HEAD
gitleaks git --redact --verbose .
# Working tree: files on disk (catches stuff that arrived via merge, submodule,
# generated files, etc. that history scan can miss)
gitleaks dir --redact --verbose .
--redact matters: without it, a matched secret is echoed to your terminal and shell history. Always pass it.
Proceed to Phase 2. Tell the user briefly: "Baseline scan found 0 leaks across history and working tree. Installing config + hook + CI."
Do not write the hook or CI. Do not silently add findings to an allowlist. Surface what was found (the redacted output is safe to show), and walk the user through the choice:
| What it is | What to do |
|---|---|
| A real, current secret | Rotate it first (revoke at the provider). Then decide whether to scrub history (git filter-repo, BFG) or accept that the value is in history and rely on rotation. Re-run the baseline once decided. |
| A real, already-rotated secret | Scrubbing history is optional. Most teams leave it — git history of a rotated credential is a hygiene problem, not an active risk. Add a path-specific allowlist entry once confirmed dead. |
| A false positive (test fixture, public sample, demo key) | Add to .gitleaks.toml's [allowlist].paths (path-scoped) or [allowlist].regexes (value-scoped). Path-scoped is safer — value-scoped allowlists can mask real future leaks that happen to match. |
| Unsure | Stop. Get the user's eyes on the actual file/line. Never auto-classify a finding as a false positive. |
End the session here when blocked. Tell the user explicitly:
Found N pre-existing finding(s). I haven't written the hook or CI — installing them now would block your next commit / break CI on first push. Resolve the findings (rotate, scrub, or allowlist), then re-invoke /security:gitleaks.
History rewrites (git filter-repo, BFG) are destructive on shared history — every collaborator has to re-fetch and discard their local branches. Do not run them on the user's behalf. Explain the cost; let them decide.
.gitleaks.tomlUse the template at assets/gitleaks.toml (copied to the repo as .gitleaks.toml). Substitute __REPO_NAME__ with the repo name (just for the title = field — cosmetic).
The template:
useDefault = true). Do not replace it — the default catches AWS keys, Stripe, Slack tokens, JWTs, etc.GOCSPX-…) — useful if they touch Google APIs./Users/<name>/…) — useful for open-source repos to keep maintainer paths out of docs/scripts. Skip on internal-only repos. If enabled, substitute __USERNAME__ (whoami is the value) in both the regex and keywords fields — without this the rule matches only the literal string /Users/__USERNAME__/… and is silently inert.Add new rules only when the user asks. The default ruleset is broad — extending it ad-hoc invites false positives.
Show the user the file before writing. Then write it to the repo root as .gitleaks.toml.
Phase 1's baseline ran with no --config, so it only checked against gitleaks' default ruleset. Any custom rule you just added (Anthropic key, Google OAuth secret, personal home path) could have pre-existing matches in history that Phase 1 missed — installing the hook/CI on top of that would block the next commit and turn CI red on first push. Re-run the dual scan, this time with the new config:
gitleaks git --config .gitleaks.toml --redact --verbose .
gitleaks dir --config .gitleaks.toml --redact --verbose .
If clean, proceed to Phase 3. If anything is found, stop and route through the same triage table as Phase 1 (rotate / scrub / allowlist), then re-run this scan. Do not install the hook or CI on a dirty re-baseline.
The hook lives at .githooks/pre-commit (not .git/hooks/ — that's not checked in and won't sync to collaborators).
mkdir -p .githooks
Copy assets/pre-commit to .githooks/pre-commit. Make it executable:
chmod +x .githooks/pre-commit
The hook is inert until each clone opts in:
git config core.hooksPath .githooks
This is per-clone, not committed (it's in .git/config). Tell the user explicitly:
Each collaborator (including you, in this clone) needs to run
git config core.hooksPath .githooksonce. Without it, the hook won't fire. We deliberately don't auto-enable it — git refuses to run hooks the user hasn't opted into, and that's the right default.
Run it for the current clone if the user asks; otherwise just print the command.
CI is the backstop. The local hook protects committers who opted in; CI catches everyone else (force-pushers, web-UI edits, collaborators who skipped core.hooksPath).
Use the template at assets/gitleaks.yml. Substitute:
__DEFAULT_BRANCH__ → captured default branch (e.g. main)__GITLEAKS_VERSION__ → the gitleaks version to pinTo pin to current latest:
gh release view --repo gitleaks/gitleaks --json tagName --jq '.tagName' | sed 's/^v//'
# Example output: 8.30.1
Show the user the resolved version. Write the file to .github/workflows/gitleaks.yml:
mkdir -p .github/workflows
The template installs gitleaks via a curl-pinned tarball rather than gitleaks/gitleaks-action. Two reasons:
If the user objects (some teams standardize on marketplace actions for CODEOWNERS / Dependabot reasons), respect that. Swap to gitleaks/gitleaks-action@<SHA> and note the trade-off.
Baseline scan: clean across history + working tree.
Wrote:
.gitleaks.toml — extends default ruleset
.githooks/pre-commit — staged-changes scan, --redact
.github/workflows/gitleaks.yml — CI scan on push/PR, gitleaks <version>
What to do next:
1. Activate the local hook (per clone):
git config core.hooksPath .githooks
2. Stage and commit the three new files.
3. Push. The CI workflow will run on the PR / next push to <default-branch>.
4. Share the core.hooksPath command with collaborators (README is a fine place).
If the user comes back later — CI failed, or the hook is blocking a commit — help them per-finding. The decision tree is the same as the baseline-scan table above. The high-leverage moves:
| Situation | First move |
|---|---|
| Hook blocks a commit that the user knows is fine | Look at the matched value redacted in the hook output. If it's genuinely a fixture / sample / docs, add a path-scoped allowlist entry in .gitleaks.toml. Re-stage, re-commit. |
| CI fails on a PR that didn't trigger the hook | Either the contributor didn't opt into the hook, or the finding is in history they pulled in. Treat as a fresh baseline-scan finding: rotate-or-allowlist. |
| Tons of fixture-file false positives | Path-scope them: [[rules]].allowlist.paths = ['''test/fixtures/'''] — or, simpler, a top-level paths = [...] in the [allowlist] table. |
| A whole rule is noisy and not useful for this repo | Disable it: [[rules]] id = "<id>" allowlist.regexes = ['''.*'''] is heavy-handed; better is extend.disabledRules = ["<id>"] at the top. |
User wants a .gitleaksignore file instead | Supported — one path per line, gitleaks reads it automatically. Simpler for path-only exclusions; .gitleaks.toml is needed for everything else. |
When advising on allowlists, repeat the rule: path-scope before value-scope. A regex allowlist can silently swallow a real future leak that happens to match. A path allowlist is bounded to one file.
.gitleaks.toml. Read it, diff against the template, ask before overwriting. The user may have rules you'd be deleting..githooks/pre-commit. Same — read it, see if it already calls gitleaks, ask before changing..github/workflows/gitleaks.yml (or any workflow named gitleaks). Read it; if it's roughly equivalent (scans history + working tree, version-pinned), say so and skip rewriting. The skill's job is to get the boilerplate in place, not to enforce a specific style.gitleaks git has nothing to scan but won't error. Run gitleaks dir only. Proceed normally; baseline is trivially clean..githooks/pre-commit won't fire under husky. Detect (ls .husky/ or cat package.json | grep husky) and write the scan command into the existing husky pre-commit script instead: gitleaks git --staged --redact --verbose --config .gitleaks.toml .. Don't create a parallel .githooks/ directory the user will forget exists.[[rules]].path matching — out of scope for this skill to scaffold, but worth a sentence.gitleaks protect. That subcommand is deprecated as of gitleaks v8.18. Use gitleaks git --staged instead — it's the supported replacement and the hook template already uses it.[allowlist] needs a comment explaining why that path / regex can never contain a real secret.--redact to gitleaks invocations. Without it, matched secret values land in shell history and CI logs.core.hooksPath opt-in), don't fight it — explain why opting in is a per-clone choice and let the user decide.npx claudepluginhub zcaceres/skills --plugin securityProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.