· 8 min read
What a penetration test report contains
The sections of a web application pentest report, what each one is for, and a fictional sample finding written the way I write real ones.
A penetration test report is the written record of an authorized attempt to break into a system: what was tested, under which rules, what was found, how serious each finding is, and exactly how to fix it. For a web application it is usually the only lasting output of the test, so its quality decides whether the money spent turns into fixed code or a PDF nobody opens.
I write these reports at the end of every security audit. This guide walks through the sections I include, why each one is there, and what a good finding looks like, with a sample that is entirely fictional.
What should a pentest report contain?
The structure below is the one I use. It follows the testing and reporting practice described in NIST's Technical Guide to Information Security Testing and Assessment (SP 800-115) and the test categories in the OWASP Web Security Testing Guide, adapted for a small team that has to act on it.
| Section | Who reads it | What it answers |
|---|---|---|
| Executive summary | Founders, customers, investors | How exposed are we, and what must happen first? |
| Scope and rules of engagement | Everyone, and any later auditor | What was tested, when, from where, and what was off-limits? |
| Method | Engineers, auditors | How was it tested, and against which standard? |
| Findings | Engineers | What is wrong, how serious, how to reproduce it and how to fix it? |
| Positive observations | Everyone | What was tested and held up? |
| Retest results | Everyone | Which findings are now closed, and how was that confirmed? |
| Appendices | Engineers | Tool output, test accounts used, full request logs |
What goes in the executive summary?
One page, written for someone who will never read the findings. It says what was tested, the overall picture in plain words, the number of findings at each severity, and the two or three actions that matter most. It avoids jargon and never hides a critical issue behind softer words.
A customer's security team will often ask for this page. That is why I write it to be shareable on its own, while the full report, which contains working attack steps, stays with the people the client names.
Why do scope and rules of engagement come first?
Because a finding means nothing without them. The scope lists the targets: domains, APIs, mobile back ends, cloud accounts. The rules of engagement record the test window, the source addresses I tested from, the test accounts for each role, what was explicitly out of bounds, and who to call if testing turned up something urgent.
This section protects both sides. It shows the client's later auditors exactly what was and wasn't covered, and it shows that every action was authorized. A report that says "no critical findings" over a scope that excluded the payment flow is telling you very little, and the scope section is where a careful reader notices.
What does the method section say?
It names the approach and the standards: automated scanning for breadth, manual testing for depth, and the reference used to decide what to test. For web applications I work through the categories in the OWASP Web Security Testing Guide and map findings to the OWASP Top 10:2025, whose first entry is broken access control. Where a client is working towards a fuller standard, I map findings to the Application Security Verification Standard as well.
The method also states what wasn't possible: a feature that was down during the window, a role with no test account, a rate limit that slowed testing. Those gaps belong in writing, not in a footnote.
What does a good finding look like?
Every finding I write has the same fields: an identifier and a title, a severity, the affected component, a description, the evidence, the steps to reproduce, the impact in plain language, a specific fix, and references. The title says what an attacker can do, not the name of a vulnerability class. "Any signed-in user can download any other company's invoices" is more useful to a product owner than "IDOR".
Here is a sample. It is fictional. The company, the application, the endpoint and the data are invented for this guide; it describes no real client or system.
Fictional sample finding (not a real client or system)
F-03: Any signed-in user can download other customers' invoices
Severity: High
Component: Billing API,
GET /api/invoices/{invoiceId}/pdfon the staging copy of "Example Ledger", a fictional invoicing appDescription: The endpoint checks that the request comes from a signed-in user, but not that the invoice belongs to that user's organization. Invoice identifiers are sequential, so they can be guessed.
Evidence: Signed in as the test user for organization A, a request for an invoice identifier belonging to organization B returned that invoice's PDF with a success status. The request and response, with the PDF contents redacted, are in Appendix B.
Steps to reproduce: (1) Sign in as the organization A test user. (2) Open one of your own invoices and note its identifier. (3) Request the PDF for the identifier one lower. (4) Observe another organization's invoice.
Impact: Any customer, or anyone who registers a free account, can download every customer's invoices: names, addresses, amounts and line items. For a business selling to companies in Europe, that is a personal data breach to assess under the GDPR.
Fix: In the invoice controller, load the invoice through the current user's organization (for example, scope the query by organization ID) and return a not-found response when it doesn't match, so the existence of other invoices isn't revealed. Add an automated test that requests another organization's invoice and expects that response. Consider non-sequential public identifiers as defence in depth, not as the fix.
References: OWASP Top 10:2025 A01 Broken Access Control; OWASP Web Security Testing Guide, authorization testing.
Retest: Closed. After the fix, the request in step 3 returned not-found; the new automated test is in the client's CI.
Three things make that finding useful. The evidence is reproducible by someone who wasn't there. The impact is written in business terms, so it competes fairly for engineering time. And the fix is specific enough to implement and test, with the test itself named, so the issue can't quietly return.
How is severity decided?
Most reports use the Common Vulnerability Scoring System from FIRST for the technical part. The CVSS v4.0 specification maps scores to qualitative ratings: None, Low, Medium, High and Critical, and it notes that using those ratings is optional. CVSS describes the vulnerability; it doesn't know your business.
So I adjust for context and explain the adjustment in the finding. The same missing authorization check is more serious on an endpoint that returns medical records than on one that returns a public product list. A finding reachable only by an administrator who could already do the damage another way is less serious than its raw score suggests. The rule I follow is that a reader should be able to disagree with a rating because the reasoning is written down.
Why include what held up?
Because a report that lists only failures can't be read correctly. If I tested the password reset flow, the file upload handling and the session management, and all three held, the client should know that, and so should their customer's security team. It also helps prioritize: effort goes where the weaknesses are, not into re-checking what is already sound.
What happens at the retest?
After the client's team fixes the findings, I test each one again with the same steps and update the report: closed, partly fixed, or still open, with the evidence. The retest is the part of the engagement that turns a list of problems into a statement a client can make to a customer. A report without one tells you what was wrong on a given day; a report with one tells you what is fixed.
What should a report not contain?
Scanner output pasted in as findings, without confirmation that the issue is real. Severity inflated to look thorough. Fixes that say "sanitize input" without saying where and how. Real customer data copied into evidence when a redacted screenshot would do. And promises the test can't keep: a penetration test shows what one tester found in one window, against one scope. It is not a certificate. SOC 2 and ISO 27001 need an accredited auditor; a good report helps you get ready for one.
How I do this
Every security audit and penetration test I run starts with scope and rules of engagement in writing, uses automated scanning for breadth and manual testing where the real risks hide, and ends with a report in the shape above, a walkthrough with your team and a retest after your fixes. If you're facing a security questionnaire or a launch, the contact page is the place to start. If you'd like the wider view of a system first, the architecture review covers security posture alongside architecture, cost and delivery.
Sources
Related service
- Service: Security audit and penetration test · Security and compliance engineering
An attacker's view of your application and infrastructure, and a fix list you can act on.
- Service: Architecture review and due diligence · Technical leadership
An independent, written view of a system: what's solid, what's risky, and what it will cost to fix.