Meta description: Learn how I set up TruffleHog in GitHub Actions to catch hardcoded secrets before they hit production — real commands, gotchas, and a working YAML workflow included.
Last updated: June 2, 2026
A few months ago, I was reviewing a routine pull request when something caught my eye: buried inside a config file update was a live AWS access key. Not a placeholder. Not a test value. A real, active AKIA... key with broad S3 permissions, pushed by a developer who thought they were just “temporarily” hardcoding it to debug a deployment issue. That moment cost us three hours of credential rotation across four environments. It didn’t have to happen. If we’d had TruffleHog running in our GitHub Actions pipeline at the time, that secret would have been flagged before the PR was even reviewed. In this post, I’m going to show you exactly how to scan GitHub Actions for hardcoded secrets using TruffleHog — so you never have that sinking feeling I had.
TL;DR
- TruffleHog v3 detects 800+ secret types with active API verification, catching only real, live credentials — not just regex matches.
- You can drop it into a GitHub Actions workflow in under 15 minutes with a single
uses:step or a CLI install. - Scanning only the PR diff (not full history) keeps your pipeline fast while still blocking new secrets from merging.
Why You Should Scan GitHub Actions for Hardcoded Secrets
Hardcoded secrets in source code are one of the most persistent and costly mistakes in software development. According to a 2023 GitGuardian report, over 10 million secrets were found in public GitHub repositories in a single year — and the number keeps climbing. The problem isn’t that developers are careless. It’s that the feedback loop is broken: there’s no friction at the moment of the commit.
GitHub Actions workflows are particularly risky because they often contain API keys, tokens, and credentials needed to deploy, test, or integrate with external services. A developer troubleshooting a failed deployment at 11pm will paste a token directly into a YAML file just to get things working. Without automated secrets detection in CI/CD, that token lives in your Git history forever — even if the next commit removes it.
TruffleHog v3, released by Truffle Security in 2022, is a complete rewrite in Go that goes far beyond regex matching. It actively verifies credentials against their respective APIs, which means when it flags something, you know it’s real.
[INTERNAL LINK: related article on securing CI/CD pipelines]
[SOURCE: https://github.com/trufflesecurity/trufflehog]
Prerequisites
Before we start, make sure you have:
- A GitHub repository with GitHub Actions enabled
- TruffleHog v3.x (v3.88+ recommended — this is what I tested with)
- A GitHub Actions runner (
ubuntu-latestworks fine) - Basic familiarity with GitHub Actions YAML syntax
- (Optional) A Slack webhook URL if you want alerting
Step-by-Step: TruffleHog Secret Scanning in GitHub Actions
Step 1: Install TruffleHog Locally and Verify It Works
Before touching your CI config, I always validate the tool locally first. It saves time debugging in the pipeline.
bash
# Install TruffleHog via the official install script
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \
| sh -s -- -b /usr/local/bin
# Verify installation
trufflehog --version
# Expected output: trufflehog 3.88.x
Run a quick local scan on your repo to see what it finds before you automate anything:
bash
# Scan your local repo's git history
trufflehog git file://. --only-verified
The --only-verified flag is important here — it tells TruffleHog to only report secrets it has confirmed as active via API calls. Without it, you’ll get a flood of false positives from test keys and example configs.
Step 2: Create the GitHub Actions Workflow YAML
Now create the workflow file. I put mine at .github/workflows/secret-scan.yml:
yaml
name: TruffleHog Secret Scan
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
trufflehog:
runs-on: ubuntu-latest
steps:
- name: Checkout repository (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required — TruffleHog needs full git history
- name: Run TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
extra_args: --only-verified
The fetch-depth: 0 is non-negotiable. If you leave it at the default (which fetches only the last commit), TruffleHog can’t diff properly and will either miss secrets or scan nothing. I made this mistake the first time and got zero results, thinking my repo was clean.
Step 3: Scan Only the PR Diff — Not the Full Git History
Scanning your full git history on every PR is overkill and slows things down. What you actually want is to scan only the changes introduced by the current PR. Here’s how I configure the base/head comparison for pull requests:
yaml
- name: Run TruffleHog on PR diff only
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.pull_request.base.sha }}
head: ${{ github.event.pull_request.head.sha }}
extra_args: --only-verified --fail
The --fail flag makes the action exit with a non-zero code when secrets are found, which blocks the PR from merging. Without --fail, TruffleHog will report findings but your workflow step will still pass — a gotcha I discovered when a junior engineer didn’t notice the warning in the logs.
For scheduled full-history scans (run weekly, not on every PR), use:
yaml
on:
schedule:
- cron: "0 3 * * 1" # Every Monday at 3am UTC
jobs:
full-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Full history scan
run: |
trufflehog git file://. --only-verified --fail
Step 4: Handle False Positives with .trufflehogignore
Even with --only-verified, you’ll occasionally need to suppress a finding — for example, a test fixture that uses a real-looking but intentionally invalid key format, or an internal secret pattern your team uses for non-sensitive config.
Create a .trufflehogignore file at your repo root:
# Ignore specific detector types for certain files
tests/fixtures/mock_aws_key.txt
docs/examples/sample_config.yaml
You can also suppress inline by adding a comment directly in your code:
python
# This is a test credential — not a real secret
TEST_API_KEY = "AKIAIOSFODNN7EXAMPLE" # trufflehog:ignore
Security Note: Use
.trufflehogignoresparingly. Every suppression is a risk. I review the ignore list quarterly and remove entries when test fixtures are updated to use obviously fake values likeFAKE_KEY_FOR_TESTING_ONLY.
Step 5: Add Slack Alerting on Secret Detection
A failing pipeline is good. A Slack notification to your security channel is better. Here’s how I wire up alerting when TruffleHog finds something:
yaml
- name: Run TruffleHog
id: trufflehog
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.pull_request.base.sha }}
head: ${{ github.event.pull_request.head.sha }}
extra_args: --only-verified
continue-on-error: true
- name: Notify Slack on secret detection
if: steps.trufflehog.outcome == 'failure'
uses: slackapi/slack-github-action@v1.26.0
with:
payload: |
{
"text": "🚨 TruffleHog detected a verified secret in PR #${{ github.event.pull_request.number }} by @${{ github.actor }}. Review immediately: ${{ github.event.pull_request.html_url }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_SECURITY_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
Note that continue-on-error: true prevents the Slack step from being skipped, but you should still have a final step that fails the workflow explicitly if TruffleHog found something.
Real-World Tips I Use in Production
Pin the TruffleHog action to a specific SHA. Using trufflesecurity/trufflehog@main is convenient but risky — if someone compromises the repo, you could run malicious code in your pipeline. I pin to a commit hash:
yaml
uses: trufflesecurity/trufflehog@5a41c9ab15ae1a2e7a9de84c1cc2f8a9beea47a8
Run TruffleHog as a required status check. In GitHub, go to your branch protection rules and add the TruffleHog job as a required check. This prevents bypassing it with a direct push.
Separate your alert threshold for PRs vs. main. On PRs, I scan with --only-verified (catches real leaks fast, low noise). On pushes to main, I also run with --results=verified,unknown to catch potential secrets that haven’t been verified yet. Belt and suspenders.
Common Errors and How I Fixed Them
Error 1: Error: fatal: bad default revision 'HEAD'
This happens when fetch-depth isn’t set to 0. TruffleHog can’t find the git baseline to diff against. Fix: add fetch-depth: 0 to your actions/checkout@v4 step.
Error 2: Error opening source git: object not found
I hit this when the base SHA was a merge commit that wasn’t included in a shallow fetch. The fix was switching from github.event.pull_request.base.sha to using ${{ github.event.repository.default_branch }} as the base, which resolves correctly in the TruffleHog action context.
Pro Tip: If TruffleHog returns zero findings and you’re suspicious, add
--print-avg-detector-timeto yourextra_args. If detectors are running in under 1ms each, you probably have a fetch-depth or base/head config issue.
[SOURCE: https://trufflesecurity.com/blog/running-trufflehog-in-a-github-action]
FAQ
Q: Can TruffleHog scan GitHub Actions workflow files themselves for hardcoded secrets? A: Yes. TruffleHog scans all files in the diff, including .github/workflows/*.yml files. This is one of the most important places to scan because developers often hardcode tokens directly in workflow environment variables or echo them in run steps.
Q: What’s the difference between TruffleHog’s verified vs. unverified findings? A: Verified findings are confirmed active by TruffleHog making an actual API call to the credential’s service (e.g., calling the AWS STS API for an AWS key). Unverified findings match the pattern but couldn’t be tested — either the API is unavailable or the credential format matches but isn’t confirmed. I recommend starting with --only-verified and expanding scope once you’ve tuned your false positive rate.
Q: How do I prevent TruffleHog from scanning node_modules or vendor directories? A: Use the --exclude-paths flag with a file listing patterns to skip:
bash
trufflehog git file://. --exclude-paths=.trufflehogexclude
In .trufflehogexclude, list directories one per line: node_modules, vendor, .venv, etc.
Q: Will TruffleHog catch secrets that were committed and then deleted from the latest commit? A: Yes — as long as you run with fetch-depth: 0 so TruffleHog has the full git history. This is one of its most powerful features. Even if a developer removes a secret in the next commit, TruffleHog finds it in the historical diff. The only safe fix is credential rotation, not deletion.
Q: How do I tune TruffleHog to reduce noise for a monorepo with many services? A: Use the --only-verified flag as your baseline, then scope scans by path using --include-paths to focus on specific services during PR scans. For a weekly full scan, run without path filtering so nothing slips through. I also recommend setting up a triage workflow where unverified findings go to a low-priority Slack channel while verified ones page the on-call engineer.
Conclusion
Setting up TruffleHog in your GitHub Actions pipeline is one of the highest-ROI security improvements you can make in a single afternoon. It’s not perfect — the --only-verified flag means you’ll occasionally miss an unverifiable secret — but it catches the most dangerous class of leaks: active, working credentials that would give an attacker immediate access to your infrastructure.
Start with the basic workflow in Step 2, get it green, then layer in the alerting and scheduling from Steps 4 and 5. Your future self will thank you.
If this helped you, drop a comment below with the first secret TruffleHog found in your repo. You might be surprised. And if you share this post with a dev who’s been hardcoding keys “just temporarily,” that’s even better.
About the Author
I’m a DevSecOps engineer with 12 years of experience building and securing CI/CD pipelines across fintech, SaaS, and infrastructure companies. My stack includes GitHub Actions, GitLab CI, Terraform, AWS, and a rotating cast of security tools I’ve learned to trust — and TruffleHog is one of the few I’d call essential. I write about practical security engineering for developers who care about shipping safely, not just passing audits.

