Share a pentest report

A penetration test report is the single most sensitive document most engineering organizations produce. It is a prioritized, reproducible list of how to compromise the system, written by someone who did.

And the standard delivery mechanism is an email attachment.

Which means: it is now in the client's inbox, the account manager's inbox, the two engineers it was forwarded to, whatever laptop opened it on a train, and a Slack channel where somebody dropped the PDF "so everyone can see it". None of that is revocable, and nobody can say who holds a copy.

Publish it instead

Most testing platforms and consultancies can export HTML, and any report written in markdown or Word converts cleanly:

pandoc pentest-2026-q3.md -s -o report.html --toc --embed-resources

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 "Penetration test — Q3 2026, external perimeter" \
        '{title: $t, html: $html}')"

--embed-resources matters here: it inlines the screenshots of the proof-of- concept so the report is one file rather than a folder with dead image links. If you would rather keep the evidence as separate files, upload them as assets and their references are rewritten to the uploaded copies.

Access is the whole point

Reports are private by default. For an engagement, the two shapes that work:

  • Named reviewers — an explicit list of people. The right default for a report with unremediated critical findings. You can see who is on it, and you can remove someone the day they change roles.
  • Domain-gated — anyone with an email at client.com. Reasonable for a final report after remediation, and it avoids the consultancy administering a user list on the client's behalf.

Both are revocable, which an emailed PDF is not. See the sharing model.

Do not make it public. The one exception is a summary attestation letter that deliberately contains no findings detail — and that should be a separate report, not the same one with a different setting.

Remediation belongs on the findings

The part that usually goes badly is not delivery, it is the six weeks afterwards. The client fixes things, the tester needs evidence, and the conversation is spread across a spreadsheet, an email chain and two tickets.

Anchored threads put it back on the document:

  • "Fixed in 4.2 — PR #918, deployed 12 Sep" on the finding.
  • "Accepted risk, compensating control: WAF rule 4471, owner: Priya" on the one that is not getting fixed.
  • "Retest confirms remediated" from the tester, on the same thread.

See commenting on HTML. When the retest report is issued, PATCH the same report id — one URL for the whole engagement, a revision for the initial report and a revision for the retest:

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 retest.html \
        --arg t "Penetration test — Q3 2026, retest" '{title: $t, html: $html}')"

The client's link does not change. The remediation trail is the revision list plus the resolved threads, which is a far better artifact for the next audit than "we believe these were fixed".

What this replaces

Instead of You get
An emailed PDF you cannot recall Revocable access, visible list
"Which version is the latest?" One URL, revisions behind it
A remediation spreadsheet Threads on the findings themselves
A forwarded copy in a Slack channel Access that ends when you end it

Limits

  • Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. Proof-of-concept screenshots are what push a report over the entry limit — upload them as assets rather than base64-inlining them.
  • 60 requests/minute per token.

Try it

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

Publish a report privately →

Related