WOMMWorks On My Machine · onmymachineitworks.comSample · fictional data
Test report · Security / penetration test
Acme Ledger web app and API
1 High (fixed) · 4 Medium open
Authorisation
Signed test agreement, 6 Oct 2026; scope and window agreed with the CTO
Scope
app.staging.acme-ledger.example, customer portal, Public API 2.4 (all endpoints)
Approach
Grey box: test accounts for 5 roles in 2 test tenants, API documentation, no source code
Method
OWASP WSTG 4.2, ASVS 4.0.3 Level 2 (selected), OWASP API Security Top 10 2023
Test window
12 Oct 2026 – 16 Oct 2026 · retest 23 Oct 2026
Tester
Tomasz Kordiak
Summary
The application has a solid baseline: 82 of 91 applicable checks passed, TLS, cookies, password storage and most authorization are done right, and there are no critical issues. The most serious finding was an authorization gap on two invoice endpoints that let one customer read another customer's invoices (SEC-01). It was fixed before the 4.2 release and the fix was verified.
Four Medium findings remain open with fixes planned: stored XSS in the portal, no login rate limiting, long-lived sessions and CSV formula injection.
Release recommendation
No open finding blocks release 4.2. Fix SEC-02, SEC-03 and SEC-05 in 4.2.1 (within 2 weeks), SEC-04 in 4.3.
Findings by severity
0Critical
1High
4Medium
4Low
94Checks performed
2Fixed and retested
ID
Finding
Severity
CVSS
Status
SEC-01
Invoice PDF and resend endpoints don't check the tenant (BOLA)
High
7.1
Fixed · retested
SEC-02
Stored cross-site scripting in invoice notes (customer portal)
Medium
5.4
Open
SEC-03
No rate limiting or lockout on login
Medium
5.3
Open
SEC-04
Sessions stay valid after logout and password change
Medium
4.8
Open
SEC-05
CSV export allows formula injection
Medium
4.6
Open
SEC-06
Missing Content-Security-Policy and Permissions-Policy headers
Low
–
Open
SEC-07
Password reset reveals whether an email has an account
Low
3.7
Open
SEC-08
Stack traces in 500 responses
Low
3.7
Fixed · retested
SEC-09
Invite links don't expire after use
Low
3.1
Open
Severity
Open
Fixed
Total
Critical
–
–
0
High
–
1
1
Medium
4
–
4
Low
3
1
4
Defects Jira tickets for each finding
Key
Summary
Severity
Status
Component
Found by
Owner
Created
ACME-531
[SEC-01] Invoice PDF and resend endpoints don't check the tenant (BOLA)
High
Fixed
GET /invoices/{id}/pdf
–
Backend
13 Oct
ACME-532
[SEC-02] Stored cross-site scripting in invoice notes (customer portal)
Medium
Open
Invoice **Notes** field rendered in the customer portal
–
Frontend
14 Oct
ACME-533
[SEC-03] No rate limiting or lockout on login
Medium
Open
Web app
–
Backend
15 Oct
ACME-534
[SEC-04] Sessions stay valid after logout and password change
Medium
Open
Web app
–
Frontend
16 Oct
ACME-535
[SEC-05] CSV export allows formula injection
Medium
Open
Web app
–
Backend
16 Oct
ACME-536
[SEC-06] Missing Content-Security-Policy and Permissions-Policy headers
Low
Open
Web app
–
Frontend
16 Oct
ACME-537
[SEC-07] Password reset reveals whether an email has an account
Low
Open
Web app
–
Backend
16 Oct
ACME-538
[SEC-08] Stack traces in 500 responses
Low
Fixed
Web app
–
Frontend
16 Oct
ACME-539
[SEC-09] Invite links don't expire after use
Low
Open
Web app
–
Backend
16 Oct
Observations not vulnerabilities
ID
Area
Observation
Impact
Suggested action
OBS-01
Positive
Tenant isolation holds on all other API operations, including filters, pagination cursors and exports.
Positive
Keep the negative tests added in rc3 for every {id} route.
OBS-02
Dependencies
npm audit and pip-audit: no known critical or high vulnerabilities; 3 moderate in dev dependencies.
Low
Update in the next dependency cycle.
OBS-03
Logging
Failed logins and permission denials are logged, but nobody gets alerted.
Detection
Alert on spikes (ties in with SEC-03).
OBS-04
MFA
MFA is enforced for admins but optional for Finance, who can issue refunds.
Medium
Require MFA for any role that moves money.
OBS-05
Secrets
Webhook secrets are shown again in the UI after creation.
Low
Show once, then mask (like API secrets).
OBS-06
Process
No security.txt or vulnerability disclosure contact.
Low
Publish /.well-known/security.txt.
Findings CVSS 3.1 base scores
HighSEC-01 Invoice PDF and resend endpoints don't check the tenant (BOLA)
GET /invoices/{id} correctly returns 404 for another tenant's invoice, but the PDF and resend endpoints only check that the caller is authenticated. Invoice IDs appear in links in emailed invoices, so they are not secret.
Reproduction
As a tenant A user, take an invoice ID of tenant B (test tenants).
GET /invoices/{id}/pdf with tenant A's token → 200 and tenant B's PDF.
POST /invoices/{id}/send → tenant B's customer receives the invoice again.
Impact
Any customer can read other customers' invoices and trigger emails on their behalf.
Remediation
Shared authorization layer for every {id} route; negative tests per endpoint.
Status
Fixed in 4.2.0-rc3 · retested 23 Oct 2026 · both endpoints return 404.
MediumSEC-02 Stored cross-site scripting in invoice notes (customer portal)
CVSS 3.1
5.4 · AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
Category
OWASP A03:2021 Injection · CWE-79
Affected
Invoice Notes field rendered in the customer portal
Description
HTML in Notes is escaped in the back office but rendered as HTML in the portal. A harmless test payload executed in the customer's browser.
Remediation
Escape on output in the portal template; add a Content-Security-Policy (SEC-06).
Both endpoints 404 for other tenants; 15 negative tests added
Fixed
SEC-08
Generic error body in production
Malformed JSON → 400 with message only
Fixed
SEC-02 – SEC-07, SEC-09
Planned for 4.2.1 / 4.3
Retest scheduled after the fixes
Pending
Scope and rules of engagement
In scope
Web app, customer portal, Public API 2.4, SSO login, file upload, webhooks
Out of scope
Production, denial-of-service, social engineering, payment provider, office network, physical
Accounts
2 test tenants × 5 roles, 1 SSO test user, 3 API clients
Limits
Max 10 requests/s for automated scans; brute-force tests limited to own test accounts
Data
Synthetic data only; no production data accessed
Contacts
CTO (escalation), on-call engineer (availability during the window)
Critical findings
Reported the same day by phone and email (SEC-01 reported 13 Oct, 15:20)
Timeline
Date
Activity
6 Oct
Scope, authorisation and test accounts agreed
12 Oct
Reconnaissance, configuration, authentication
13 Oct
Authorization testing; SEC-01 found and reported the same day
14 Oct
Input validation, business logic
15 Oct
Files, API-specific, cryptography, client side
16 Oct
Verification of findings, draft report
23 Oct
Retest of fixes in rc3, final report
Method and limits
Manual testing supported by Burp Suite and OWASP ZAP; automated scans were rate-limited to avoid load on staging. A penetration test is a point-in-time check of the agreed scope and does not prove the absence of vulnerabilities.
Evidence (requests, responses, screenshots) is stored encrypted, shared only with the named contacts and deleted 90 days after the final retest.
Sample report. Acme Ledger, its data, people and defects are fictional. Your report shows your product, your tickets and your tools. More sample reports · Book a 15-minute call