Confidential · Example only
Penetration test report: customer portal and API
1Executive summary
Example Co. asked us to test its customer portal and API, including the AI support assistant, before renewing a contract with its largest customer. We tested as an anonymous visitor, and as an admin and a standard user in two separate customer accounts.
The most serious issue let any logged-in customer read every other customer's invoices by changing a number in the request. We reported it by phone on the day we found it. It was fixed during the testing window and confirmed closed at retest.
At retest, three of the four scored findings were fixed and verified. The remaining medium finding, in the support assistant, has a fix scheduled for the next release, with uploads limited to staff in the meantime.
- 0Critical
- 1High
- 3Medium
- 0Low
- 1Info
2Findings summary
| ID | Finding | Severity · CVSS 4.0 | After retest |
|---|---|---|---|
| LIM-01 | Any customer can read other customers' invoices through the API | High7.1 | Fixed, verified |
| LIM-02 | Password reset links stay valid after use | Medium6.3 | Fixed, verified |
| LIM-03 | Support assistant follows instructions hidden in uploaded files | Medium5.9 | Open, fix scheduled |
| LIM-04 | Stored cross-site scripting in support ticket subjects | Medium5.1 | Fixed, verified |
| LIM-05 | Error pages reveal the framework and version | Info— | Risk accepted |
3LIM-01: Any customer can read other customers' invoices
- Severity
- High 7.1
- CVSS v4.0
- 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
- Affected
- GET /api/v2/invoices/{id}
- References
- OWASP API1:2023 Broken Object Level Authorization · CWE-639
Description
The API returns an invoice to any logged-in user who asks for it by ID. It checks that the user is authenticated, but not that the invoice belongs to the user's organization. Invoice IDs are sequential, so every invoice in the system can be listed by counting.
Steps to reproduce
- Log in as a standard user in tenant A.
- Request one of tenant A's own invoices,
GET /api/v2/invoices/48213. The API returns it, as expected. - Change the ID to
48214, an invoice belonging to tenant B. The API returns it too.
Evidence
GET /api/v2/invoices/48214 HTTP/2
Host: api.example.com
Authorization: Bearer eyJhbGciOi…[redacted] ← standard user, tenant A
HTTP/2 200 OK
Content-Type: application/json
{"id":48214,"tenant":"tenant-b","customer":"[redacted]",
"billing_address":"[redacted]","total":"[redacted]","line_items":[…]}Impact
Any customer, or anyone holding one customer's stolen password, could download every other customer's invoices, including names, billing addresses and amounts. It takes no special skill: a short script that counts upward would collect them all.
Remediation
- Check ownership on every object lookup. Load the invoice only where it belongs to the caller's tenant, for example by filtering on tenant ID in the query.
- Return 404 rather than 403 for objects in other tenants, so IDs can't be probed.
- Add an automated test, for every API resource, that requests another tenant's object and expects it to fail.
- Random, non-sequential IDs are worth adding as defence in depth, but they aren't the fix.
Retest, 30 Oct 2026
The request from step 3 now returns 404. We repeated the test against the invoices, payments, users and documents endpoints with the same result. Status: fixed, verified.
GET /api/v2/invoices/48214 HTTP/2
Authorization: Bearer eyJhbGciOi…[redacted] ← same user, tenant A
HTTP/2 404 Not Found4Attestation letter (excerpt)
Between 7 and 18 September 2026, Limotta IT performed a web application and API penetration test of Example Co.'s customer portal (portal.example.com), API (api.example.com/v2) and AI support assistant. Testing followed the OWASP Web Security Testing Guide and the OWASP Top 10 for LLM Applications, and was performed from the perspective of an anonymous visitor and of authenticated users at two privilege levels in two customer accounts.
Testing identified one high, three medium and one informational finding. At retest on 30 October 2026, the high finding and two medium findings were confirmed remediated. The remaining medium finding has a documented remediation plan and an interim control in place.
The letter carries no vulnerability details, so it's safe to share with customers, auditors and insurers.
5What the full report contains
- Executive summary
- Scope, rules of engagement and testing window
- Methodology and coverage, including what wasn't tested and why
- Findings summary
- Each finding: description, steps to reproduce, evidence, impact, severity and remediation
- Retest results
- Appendix: accounts used, test data created and removed, tools
Fictional example. Example Co. and every detail in this report are invented.