Writing Remediation a Developer Will Actually Action

Writing pentest remediation a developer will action: name the location, give the fix at the right level, say what counts as fixed, and order it by impact.

Remediation a developer will action names the exact location, gives the fix at the level they can apply it without writing their code for them, states what you would accept as fixed, and stands on its own when pasted into a ticket. The remediation is the part that decides whether a finding is still open in six months, and it is routinely the weakest paragraph in the report.

The failure is rarely ignorance. It is that remediation gets written last, at the end of a long day, by somebody who has already proved the point to themselves and is now describing it to a reader they cannot see. What comes out is a category name where an instruction should be.

The advice that gets ignored

"Implement proper input validation." "Ensure access controls are enforced." "Follow the principle of least privilege." Each of these is true, none is actionable, and a developer reading them learns only that you did not want to be pinned down.

Worse, they are unfalsifiable. A developer can add a length check, call it input validation, and close the ticket. You will find the same injection on the retest, and the argument that follows will be about whether they did what you asked, because what you asked for did not say.

Name the location, not the class

The person fixing this needs to know where to go. Not the vulnerability class, which they can look up, but the parameter, the endpoint, the file where the pattern lives if you saw it, and whether the same pattern appears elsewhere.

"The account_ref parameter on GET /api/v1/accounts is concatenated into the query" tells a developer where to open their editor. "SQL injection is present in the application" starts a search that may take them a day and may end somewhere you did not test.

Where the same weakness appears at several locations, list them. A remediation that fixes one of five instances is a remediation that will be marked complete.

Give the fix at the level they can apply it

There is a level of specificity that helps and a level that is presumptuous, and the line sits at the point where you would need to know their codebase. Say what the correct pattern is; do not write their function.

  • Useful: "Use a parameterised query so the value is never part of the SQL string. In this framework that is the query builder rather than string interpolation."
  • Presumptuous: a fifteen-line code sample using a library they may not have, in a style their codebase does not use.
  • Useless: "Sanitise user input before use."

If the framework has a documented safe way to do the thing, name it. That single sentence saves a developer the half hour of deciding which of four approaches you meant, and it moves the conversation from whether to how.

Say what you would accept as fixed

This is the sentence most reports are missing, and it is the one that makes a retest fast. State the observable condition that means the finding is closed.

"Fixed when the same request returns the same response for a valid identifier and a 403 for an identifier belonging to another tenant" is a criterion. Both sides can check it, neither can argue about it, and the developer knows when to stop. It is also exactly what a retest run properly verifies.

It also protects you. A finding without a closure criterion is one where the client decides what fixed means, and they will decide generously, because they are the ones under pressure to close it.

When there is no clean fix

Sometimes the correct answer is architectural and the client cannot do it this quarter. Pretending otherwise produces a remediation nobody will attempt, which is worse than an honest one they will.

Give the real fix first, then the mitigations, ranked, with what each one does and does not buy. A rate limit in front of an enumeration weakness reduces the volume an attacker gets and does not remove the weakness, and saying so is the difference between a mitigation and a false sense of completion.

Where the client has already accepted the risk, that belongs in the finding as a decision with a name and a date attached, not as a softened remediation, as described in when a client accepts the risk instead of fixing it. Softening the advice to match a decision is how a report stops being evidence of anything.

Do not paste the vendor page

A remediation section that is three paragraphs of copied guidance and one line of your own is transparent to anybody who has read the guidance. It also ages: the link rots, the vendor reorganises, and your report now cites something that says something else.

Cite the reference, summarise the part that applies here, and write the rest yourself. What the client is paying for is the judgement about which part applies to them, and that is the part a copied page never contains.

Order matters more than completeness

A findings list with nineteen remediations and no order is a list that gets worked from the top, which is usually the order you happened to write them in. If the third item is the one that closes off the path to customer data, it should not be third because of when you found it.

Order by what the fix removes, not by severity label alone. Two Highs are not equivalent when one breaks a chain and the other is an isolated weakness on a system nobody can reach. Where a single fix closes several findings, say so in each of them, because that fix has just become the cheapest thing on the list and nobody can see that from the individual entries. Agreed vulnerability remediation timelines then turn the order into dates.

Where sequencing matters technically, state it. Rotating a credential before you have fixed the leak that exposed it is work that will be done twice, and a developer following a list top to bottom has no way to know that from the findings alone.

Write it for the person who did not read the description

The remediation is the section most likely to be read in isolation. It gets pasted into a ticket, forwarded to a team that was not on the call, and read by somebody who has the fix to make and no interest in how it was found.

That means it has to carry enough context to stand alone: what is wrong, where, and what correct looks like. Not the whole description again, but not a sentence that only parses if you have just read three paragraphs above it. "Fix this by parameterising the query" is meaningless in a ticket; "the account_ref parameter on this endpoint is concatenated into SQL, so parameterise it" survives the journey.

A useful habit is to read only your remediation sections, in order, as if you were the developer. Anything that does not make sense on its own gets one more clause. It takes ten minutes and it removes most of the follow-up questions you would otherwise field a fortnight later.

A finding your firm has written well, remediation included, can be added to the findings Library and imported into the next engagement, so the version you argued about once is where the next tester starts, with the instance specifics still to write. Where AI is switched on, it drafts fields from the evidence against prompts you can read and edit, on your own key or a local model, and the draft is a starting point the tester still owns. See how findings and the Library work.

Common Questions

How do you write good remediation advice in a penetration test report?
Name the exact location, such as the parameter, endpoint or file, rather than the vulnerability class. Give the correct pattern at the level the developer can apply, naming the framework's documented safe method where one exists. State the observable condition that means the finding is fixed, and make the paragraph stand on its own, because it will be pasted into a ticket and read without the description.
How specific should pentest remediation be?
Specific enough that a developer knows where to open their editor and what correct looks like, but not so specific that you are writing their function. "Use a parameterised query; in this framework, the query builder rather than string interpolation" helps. A fifteen-line code sample in a style their codebase does not use is presumptuous, and "sanitise user input" is useless.
What should remediation say when there is no clean fix?
Give the real fix first, even if it is architectural and cannot happen this quarter, then the mitigations in order, each with what it does and does not buy. A rate limit in front of an enumeration weakness reduces what an attacker gets without removing the weakness, and saying so separates a mitigation from a false sense of completion.
What is a closure criterion for a pentest finding?
A sentence stating the observable condition that means the finding is fixed, for example that a request for another tenant's identifier now returns 403 while a valid identifier still works. Both sides can check it and neither can argue about it, the developer knows when to stop, and the retest becomes fast rather than a debate about what was asked for.

Keep Reading

Setup guide

See the product set up, end to end

The whole setup in 10:01, with chapters you can jump to: registering, your own domain, your own database in any of three places, your own mail server, branding, the first sign-in, updating the database on Supabase Cloud, and moving the database if you change your mind.

  • 0:00 · Registering, signing in, and the free trial
  • 0:44 · Your name and your firm’s name
  • 0:50 · Custom domain
  • +6 more