trufflehog
TruffleHog Tool Analysis
Overview
TruffleHog is a secrets scanner that detects credentials — API keys, tokens, private keys, and 800+ other credential types — using provider-specific regex detectors and entropy analysis. Its signature capability is optional live verification: it can call the corresponding provider API to confirm whether a detected credential is active.
This analysis describes Lintro’s TruffleHog wrapper and how it complements the existing
gitleaks integration.
Core Tool Capabilities
- 800+ detectors: provider-specific patterns (GitHub, AWS, GCP, Slack, …)
- Verification:
--no-verificationtoggles live credential checking - Scan sources:
filesystem,git,GitHub,s3,gcs, Docker images - Result filtering:
--results verified,unverified,unknown - Custom detectors:
--config - Path filtering:
--include-paths/--exclude-paths - Output:
--json(newline-delimited JSON),--json-legacy, GitHub Actions
Lintro Implementation
Lintro runs TruffleHog in filesystem mode, which fits Lintro’s file-oriented model
(git-history scanning is a separate concern from linting a working tree).
Verification is disabled by default
Lintro passes --no-verification by default. Verification makes outbound network calls
to third-party providers to test candidate credentials. That is inappropriate for a
linter that must be:
- Hermetic — no network dependency for a local or CI lint run.
- Deterministic — results must not depend on provider availability or on whether a test/fake credential happens to resolve.
- Safe — never transmit repository contents to external services implicitly.
Verification can be explicitly re-enabled per run via
--tool-options trufflehog:no_verification=False, and the verified status is still
surfaced on every finding (it is simply false when verification is off).
Output parsing
TruffleHog emits newline-delimited JSON (one object per line) on stdout, and
writes diagnostic logs to stderr. The native parser (lintro/parsers/trufflehog/)
reads stdout only (see #1043), skips diagnostic lines (objects without
SourceMetadata), and maps each finding to a TrufflehogIssue carrying the detector
name/type, verification status, decoder, source metadata (file + line), and detector
ExtraData (e.g. rotation guide).
Safety hardening
TruffleHog exits 0 for clean scans, for findings (unless --fail is passed), and
even when it cannot read a scan target (it logs encountered errors during scan;
other targets in the same run may still emit partial findings). A secrets scanner must
never report a clean pass from a scan that did not run. The wrapper therefore:
- Resolves every requested path to an absolute path before scanning, because a relative path that does not resolve against TruffleHog’s working directory yields a silent empty result.
- Fails when an explicitly configured
--configfile is absent, so custom detectors cannot be silently disabled. - Skips optional
--exclude-pathsfiles when they are absent on disk, so CI-only artifact paths do not generate scan-timelstatnoise. - Treats non-empty-but-unparseable stdout as a parse failure (see #1044).
- Treats
encountered errors during scanas a failure when the per-error reasons are missing, unparseable, or genuine (permission denied, a missing target that was part of the resolved scan set, crashes). Benignlstat/statno such file or directoryerrors for paths outside the resolved scan set (for example CI-onlycoverage/dirs) are logged and ignored so the gate stays green when no secrets were found (see #1631). Partial findings are always preserved.
Options
| Option | Default | Flag | Purpose |
|---|---|---|---|
no_verification | True | --no-verification | Disable live credential verification |
results | None | --results | Filter output result types |
config | None | --config | Custom detector configuration |
exclude_paths | None | --exclude-paths | File of regexes for paths to exclude |
concurrency | None | --concurrency | Number of concurrent workers |
Why SARIF is not used
TruffleHog does not emit SARIF (its output formats are --json, --json-legacy,
and --github-actions), so the shared SARIF ingestion path
(docs/design/sarif-ingestion-evaluation.md) is not an option. Even if it did, SARIF’s
result model has no natural home for TruffleHog’s two most important security signals —
the detector identity and the verification status — which would have to be
squeezed into generic properties. A native parser keeps these fields first-class.
Lintro therefore uses a native parser.
Relationship with gitleaks
TruffleHog and gitleaks are complementary, not redundant:
| Aspect | gitleaks | trufflehog |
|---|---|---|
| Detection | Configurable regex rules + entropy | 800+ provider-specific detectors + entropy |
| Verification | None | Live credential verification (disabled by lintro) |
| Config model | .gitleaks.toml rules | Command-line + custom detector --config |
| Output | JSON array | Newline-delimited JSON (JSONL) |
| Best at | Fast, tunable, CI-friendly baseline | Broad provider coverage, richer per-detector data |
They overlap on common credential shapes (e.g. GitHub PATs, AWS keys), but each catches
secrets the other can miss because their detection engines differ. Running both
maximizes coverage; each is an independent, no-fix SECURITY tool in Lintro.
Usage
# Scan the current directory
lintro check --tools trufflehog .
# Filter to a specific path and raise concurrency
lintro check --tools trufflehog --tool-options trufflehog:concurrency=8 src/
# Explicitly enable live verification (makes network calls — off by default)
lintro check --tools trufflehog --tool-options trufflehog:no_verification=False .
Installation
- Homebrew:
brew install trufflehog - Binary: download from the releases page
- Lintro: bundled via
scripts/utils/install-tools.sh(checksum-verified)
Run lintro doctor to confirm the detected version.