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:

# 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 (reports:write):

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 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.
  • Attach, don't inline, the raw output. The full SARIF or JSON goes in as an asset (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.

Agents can read those threads too. An agent attached over Comma's MCP server 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 on revision.created posts the scan into the security channel; the message is the notification, the report is the artifact.
  • Any providerGitHub Actions, GitLab CI, Jenkins, Azure Pipelines. 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 →

Related