What our assessment report looks like
A redacted, illustrative excerpt of the deliverable you receive at the end of an engagement. Every finding below is synthetic and relates to a fictional target. No real client data appears here.
- Engagement
- Web application & API penetration test
- Target
- Acme Cloud Platform (illustrative)
- Assessment window
- A two-week engagement (relative, no fixed dates)
- Classification
- Confidential (illustrative sample)
- Report version
- Sample v1.0
Executive summary
Vexaro assessed the externally reachable web application and supporting API of the Acme Cloud Platform over a two-week, grey-box engagement. Testing followed the OWASP Web Security Testing Guide and was verified manually beyond automated tooling.
Overall risk posture: elevated. The application enforces authentication and transport security correctly, but authorisation is applied inconsistently at the object level. Two findings can each be exploited on their own: an insecure direct object reference on the billing API and a server-side request forgery in the document-import feature. Chained together, they would let an authenticated low-privilege user read other tenants' invoices and reach internal cloud metadata. Fixing the access-control layer and restricting outbound fetches removes most of the remaining risk.
8 findings, scored with CVSS 3.1
Scope & methodology
The engagement covered the production web application, its REST API, the cloud account hosting them and the externally reachable network perimeter. Testing was time-boxed and conducted from an authenticated low-privilege account plus an unauthenticated baseline.
- Web application: app.acme.example (authenticated and anonymous flows)
- REST API: api.acme.example (v1 and v2 endpoints)
- Cloud account: object storage, compute metadata and IAM review
- External network perimeter: exposed services and TLS configuration
- Denial-of-service and volumetric load testing
- Social engineering and physical access
- Third-party SaaS not operated by the target
Methodology & references
Testing was manual-first and mapped to recognised industry frameworks so findings are reproducible and the coverage is auditable.
Web Security Testing Guide: the test cases behind application coverage.
Application Security Verification Standard: the control-level verification baseline.
Adversary tactics and techniques used to frame realistic attack paths.
Penetration Testing Execution Standard: the overall engagement process.
Each finding is scored with CVSS 3.1 and translated into a business-risk rating. Severity reflects exploitability and impact in the context of this environment, not the base score alone.
Findings
Eight representative findings from the engagement, ordered by severity. The first is expanded in full below to show the level of detail in every report entry.
| ID | Finding | Severity | CVSS | Status |
|---|---|---|---|---|
| ACME-2024-001 | IDOR on billing API exposes other tenants' invoices | High | 8.1 | Open |
| ACME-2024-002 | Server-side request forgery in document import | High | 7.7 | Open |
| ACME-2024-003 | Broken function-level authorisation on admin endpoints | Medium | 6.5 | Fixed and retested |
| ACME-2024-004 | Stored XSS in the support-ticket subject field | Medium | 5.4 | Open |
| ACME-2024-005 | Weak password policy permits trivial credentials | Low | 3.7 | Open |
| ACME-2024-006 | Verbose API errors leak stack traces and library versions | Low | 3.1 | Risk accepted |
| ACME-2024-007 | Missing security headers (HSTS, CSP, frame-ancestors) | Info | 2.4 | Fixed and retested |
| ACME-2024-008 | Session cookie issued without the Secure attribute | Info | 2.0 | Open |
Sample finding in full
One finding, expanded to the depth every entry gets in a real report: description, impact, evidence, reproduction, remediation and references.
IDOR on billing API exposes other tenants' invoices
- Finding ID
- ACME-2024-001
- CVSS 3.1
- 8.1 (High)
- Vector
- AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
- CWE
- CWE-639: Authorization Bypass Through User-Controlled Key
Description
The billing API returns invoice documents by a sequential numeric identifier supplied in the request path. The endpoint authenticates the caller but never checks that the requested invoice belongs to the caller's tenant. Any authenticated user can therefore enumerate the identifier and retrieve invoices belonging to other organisations, including billing contact names, amounts and line items.
Impact
This is a cross-tenant confidentiality breach. An authenticated low-privilege user from any tenant can read the financial records of every other tenant by incrementing a single integer. The exposed data includes commercial terms and personal contact details, creating both a privacy and a competitive-intelligence exposure. Because identifiers are sequential, the entire invoice corpus can be scraped programmatically in minutes.
Evidence
Illustrative request and response. The host, token and values are synthetic and redacted.
GET /v2/invoices/10428 HTTP/1.1
Host: api.acme.example
Authorization: Bearer eyJhbGciOi...REDACTED.SAMPLE.TOKEN
Accept: application/jsonHTTP/1.1 200 OK
Content-Type: application/json
{
"invoice_id": 10428,
"tenant_id": 3304,
"bill_to": "[REDACTED], Northwind Trading",
"amount_due": "14,920.00 USD",
"status": "unpaid"
}'Steps to reproduce
- 1Authenticate to the application with any valid low-privilege account and capture the bearer token.
- 2Request your own invoice, e.g. GET /v2/invoices/10427, and confirm a 200 response.
- 3Decrement or increment the numeric identifier (.../10428, .../10429) and replay the request with your own token.
- 4Observe that invoices belonging to other tenant_id values are returned, confirming the missing object-level authorisation check.
Remediation
Enforce object-level authorisation on every request: resolve the invoice, then confirm its tenant_id matches the authenticated principal's tenant before returning any data, otherwise respond 404. Apply the check centrally (for example in a policy layer or query scope) rather than per-handler, and add a regression test that asserts a cross-tenant request is rejected. Replacing sequential identifiers with unguessable ones reduces enumeration but is defence-in-depth, not a substitute for the authorisation check.