Keeping credentials out of source, and what to do once one is in: rotate before removing, because deleting the line does not un-leak it. Covers where a secret may live, safe .env templates, why a client-reachable config value is public, and running a scanner rather than eyeballing for patterns. Use when writing code that reads a credential, creating config or .env templates, before committing, or after discovering a secret in a repository.
Installs into .claude/skills of the current project.
Are you the author of Secret Detection?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/shieldnet-360-secret-detection-secure-vibe)
---
name: secret-detection
description: "Keeping credentials out of source, and what to do once one is in: rotate before removing, because deleting the line does not un-leak it. Covers where a secret may live, safe .env templates, why a client-reachable config value is public, and running a scanner rather than eyeballing for patterns. Use when writing code that reads a credential, creating config or .env templates, before committing, or after discovering a secret in a repository."
---
<!-- Native skill bundle for Claude Code. Generated by `secure-vibe dev regenerate`. -->
<!-- Do not edit by hand; the source of truth is skills/secret-detection/SKILL.md. -->
# Secret Detection
Keeping credentials out of source, and what to do once one is in: rotate before removing, because deleting the line does not un-leak it. Covers where a secret may live, safe .env templates, why a client-reachable config value is public, and running a scanner rather than eyeballing for patterns. Use when writing code that reads a credential, creating config or .env templates, before committing, or after discovering a secret in a repository.
## ALWAYS
- On finding a committed credential, **rotate or revoke it at the provider first**. Deleting the line does not remediate it, and neither does `git rm`, an amended commit, or a history rewrite: the value reached a remote, a clone, a fork, a CI log and a backup, and it is compromised from the moment it was pushed. Rotate, then clean the history, then add the guard that stops it recurring — in that order, because only the first one is time-critical.
- **Run a scanner**; do not scan by eye. `scan_secrets` and `check_secret_pattern` carry the full pattern set with per-pattern entropy floors and hotword proximity, and a person reading a diff matches none of that reliably. A working-tree scan and a history scan answer different questions — the first says what you are about to commit, the second says what is already public.
- Read the credential from the environment or a secret manager at the point of use — `os.environ`, `process.env`, `os.Getenv`, a Vault / AWS Secrets Manager / GCP Secret Manager / 1Password client. Nothing about the value should be reconstructible from the repository.
- Ship a **`.env.example`** that is committed and contains every key with an empty or obviously-fake value, alongside a `.env` that is ignored and never committed. The example file is documentation, so it must never be produced by copying a working `.env` and editing it — that is how a real value survives into the template.
- Ignore the file classes that carry key material, in one list: `.env`, `.env.*` (but not `.env.example`), `*.pem`, `*.key`, `*.p12`, `*.pfx`, `*.ppk`, `id_rsa*`, `id_dsa*`, `id_ecdsa*`, `id_ed25519*`, `*credentials*`, `*secret*`. A `.gitignore` entry is a guard against habit, not a control — it does nothing for a file already tracked, and `git add -f` overrides it.
- Treat every **client-reachable config value as public**: a shipped bundle, a `/config` endpoint, Firebase Remote Config, a feature-flag payload, a mobile app's build-time constants. A client may hold public identifiers and nothing else.
- Prefer credentials that expire and are scoped. A short-lived, narrowly-scoped token bounds what a leak costs, and it is the only control that still helps after every other one has failed — which, given the rule above about rotation, is the case worth designing for.
## NEVER
- Hardcode an API key, token, password, connection string, or private key in source, and do not hide one in a default parameter, a test constant, a Dockerfile `ARG`, or a committed CI variable.
- Put a real secret in a **test fixture**. Use the documented placeholders — `AKIAIOSFODNN7EXAMPLE`, `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`, `xoxb-EXAMPLE-EXAMPLE` — which every scanner already excludes by name.
- Distribute a bearer secret to clients at **runtime** through a config or feature-flag service. Anyone holding the app's public configuration can fetch it, so it is exactly as exposed as a hardcoded one. Proxy the privileged call through an authenticated backend instead.
- Write a credential into a place designed to be read later: a log line, an error payload, a commit message, a PR or issue body, a CI job name, a screenshot in a ticket. `logging-security` owns the log sink; the others have no redactor at all.
- Treat a **pre-commit hook** as the control. `--no-verify` skips it, `git clone` does not carry it, and it never runs on the server or on a force-push. It is a convenience that catches accidents; the blocking CI check is the control, and `cicd-security` owns that gate.
## KNOWN FALSE POSITIVES
- The documented AWS example pair, `AKIAIOSFODNN7EXAMPLE` and `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`.
- A value whose text is itself a placeholder — `your-key-here`, `REPLACE_ME`, `CHANGEME`, `xxx`, `dummy` — or a credential-shaped string with a documentation marker in the surrounding prose.
- High-entropy strings that are not credentials: git commit SHAs in a changelog, CSS colour literals, base64-encoded test fixtures, a UUID used as a correlation id, an integrity `sha384-` hash in a `<script>` tag.
- **Publishable** keys that are designed to be in a client: a Stripe `pk_live_`, a Firebase web `apiKey` in a `firebaseConfig` block, a public Sentry DSN. These are identifiers, not credentials — the question is whether the backend authorizes anything on them.
- A **test-mode** key is *not* in this list. `sk_test_` authenticates against a real test API, reads real test data, and is a credential whose name merely contains the word "test".
## Reference files
Read these only when the task calls for them.
- `references/verifying-findings.md`