Skip to content

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

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:

  1. Computes the diff between the PR branch and the base.
  2. Runs credwatch scan diff against the diff.
  3. Emits inline GitHub Actions annotations (::error file=...::) — the leaked line shows up directly in the PR's "Files changed" view.
  4. On any finding, posts a comment with a findings table.
  5. 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 mainscan diff only 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.