CoursesSecure CI/CD with GitLabSecurity scanning stages

Secret detection

Catch the committed key before main.

Intermediate10 min · lesson 9 of 17

Secret detection scans commits and diffs for credentials that should never be in a repository — API keys, tokens, private keys, passwords. It is the cheapest, highest-value scanner to run, because a committed secret is not a theoretical risk: bots scrape public and internal repos for exactly these patterns within minutes. The goal is to catch the accidental key before it reaches the default branch, and to know instantly when one slips through.

.gitlab-ci.yml
include:
- template: Jobs/Secret-Detection.gitlab-ci.yml # GitLab built-in
# or standalone:
gitleaks:
stage: test
image: zricethezav/gitleaks
script: [gitleaks detect --source . --redact --exit-code 1] # fail on a finding

Scan history and block new leaks

There are two jobs here. First, scan the existing history once — long-lived repos usually contain a forgotten secret or two, and you want to find and rotate them. Second, run detection on every merge request so a new secret is caught in review, before it merges. Tools like Gitleaks and TruffleHog do both; the built-in GitLab job focuses on new commits. Blocking the merge on a finding is appropriate here — a real credential in a diff should stop the pipeline.

Detection is only half — rotate

Finding a committed secret is not the fix. Git history is permanent: once a credential has been pushed, it must be treated as compromised and rotated, even if you delete it in the next commit, because it lives in the history and in every clone forever. So the incident response is always the same — rotate the secret first, then optionally scrub history. Prevention (a pre-commit hook plus CI detection) is far cheaper than the rotation scramble.

terminal
# a pre-commit hook catches secrets before they are ever committed (client side)
$ gitleaks protect --staged --redact
# CI is the backstop; the hook is the first line. A hit in either = rotate the key NOW.
A committed secret is a leaked secret
Deleting a credential in a later commit does not un-leak it — it remains in the git history and in every clone and fork, recoverable forever. The only correct response to a committed secret is to rotate it immediately. Run detection on history and every MR, add a pre-commit hook, and when one is found, revoke-and-reissue rather than just delete.