Share a Gitleaks report

Gitleaks scans your git history for committed credentials. It is fast, the rules are good, and its output is a JSON file that is — and this is the part that catches people — a list of your secrets in plaintext.

That single fact should shape how you share it.

Scan with --redact. Always.

gitleaks detect \
  --redact \
  --report-format sarif \
  --report-path gitleaks.sarif || true

--redact replaces each matched secret value with a placeholder while keeping everything you need to act: the file, the line, the commit, the author, and which rule fired. That is a triage document. Without it, the report is a second copy of the leak — one that then gets attached to a ticket, emailed, and pasted into a channel.

The || true matters because Gitleaks exits non-zero when it finds something, and the publish step has to survive exactly the run you care about.

Convert to HTML

Gitleaks has no HTML writer. SARIF is the right intermediate:

pip install sarif-tools
sarif html gitleaks.sarif --output gitleaks.html

The same converter handles Semgrep, Trivy, Checkov and anything else in the pipeline that emits SARIF, so it is worth setting up once. See share a Semgrep report.

Set access, then publish

Order matters here more than on any other report type. Reports created over the API start private unless the create call says otherwise — lean on that, and don't widen it casually.

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 gitleaks.html \
        --arg title "Secret scan — $(git rev-parse --short HEAD)" \
        '{title: $title, html: $html}')"

Use a scoped token (reports:write) from CI secrets. Note that widening access needs the separate sharing scope, so a publishing token cannot make a secret-scan report public even by mistake — which is the correct blast radius for this particular job. See publishing from CI.

Keep it private or team. Password gates and expiring links are Enterprise and are a reasonable thing to want here. See sharing & access control.

Triage and rotation, recorded on the finding

Secret-scanning findings have a distinctive shape: most of them are in history, not in the working tree, and the question is never "is this a secret" but "was it rotated, and by whom, and when."

That answer is what gets lost. It lives in a Slack thread, or in one person's memory, and six months later the same commit fires the same rule and somebody re-investigates from scratch.

Published to a URL, each finding carries its own thread:

  • Rotated on this date, ticket here.
  • Not a secret — it's a test fixture, here's why.
  • Still live — this one is urgent, owner assigned.

It stays anchored to that finding across scans. See commenting on HTML.

Revision diffs then answer the trend honestly: two scans, one diff, which findings actually cleared — as opposed to a count that dropped because somebody widened .gitleaksignore.

A note on history rewrites

If a finding leads to a history rewrite, the report's revision history becomes the record of what the repository looked like before it — which is occasionally exactly what an auditor asks for, and occasionally something you would rather not retain. Delete the report if it is the latter; the decision should be deliberate either way.

Limits

  • Entry HTML: 5 MB. A first scan of a long history can exceed it — scan a commit range, or publish by rule. Assets: 25 MB per file, 250 MB and 500 files total.
  • Scripts run, sandboxed (allow-scripts, no allow-same-origin).
  • 60 requests/minute per token.

Try it

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

Publish a scan report →

Related