A practical guide to writing penetration testing reports that connect scope, exact reproduction steps, request evidence, impact, severity, remediation, and retest criteria.

A penetration testing report succeeds when another person can act on it without reconstructing the assessment from scratch.
Compare these two findings:
IDOR found on invoices. Severity: High.
and:
A user in Organization A can retrieve Organization B's invoice by replacing the invoice identifier in
GET /api/invoices/{id}. The response returns the other organization's customer name, billing address, line items, and balance. The same request returns403after the authorization fix described below.
The first statement names a vulnerability class. The second gives a developer a starting condition, an action, an observed result, an impact, and a retest condition. That difference is the heart of a useful penetration testing report.
This guide presents a repeatable way to turn assessment evidence into findings that security leaders can prioritize and developers can reproduce. It follows the reporting principles in the OWASP Web Security Testing Guide and the planning, testing, analysis, and mitigation structure in NIST SP 800-115.

A proxy history, terminal log, or agent trace records what happened during testing. It is valuable source material, but it is not yet a report.
The report must convert that material into decisions:
This conversion requires judgment. A hundred requests may support one finding. Three similar observations may be one root cause, or they may be separate issues because the affected assets, authorization boundaries, or impacts differ.
Use an assessment workspace to preserve evidence and carry validated findings into report-ready sections. The tester still owns scope, interpretation, severity, and the final statement of risk.
Readers need to know what the report can and cannot establish. Put that context before the findings.
At minimum, record:
Avoid vague scope such as “the production application.” Prefer precise descriptions such as “the authenticated customer portal at app.example.test and its documented REST API; mobile clients and third-party payment processing were excluded.”
Limitations are not defensive boilerplate. They define how confidently a reader can generalize the results. If the assessment had one standard user account but no administrator account, say so. If a workflow could not be completed without triggering real payments, record the untested branch.
One report can serve multiple audiences, but one paragraph rarely serves them all.
The executive layer should explain:
The technical layer should contain the exact information needed to reproduce, fix, and retest each issue.
Do not make the executive summary a list of vulnerability names. “One critical and three high findings” says little about exposure. “A standard customer can access invoices belonging to other organizations, and an unauthenticated user can reset another user's password” tells a decision-maker what is at stake.
Likewise, do not remove uncertainty to make the summary sound decisive. A finding that was observed only in a staging configuration should not be presented as confirmed production exposure.
Every confirmed finding should answer the same sequence of questions.

A title should identify the broken control and the affected surface.
Weak: Broken access control
Stronger: Cross-organization invoice access through the invoice detail API
The stronger title remains readable in a dashboard, ticket, or remediation meeting. It also helps distinguish the issue from other access-control failures.
Record what the tester needed before exploitation:
Preconditions prevent a reader from overestimating or underestimating exploitability. Requiring a normal customer account is different from requiring an internal administrator account.
A controlled comparison is often more persuasive than a single suspicious response.
For an authorization issue, show an allowed baseline first:
1GET /api/invoices/inv_A102 HTTP/1.1
2Host: app.example.test
3Authorization: Bearer <organization-a-user>Then show the minimal mutation:
1GET /api/invoices/inv_B884 HTTP/1.1
2Host: app.example.test
3Authorization: Bearer <same-organization-a-user>Explain that inv_A102 belongs to the test user's organization and inv_B884 belongs to a separate test organization. Redact live secrets, session tokens, personal data, and unrelated response fields, but preserve enough structure for reproduction.
“The request worked” is not evidence. State the status, security-relevant response fields, and any resulting state change.
For example:
The mutated request returned
200 OKand exposed the second organization's customer name, billing address, invoice line items, and outstanding balance. Repeating the request with a nonexistent identifier returned404, showing that the successful response was not a generic fallback.
If a result depends on a browser action, include the relevant request as well as the visible outcome. Screenshots are useful supporting evidence, but text and request details remain searchable and easier to reproduce.
Impact should describe what the behavior enables, not simply repeat the vulnerability category.
Ask:
Avoid unsupported extremes. If testing proved access to two seeded invoices, do not claim compromise of every invoice in production. Explain the observed scope, the likely pattern, and what further evidence would be needed to establish scale.
A CVSS score provides a consistent way to communicate vulnerability characteristics. The FIRST CVSS v4.0 User Guide also makes clear that supplemental and environmental context can matter to consumers of a score.
Include the vector or relevant scoring assumptions when you use CVSS. Then add the business context that the score cannot infer: data sensitivity, tenant boundaries, transaction value, exposed population, monitoring, and compensating controls.
Severity and remediation priority are related, but they are not identical. A medium-severity flaw on an internet-facing revenue workflow may be fixed before a higher-scoring issue on an isolated test system.
Good remediation guidance addresses the broken security invariant.
For the invoice example:
Enforce object-level authorization on every invoice operation. Resolve the invoice only within the authenticated principal's permitted organization, and reject access before returning invoice metadata. Apply the same policy to read, update, download, and export paths. Add negative tests using two organizations and multiple roles.
“Validate the invoice ID” would be insufficient. The identifier can be syntactically valid while still belonging to another organization.
A retest is easier when the original report states success criteria.
For example:
These criteria turn the report into an executable remediation contract.
During testing, a response may look vulnerable for several reasons: shared test data, an intentionally public object, an expired session, a mock service, or a transient error. A plausible observation should enter validation, not the final report, until the expected and observed behaviors are separated.
Use three states:
Report confirmed issues as findings. Preserve important inconclusive tests in limitations or an assessment note when they affect coverage. Do not inflate the finding count with rejected candidates.
Repeated instances can share one root cause. Combining them gives developers one coherent fix and keeps the report readable.
But deduplication should preserve meaningful differences. Two endpoints may deserve separate findings when they have different authorization models, impacts, owners, or remediation paths. Within a combined finding, list each validated asset and note any variation in preconditions or exposed data.
Tools can help consolidate repeated evidence and omit rejected candidates from report output. Review the grouping before delivery. Automation can detect similarity; the tester decides whether the security boundary and fix are truly the same.
Before delivering a penetration testing report, verify that:
A strong pentest report maintains a visible chain:
scope → precondition → baseline → controlled mutation → observed evidence → impact → severity reasoning → remediation → retest
If a link is missing, the reader has to guess. If every link is present, the report becomes a practical bridge from offensive testing to engineering work.
This guide synthesizes the reporting and analysis guidance in the OWASP Web Security Testing Guide, the assessment lifecycle described by NIST SP 800-115, and the scoring guidance in FIRST CVSS v4.0. The workflow was reviewed for reproducibility, evidence quality, audience needs, remediation usefulness, and retest clarity.
It focuses on reports for authorized external application and API assessments. The examples use fictional organizations, users, identifiers, hosts, and data.
No single reporting template fits every engagement. Regulatory assessments, source-code reviews, cloud configuration reviews, mobile applications, thick clients, and incident investigations may require additional evidence and attestations.
CVSS does not replace business-specific prioritization. Reproduction steps can also become stale as systems change, so teams should preserve the tested version and assessment date and confirm behavior during retest.