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-verification toggles 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 --config file is absent, so custom detectors cannot be silently disabled.
  • Skips optional --exclude-paths files when they are absent on disk, so CI-only artifact paths do not generate scan-time lstat noise.
  • Treats non-empty-but-unparseable stdout as a parse failure (see #1044).
  • Treats encountered errors during scan as 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). Benign lstat/stat no such file or directory errors for paths outside the resolved scan set (for example CI-only coverage/ dirs) are logged and ignored so the gate stays green when no secrets were found (see #1631). Partial findings are always preserved.

Options

OptionDefaultFlagPurpose
no_verificationTrue--no-verificationDisable live credential verification
resultsNone--resultsFilter output result types
configNone--configCustom detector configuration
exclude_pathsNone--exclude-pathsFile of regexes for paths to exclude
concurrencyNone--concurrencyNumber 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:

Aspectgitleakstrufflehog
DetectionConfigurable regex rules + entropy800+ provider-specific detectors + entropy
VerificationNoneLive credential verification (disabled by lintro)
Config model.gitleaks.toml rulesCommand-line + custom detector --config
OutputJSON arrayNewline-delimited JSON (JSONL)
Best atFast, tunable, CI-friendly baselineBroad 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.