Share a TruffleHog report

Every other scanner on this site produces a report you should be careful with. TruffleHog produces one you should be paranoid about.

The difference is verification. TruffleHog does not just pattern-match for things that look like an AWS key — it calls the provider to check whether the key still works:

trufflehog git file://. --only-verified --json > findings.json
trufflehog github --org=acme --only-verified --json > findings.json
trufflehog s3 --bucket=acme-backups --only-verified --json > findings.json

A verified finding contains the live credential. That output is not a list of risks; it is a working set of keys to your infrastructure.

So the first rule of sharing a TruffleHog report is: do not share the raw output.

Render, redacting as you go

There is no built-in HTML writer, which turns out to be the right shape — the rendering step is where redaction happens:

trufflehog git file://. --only-verified --json > findings.json

jq -rs '
  "<h1>Verified secrets</h1><table>
   <tr><th>Detector</th><th>File</th><th>Commit</th><th>Status</th></tr>" +
  (map("<tr><td>\(.DetectorName)</td>
        <td>\(.SourceMetadata.Data.Git.file // "—")</td>
        <td>\(.SourceMetadata.Data.Git.commit[0:12] // "—")</td>
        <td>VERIFIED — rotate</td></tr>") | join("")) +
  "</table>"
' findings.json > report.html

Note what is not in that template: .Raw, .RawV2, and .Redacted. The report tells a reader which detector fired, in which file, in which commit — enough to find and rotate the credential — and does not restate the secret.

Anyone who needs the value itself can get it from the source, under whatever access controls that source already has.

Publish it

curl -fsS -X POST "https://commareports.com/api/v1/reports" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html report.html \
        --arg t "Verified secrets — acme/api, 2026-09-08" \
        '{title: $t, html: $html}')"

Or drag report.html into the app.

Access: named reviewers. Not team, not domain-gated, for the initial report. The set of people who need to see which credentials are live and unrotated is small and specific. See the sharing model.

Track rotation, not detection

The finding is the easy part and it is finished the moment the scan completes. The thing that actually needs a shared artifact is the rotation status, which is where these incidents usually stall — someone finds nine leaked keys, rotates six, and nobody can later say which three were left.

A thread per finding turns that into a record:

  • "Rotated 2026-09-08 14:20, old key deleted in IAM" — done.
  • "This is a test fixture, value is a placeholder from the SDK docs" — false positive, argued once, on the record.
  • "Cannot rotate until the batch job is redeployed — INF-902" — the one that needs following up.

See commenting on HTML. Resolve the thread when the key is dead, and the unresolved threads are your remaining exposure.

In CI, gate but still publish

trufflehog git file://. --since-commit "$BASE_SHA" --only-verified --json > findings.json
# …render report.html, redacting…

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html report.html \
        --arg t "Secret scan — $GITHUB_REF_NAME" '{title: $t, html: $html}')"

[ "$(jq -s length findings.json)" -eq 0 ]   # fail the build after publishing

Publish first, then fail. A gate that blocks the build without leaving a readable report behind is a gate that gets bypassed with --no-verify.

Limits

  • Entry HTML: 5 MB. A redacted findings table is small; a first scan of a long git history with unverified findings included is not — which is another reason to publish --only-verified.
  • 60 requests/minute per token.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a scan privately →

Related