Skip to content

LearnReporting

Writing a finding that gets fixed

How we write a security finding an engineer can reproduce and fix without a call: title, severity reasoning, steps, the fix as a change, and the retest.

Area
Reporting
Stage
Reporting
Reviewed
On this page
  1. What to check first
  2. Write the title as the impact
  3. Use the same structure every time
  4. Steps a developer can rerun
  5. Severity with its reasoning
  6. Write the fix as a change
  7. Evidence without collateral
  8. Plan the retest
  9. Words we drop
  10. Tools
  11. Related research

A finding is only worth what gets fixed because of it. The reader who matters is not the security lead who commissioned the test. It is the engineer who picks up the ticket three weeks later, was not on any call, and has to decide in ten minutes whether the finding is real and what to change. Everything below is written for that person.

What to check first

  • One finding, one root cause. Five endpoints missing the same check are one finding with five affected items, not five findings.
  • Steps a developer can rerun in their own environment with their own test accounts.
  • A severity the client can defend to someone who disagrees, with the reasoning written down.
  • The fix written as a change to their system, not as a principle.
  • What we will check on retest to close it.

Write the title as the impact

Weak:   Insecure Direct Object Reference
Better: Any logged-in user can read other customers' invoices through /api/invoices/{id}

The weak title names a category the engineer has to look up. The better one says who can do what, and where, in one line. Someone triaging forty tickets can rank it without opening it.

Use the same structure every time

Title
Severity, with the CVSS vector and one sentence of context
Affected: hosts, endpoints, versions, accounts
What we did: numbered steps with the exact requests
What it allows: the impact we showed, and the impact we assumed but did not test
Fix: the change, and an interim measure if the change will take time
Retest: what we will check to close it
References

The split between impact shown and impact assumed is the part reports most often leave out. It is also what stops an argument about severity, because the assumptions are written down where the client can challenge them.

Fix. If you commission tests, put this structure in the statement of work. A finding without reproduction steps or a written severity rationale should go back to the tester before it reaches an engineer.

Steps a developer can rerun

GET /api/invoices/10432 HTTP/1.1
Host: app.example.com
Authorization: Bearer <token for tester-b, redacted>

HTTP/1.1 200 OK
Content-Type: application/json

{"id":10432,"customer":"<tenant A, redacted>","total":"<redacted>"}

We include the raw request and response, with secrets and personal data redacted, timestamps in UTC, and the names of the test accounts used. Screenshots support the text; they do not replace it, because nobody can copy a request out of a screenshot.

Severity with its reasoning

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
High: any customer account can read every other customer's invoices, which include
billing addresses and amounts. We read two records to prove it and stopped there.

The vector lets anyone recompute the score in the FIRST calculator and see which metric drives it. The sentence explains it in terms of the client's business. Two cases from our research show what happens when the reasoning is missing. A VPN authentication bypass was rated medium and exploited four days after the fix, because the score left out that the device sits on the internet and grants network access. An F5 bug published as a denial of service was later reclassified as remote code execution.

Write the fix as a change

Weak:   Implement proper access controls.
Better: In InvoiceController.show, load the invoice with WHERE id = ? AND org_id = ?,
        taking org_id from the session, and return 404 when no row matches. Add a test
        that requests another organization's invoice and expects 404.

The better fix names the place, the change and the test that keeps it fixed. When the change will take weeks, we add an interim measure, such as a WAF rule or a feature flag, and say plainly that it is temporary.

Evidence without collateral

We collect the minimum data that proves the issue. Records belonging to real customers are redacted in the report and deleted from our systems at the end of the retention period agreed in the contract. A finding that proves itself with two records does not need two thousand.

Plan the retest

The retest reruns the original steps plus the obvious variants: the other HTTP methods, the sibling endpoints, the other object types. A fix that blocks the one request in the report while leaving the pattern in place fails retest, and the report says so.

Words we drop

"Could potentially" becomes "can" or is cut. "Best practice" becomes the actual practice. "Critical" appears only next to a score that supports it.

atarus-report-kit is a single-file, offline reporting tool, so a draft with client data in it never has to sit on someone else's service.