Skip to content
MD-5

// Sample report

Sample penetration test report

An abridged example of the report you receive, from a fictional web application. Every real report follows this structure.

Illustrative example. “Acme Invoicing” is a fictional application. Client reports are confidential and never published.

01 · Executive summary

Overall risk: High

A grey-box penetration test of the Acme Invoicing web application and API was performed over five days. Testing was carried out as an unauthenticated visitor and as users in the member and admin roles.

The most significant issue allowed any logged-in customer to read and modify invoices belonging to other customers, exposing names, addresses and billing history across all accounts. This was reported on the first day of testing and fixed before the report was issued.

Authentication and session management were otherwise well implemented. Five further issues of medium or lower severity were identified; four have been fixed and verified, and one has been formally accepted as a risk by Acme.

High
1
Medium
3
Low
1
Info
1

04 · Findings summary

All findings

ID Finding Severity CVSS Retest
F-01 Broken access control on invoice endpoints (IDOR) HIGH 8.1 Fixed
F-02 Password reset tokens remain valid after use MEDIUM 6.8 Fixed
F-03 Stored cross-site scripting in customer notes MEDIUM 5.4 Fixed
F-04 No rate limiting on the login endpoint MEDIUM 5.3 Risk accepted
F-05 Missing HSTS and Content-Security-Policy headers LOW N/A Fixed
F-06 Framework version disclosed in error pages INFO N/A Fixed

05 · Detailed finding

HIGH F-01

Broken access control on invoice endpoints (IDOR)

CVSS 3.1
8.1 · AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Category
OWASP A01:2025 Broken Access Control · CWE-639
Affected
GET, PUT /api/v2/invoices/{id}
Status
Fixed, verified in retest

Description

The invoice API checks that a request comes from a logged-in user, but not that the invoice requested belongs to that user. Invoice IDs are sequential, so every invoice in the system can be reached by changing the number in the URL.

Impact

Any customer, including one who signed up for a free trial, could read the names, postal addresses, line items and totals on every other customer’s invoices, and change their contents. If exploited, this would be a reportable personal-data breach under GDPR and similar laws.

Evidence

Logged in as test user A, a request for an invoice owned by test user B returned B’s data:

GET /api/v2/invoices/10482 HTTP/1.1
Host: app.acme-invoicing.example
Authorization: Bearer [user A token, redacted]

HTTP/1.1 200 OK
{ "id": 10482, "customer_id": 311, // user B, not user A (customer_id 207)
  "billing_name": "[redacted]", "total": "1,240.00", ... }

Remediation

  • Scope every invoice query to the current user’s account on the server, e.g. look up invoices by id and customer_id from the session, never from the request.
  • Apply the same ownership check to update and delete handlers, not only reads.
  • Add automated tests that request another user’s objects and expect 404.
  • Consider non-sequential identifiers (UUIDs) as defense in depth, not as the fix.

Retest

Retested after the fix: requests for invoices owned by other customers now return 404 Not Found for both read and update. Closed.

Want a report like this for your app?

Tell us what you need tested. You’ll hear back within one business day, and we’ll agree the price with you before any work starts.

Get a quote