CI/CD pre-merge scanner¶
credwatch scan diff is a CLI that scans a unified diff for exposed secrets. Drop it into your CI to catch credentials before they're merged. Same detection engine as the platform scanner; runs entirely in your CI environment.
Exit codes:
| Code | Meaning |
|---|---|
0 |
No findings |
1 |
Findings detected |
2 |
Configuration error |
Why pre-merge scanning matters¶
The platform scans your sources on a 4-hour cycle (or whatever cadence you've set). If a key is committed at 10:00 and your next scan runs at 12:00, you have a 2-hour exposure window before CredWatch even sees the leak — and longer before alert + remediation.
A pre-merge scan in CI blocks the leak at the pull request boundary. The credential never lands on main.
Two modes¶
Mode A — DB-backed (uses your CredWatch account's patterns)¶
Requires the CredWatch CLI installed in your CI and DATABASE_URL reachable from the CI runner. Practical only if your CI runs in the same network as your CredWatch deployment (e.g. self-hosted CredWatch on the same VPC).
git diff origin/main...HEAD | credwatch scan diff --slug acme
Mode B — File-backed (recommended for CI)¶
Export your patterns once, commit the JSON to the repo, and run the scanner offline in CI. No DB required.
# One-time: export patterns from your CredWatch account
credwatch pattern export acme --output .credwatch/patterns.json
# Commit the file
git add .credwatch/patterns.json
git commit -m "chore: add CredWatch patterns for CI scanning"
Then in CI:
git diff origin/main...HEAD | credwatch scan diff \
--patterns-file .credwatch/patterns.json
Re-export when you add or change patterns on the CredWatch side.
GitHub Actions¶
We ship a composite Action and an example workflow.
Drop-in workflow¶
Create .github/workflows/secret-scan.yml:
name: Secret scan
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write # for the PR comment on findings
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # we need the base ref's history
- name: CredWatch secret scan
uses: fpulidov/credwatch/.github/actions/credwatch-scan@main
with:
patterns-file: .credwatch/patterns.json
base-ref: origin/${{ github.base_ref }}
What it does:
- Computes the diff between the PR branch and the base.
- Runs
credwatch scan diffagainst the diff. - Emits inline GitHub Actions annotations (
::error file=...::) — the leaked line shows up directly in the PR's "Files changed" view. - On any finding, posts a comment with a findings table.
- Fails the job (exit 1), blocking the merge if branch protection is set up.
GitLab CI¶
secret-scan:
stage: test
image: python:3.12-slim
before_script:
- pip install credwatch
script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
- git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD \
| credwatch scan diff --patterns-file .credwatch/patterns.json --json
only:
- merge_requests
Ignoring paths¶
Skip vendored or generated files:
git diff origin/main | credwatch scan diff \
--patterns-file .credwatch/patterns.json \
--ignore vendor/ \
--ignore "*.lock" \
--ignore "**/__snapshots__/**"
--ignore can be passed multiple times. Each accepts a glob.
JSON output¶
For piping into other tools:
git diff origin/main | credwatch scan diff \
--patterns-file .credwatch/patterns.json --json
Output shape:
{
"findings": [
{
"file": "src/config.ts",
"line": 42,
"matched_text": "sk-a1****cd34",
"pattern_name": "OpenAI API Key",
"secret_type": "openai",
"composite_score": 87
}
],
"exit_code": 1
}
Local pre-commit hook¶
If you want the same check on a developer's laptop before they even open a PR:
# .git/hooks/pre-commit
#!/usr/bin/env bash
git diff --cached | credwatch scan diff \
--patterns-file .credwatch/patterns.json
exit $?
chmod +x .git/hooks/pre-commit and commits are scanned client-side. (pre-commit the framework also works — wrap the command in a repos: entry.)
What CI scanning won't catch¶
- Existing secrets on
main—scan diffonly sees the PR diff. The platform scanner is what surfaces what's already there. - Secrets in deleted lines — by definition, only the
+lines of the diff are scanned. (Deleting a leaked key is a good thing — though you still need to rotate.) - Force-pushed history — if someone rewrites history that bypasses a PR, the platform's commit-history scan is the safety net.
Use the CI scanner and the platform scan, not one or the other.