# Share a Security Scan Report — Private Link, Comments on Each Finding

Canonical: https://commareports.com/share-security-scan-report
Published: 2026-08-21

> Trivy, Semgrep and npm audit output ends up in CI logs nobody triages. Publish the scan as an HTML report with one curl: a private URL per scan, a comment thread per finding, and a revision per run that shows what's new.

# Share a security scan report

Every pipeline gained a scanner in the last few years. Trivy on the image,
Semgrep or CodeQL on the source, `npm audit` or `pip-audit` on the
dependency tree, something checking the IaC. Every one of them writes its
findings into a CI log, and the CI log is where triage goes to die.

The failure is not detection. It's that a list of findings with no
addressable surface can't be worked. There is no place to write "accepted
until the base image lands in April," no way to see which three of the
sixty findings are new this week, and no link to send someone that opens on
the finding you're talking about.

## Publish the scan instead of logging it

Every serious scanner emits machine-readable output — that's the input:

```bash
# container image
trivy image --format json -o trivy.json "$IMAGE"

# source
semgrep --config auto --sarif -o semgrep.sarif

# dependencies
npm audit --json > npm-audit.json
```

Render whichever you use into one HTML table — severity, package or rule,
location, fixed-in version, and a link to the advisory — and publish it
with a [scoped token](/docs/api-tokens) (`reports:write`):

```bash
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 scan.html \
        --arg title "Image scan — ${GITHUB_SHA:0:7}" \
        '{title: $title, html: $html}')"
```

One report per scan target, `PATCH` on every run. That's the whole
integration.

## Keep it private

This is the one report type where the default matters. A scan report is an
inventory of known weaknesses in systems you operate. Publish it
**private or team-visible**, never public — the [sharing
model](/docs/sharing) covers private, team, domain-gated, and named-reviewer
access. Treat the report with the same care as the CI log it replaces:
same audience, better surface.

Two related habits worth keeping:

- **Scope the token.** `reports:write` and nothing else. See
  [scoped tokens](/docs/api-tokens).
- **Attach, don't inline, the raw output.** The full SARIF or JSON goes in
  as an [asset](/docs/api) (25 MB per file), so the machine-readable
  original is available without bloating the body past the 5 MB HTML cap.

## What the revision history buys you

Scanners are noisy in a specific way: the same sixty findings, every run,
forever. The number that matters is the delta.

Because each scan appends a revision at the same URL, "what's new since
Friday" is a diff between two revisions. A finding that appears for the
first time is visible as an added row; a finding that disappears is a fix
you can point at. Teams that track this properly usually built a diffing
script to do it; here it comes with the publish.

## A thread per finding

The decisions are the valuable part, and they're the part that currently
lives in Slack.

A reviewer highlights `CVE-2026-1234 · openssl · HIGH · fixed in 3.2.1` and
pins a thread: "not exploitable in our build — we don't link the affected
codepath. Re-check when we move off the bullseye base image, tracked in
INFRA-812." That thread stays anchored to the finding across every
subsequent scan revision. The next engineer who opens the report six weeks
later reads the decision instead of asking for it again — the mechanism is
the same one described in [commenting on HTML](/comment-on-html).

Agents can read those threads too. An agent attached over
[Comma's MCP server](/mcp) can call `list_comments` before it opens a bump
PR, and skip the finding somebody already accepted.

## Wiring it into a pipeline

- **Run the publish unconditionally** — a failing scan is the one you most
  want linkable.
- **Announce it** — a [webhook](/docs/api) on `revision.created` posts the
  scan into the security channel; the message is the notification, the
  report is the artifact.
- **Any provider** — [GitHub Actions](/ci/github-actions-html-report),
  [GitLab CI](/ci/gitlab-ci-html-report),
  [Jenkins](/ci/jenkins-html-report),
  [Azure Pipelines](/ci/azure-devops-html-report). It's plain HTTPS.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history. Publish this week's scan privately and triage it on the page.

**[Create your first report →](https://commareports.com/)**

### Related

- [Publish from CI](/docs/ci) — the general pipeline recipe
- [Share a Terraform plan](/share-terraform-plan) — the other artifact that gets truncated
- [How to share an HTML report](/share-html-report) — the six properties a shared report needs
