How To

How to Run Secrets Detection Automation with GitGuardian's Native Tools

Part two of our GitGuardian secrets series: build secrets detection automation with GitGuardian's own tooling only. Wire ggshield into pre-commit and CI (exact hook and Actions config), drive the incidents workflow — assign, resolve, ignore-with-reason — and prioritize by validity then severity. Query the GitGuardian API with curl to filter incidents by validity and severity, script assign/resolve calls, and plant honeytokens as tripwires for active credential abuse. Closes honestly on the ceiling: detection and workflow are automated, but the revoke-rotate-redeploy-verify loop across your cloud is still a human sprint.

·
gitguardiansecuritysecretssecretsdetectionggshieldhoneytokensdevsecops
Cover Image for How to Run Secrets Detection Automation with GitGuardian's Native Tools

How to Run Secrets Detection Automation with GitGuardian's Native Tools

Two lines in a pre-commit config stop the next credential from ever reaching your history:

repos:
  - repo: https://github.com/gitguardian/ggshield
    rev: v1.34.0
    hooks:
      - id: ggshield
        stages: [pre-commit]

That block is cheap insurance for everything committed from today forward. It does nothing for the backlog — the hundreds of incidents that landed before anyone wired it up. In part one of this series we covered why that backlog exists: env files, debug commits, notebooks, and CI logs keep leaking credentials, deletion doesn't remediate anything once a secret is pushed, and validity checking is the triage superpower that separates a live key from dead noise.

This article is the hands-on half — secrets detection automation built entirely on GitGuardian's own tooling. You'll wire ggshield into pre-commit and CI, drive the incidents workflow, prioritize by validity and severity, query the GitGuardian API with curl, and plant honeytokens as tripwires. No third-party layer, nothing beyond a GitGuardian account and an API key. We'll work tool by tool, then close with the ceiling every DIY setup hits.

Tool 1: ggshield — shift detection left

ggshield is GitGuardian's CLI. Install it, authenticate once, and it scans locally against the same secrets engine the platform uses (GitGuardian docs).

pip install ggshield
ggshield auth login

Pre-commit: block the leak before it's a commit

Add the hook block from the top of this article to .pre-commit-config.yaml, then activate it:

pip install pre-commit
pre-commit install

Now every git commit runs the staged diff through GitGuardian first. A matched secret fails the commit with the file, line, and detector name. This is the cheapest possible remediation because there's nothing to remediate — the secret never enters history.

If you'd rather not depend on the pre-commit framework, install the hook directly:

ggshield install --mode local -t pre-commit

How to read it. Pre-commit is developer-side and skippable (git commit --no-verify), so treat it as the first net, not the only one. It also can't see what's already committed — only the diff in front of it. That's why you also scan in CI, where nobody can bypass it.

CI: the net nobody can skip

In CI, scan the full push range so a secret buried in an intermediate commit still surfaces. A GitHub Actions job:

name: gitguardian
on: [push, pull_request]
jobs:
  scanning:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: GitGuardian/ggshield-action@v1
        env:
          GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }}
          GITHUB_PUSH_BASE_SHA: ${{ github.event.base }}
          GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }}
          GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
          GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}

The equivalent bare command, for GitLab or any other runner:

ggshield secret scan ci

To scan an entire repository's history in one pass — the right move when you first onboard a repo:

ggshield secret scan repo .

How to read it. fetch-depth: 0 matters: a shallow clone hides the commits where secrets usually hide. The full-history scan is your backlog discovery pass — run it once per repo, feed the output into the platform, and every hit becomes a tracked incident you can work in the next tool.

Tool 2: The incidents workflow — turn detections into owned work

Every secret GitGuardian finds becomes an incident on the dashboard (Internal Monitoring → Incidents → Secrets). An unmanaged incident list is just anxiety; the workflow is what turns it into progress.

Each incident carries a status you drive explicitly:

  • Assign it to the engineer who owns the service the secret belongs to. Unassigned incidents are the ones that rot.
  • Resolve it once the credential is revoked and rotated (not merely deleted from the file).
  • Ignore it only with a reason — test_credential, false_positive, or low_risk. The reason is the audit trail; a silent ignore is indistinguishable from neglect.

Prioritize by validity, then severity. GitGuardian tests detected credentials against the provider and tags each incident's validity as valid, invalid, or unknown. A valid secret is a live key an attacker can use right now — it jumps the queue regardless of anything else. Within the valid set, sort by severity, which GitGuardian derives from the secret type and the permissions it likely grants (a cloud provider root key outranks a read-only third-party token). Work valid-and-critical first; everything else is backlog you schedule, not a fire you fight.

For recurring, low-judgment patterns, set playbooks (Settings → Playbooks) to act automatically — for example, auto-resolve an incident when GitGuardian confirms the credential has become invalid, or notify a Slack channel the moment a valid incident of critical severity appears. Auto-resolve on invalidity is safe because an invalid key is, by definition, no longer a live risk; auto-resolving valid incidents is not something you ever want to configure.

Tool 3: The GitGuardian API — query incidents at scale

The dashboard is fine for a handful of incidents; the API is how you triage hundreds. Everything the UI shows is available over REST (API reference). Create a personal access token (API → Personal access tokens) with incidents:read, then:

export GG_TOKEN="your_token_here"

List the open incidents, newest first:

curl -s -H "Authorization: Token $GG_TOKEN" \
  "https://api.gitguardian.com/v1/incidents/secrets?status=TRIGGERED&ordering=-date&per_page=50"

The triage query that actually matters — valid credentials at critical severity, the ones you work today:

curl -s -H "Authorization: Token $GG_TOKEN" \
  "https://api.gitguardian.com/v1/incidents/secrets?validity=valid&severity=critical&status=TRIGGERED"

Pipe it through jq to get a scannable worklist instead of a wall of JSON:

curl -s -H "Authorization: Token $GG_TOKEN" \
  "https://api.gitguardian.com/v1/incidents/secrets?validity=valid&status=TRIGGERED" \
  | jq -r '.[] | [.id, .severity, .detector.name, .gitguardian_url] | @tsv'

Drive workflow actions the same way. Assign an incident, then resolve it once you've rotated the key:

# Assign incident 12345 to an owner by email
curl -s -X POST -H "Authorization: Token $GG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"owner@yourco.com"}' \
  "https://api.gitguardian.com/v1/incidents/secrets/12345/assign"

# Resolve it after revoke + rotate
curl -s -X POST -H "Authorization: Token $GG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"secret_revoked":true}' \
  "https://api.gitguardian.com/v1/incidents/secrets/12345/resolve"

How to read it. The validity and severity filters are the whole game — they collapse a 400-incident list into the 5 that can hurt you before lunch. Wire the valid-and-critical query into a scheduled job and page on a non-empty result; that's a live-key alarm built from two curl calls. The write endpoints let you script bulk cleanup (mass-ignore a batch of confirmed test credentials with a reason), but the mutating half of remediation still lives outside GitGuardian — more on that below.

Tool 4: Honeytokens — tripwires for the breach you can't see

Detection tells you a secret leaked. A honeytoken tells you someone is using leaked access. It's a decoy AWS credential GitGuardian generates that grants nothing, but alerts the instant anyone tries to authenticate with it (honeytoken docs).

Create one from the API:

curl -s -X POST -H "Authorization: Token $GG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-deploy-decoy","type":"AWS","description":"planted in build config"}' \
  "https://api.gitguardian.com/v1/honeytokens"

Placement is the whole strategy. A honeytoken only fires if an intruder finds it while poking around, so plant it where a real credential would plausibly live and where a legitimate engineer would never actually call it:

  • In a .env.example, a Terraform variables file, or a CI config — spots an attacker scrapes first.
  • Inside a private repo you want to know is breached the moment it's cloned by the wrong person.
  • In an internal wiki page or S3 bucket that stores "deployment credentials."

A honeytoken trip is categorically different from a detection incident: a detection means a secret might be exposed; a trip means someone acted on stolen access. Route those to an immediate page, not the backlog queue — the alert carries the source IP and user agent of whoever used it, which is your first evidence of the breach.

The honest part: where native tooling stops

Wire all four tools together and you have genuinely strong secrets detection automation: leaks blocked at commit, an unbypassable CI net, a prioritized incident queue, scriptable triage, and tripwires for active abuse. Run it. But know the ceiling before you call it a program.

Detection and workflow are automated. Remediation is not. GitGuardian tells you a valid AWS key leaked, who should own it, and how bad it is. It cannot revoke that key in your AWS account, mint a replacement, push the new value into your secrets manager, redeploy the services that read it, and confirm the old one is dead. That revoke-rotate-redeploy-verify loop — the part that actually closes the exposure — is a human sprint across your cloud console, your secrets store, and your deploy pipeline, run once per incident.

The queue moves at human speed. Playbooks auto-resolve invalidated keys and route alerts, but a valid critical incident still waits on a person to pick it up, trace which account the key opens, and do the rotation by hand. On a busy repo, the gap between "GitGuardian alerted" and "credential rotated" is measured in hours or days — exactly the window an attacker needs.

Blast radius is off-platform. GitGuardian scores severity from the secret's type, not from what that specific key can reach in your environment. Whether a leaked token opens a sandbox or your production billing account is a question you answer by hand, in the cloud console, every time.

None of this makes the native tooling optional — it makes it the detection layer, not the response layer. The question it leads to: how do you attach automated, blast-radius-aware remediation to every valid incident and every honeytoken trip?

What comes next

Everything you scripted here — the valid-and-critical triage, the honeytoken watch, the revoke-rotate-redeploy loop — can run continuously with a security agent attached. That's part three of this series: Olivier, CloudThinker's security agent, watches new incidents and honeytoken trips over a read-only GitGuardian token, triages by validity and blast radius, drafts the remediation plan, and executes it step by step under your approval.

Or see your own open incidents triaged: Try CloudThinker free — 100 premium credits, no card required.