Share a SARIF report

SARIF solved a real problem: every scanner used to invent its own output format, so every CI integration was bespoke. Now CodeQL, Semgrep, Trivy, Checkov, Bandit, Grype, KICS and most of the rest emit the same JSON.

It solved that problem for tools. For people it created a new one. A .sarif file looks like this:

{"runs":[{"tool":{"driver":{"name":"CodeQL","rules":[…]}},
  "results":[{"ruleId":"js/sql-injection","level":"error",
  "locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/db.js"},
  "region":{"startLine":42,"startColumn":17}}}]}]}]}

Correct, complete, and useless to hand to a developer or an auditor. So the findings end up summarized by hand into a ticket, which is where the fidelity goes.

Render it to HTML

sarif-tools is the shortest path:

pip install sarif-tools
sarif html results.sarif --output report.html

Others, depending on what you already have:

# Microsoft's multitool
npx @microsoft/sarif-multitool rewrite results.sarif --output merged.sarif

# merge several scanners into one report first
sarif copy codeql.sarif semgrep.sarif trivy.sarif --output all.sarif
sarif html all.sarif --output report.html

That merge step is worth doing. A reviewer wants "here is everything we found", not three files from three tools with three overlapping definitions of "critical".

Publish it

Drag report.html into the app, or POST 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 "Security scan — api-gateway, 2026-09" \
        '{title: $t, html: $html}')"

Report HTML renders with scripts enabled inside a sandboxed iframe (allow-scripts, no allow-same-origin), so any filtering or severity grouping in the generated report keeps working.

Set the access before you send the link

This is the part worth being deliberate about. A SARIF-derived report contains file paths, code snippets, and in many cases the precise line of an unremediated vulnerability. That is a document you are handing an attacker if it leaks.

Reports are private by default. Keep security scans on team, domain-gated, or named-reviewer access — see the sharing model. Publish a public link only for a scan of something already public, and even then check what the snippets contain.

From CI

semgrep --config auto --sarif --output semgrep.sarif .
sarif html semgrep.sarif --output report.html

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 "Security scan @ $GITHUB_SHA" '{title: $t, html: $html}')"

PATCHing one report id keeps a single URL per repo and appends a revision per scan — so "when did this finding first appear?" is answerable, which is exactly the question an auditor asks and a CI log cannot answer after it expires.

What review adds

  • Anchored threads per finding, so a triage decision — "accepted risk, ticket SEC-412" — lives on the finding rather than in someone's memory. See commenting on HTML.
  • Revisions, which give you the remediation trend for free.
  • Named reviewers, when a specific person has to sign off.

Limits

  • Entry HTML: 5 MB. A scan with thousands of findings will exceed this — filter by severity (sarif html --blame-filter, or filter the SARIF before rendering) rather than publishing everything.
  • 60 requests/minute per token.

Try it

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

Publish a security report →

Related