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
IDFindingSeverityCVSSStatus
SEC-01Invoice PDF and resend endpoints don't check the tenant (BOLA)High7.1Fixed · retested
SEC-02Stored cross-site scripting in invoice notes (customer portal)Medium5.4Open
SEC-03No rate limiting or lockout on loginMedium5.3Open
SEC-04Sessions stay valid after logout and password changeMedium4.8Open
SEC-05CSV export allows formula injectionMedium4.6Open
SEC-06Missing Content-Security-Policy and Permissions-Policy headersLow–Open
SEC-07Password reset reveals whether an email has an accountLow3.7Open
SEC-08Stack traces in 500 responsesLow3.7Fixed · retested
SEC-09Invite links don't expire after useLow3.1Open
SeverityOpenFixedTotal
Critical––0
High–11
Medium4–4
Low314

Defects Jira tickets for each finding

KeySummarySeverityStatusComponentFound byOwnerCreated
ACME-531[SEC-01] Invoice PDF and resend endpoints don't check the tenant (BOLA)HighFixedGET /invoices/{id}/pdf–Backend13 Oct
ACME-532[SEC-02] Stored cross-site scripting in invoice notes (customer portal)MediumOpenInvoice **Notes** field rendered in the customer portal–Frontend14 Oct
ACME-533[SEC-03] No rate limiting or lockout on loginMediumOpenWeb app–Backend15 Oct
ACME-534[SEC-04] Sessions stay valid after logout and password changeMediumOpenWeb app–Frontend16 Oct
ACME-535[SEC-05] CSV export allows formula injectionMediumOpenWeb app–Backend16 Oct
ACME-536[SEC-06] Missing Content-Security-Policy and Permissions-Policy headersLowOpenWeb app–Frontend16 Oct
ACME-537[SEC-07] Password reset reveals whether an email has an accountLowOpenWeb app–Backend16 Oct
ACME-538[SEC-08] Stack traces in 500 responsesLowFixedWeb app–Frontend16 Oct
ACME-539[SEC-09] Invite links don't expire after useLowOpenWeb app–Backend16 Oct

Observations not vulnerabilities

IDAreaObservationImpactSuggested action
OBS-01PositiveTenant isolation holds on all other API operations, including filters, pagination cursors and exports.PositiveKeep the negative tests added in rc3 for every {id} route.
OBS-02Dependenciesnpm audit and pip-audit: no known critical or high vulnerabilities; 3 moderate in dev dependencies.LowUpdate in the next dependency cycle.
OBS-03LoggingFailed logins and permission denials are logged, but nobody gets alerted.DetectionAlert on spikes (ties in with SEC-03).
OBS-04MFAMFA is enforced for admins but optional for Finance, who can issue refunds.MediumRequire MFA for any role that moves money.
OBS-05SecretsWebhook secrets are shown again in the UI after creation.LowShow once, then mask (like API secrets).
OBS-06ProcessNo security.txt or vulnerability disclosure contact.LowPublish /.well-known/security.txt.

Findings CVSS 3.1 base scores

High SEC-01 Invoice PDF and resend endpoints don't check the tenant (BOLA)

CVSS 3.1
7.1 · AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N
Category
OWASP API1:2023 Broken Object Level Authorization · CWE-639
Affected
GET /invoices/{id}/pdf, POST /invoices/{id}/send
Description
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.

Medium SEC-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).
Status
Open · planned for 4.2.1

Medium SEC-03 No rate limiting or lockout on login

CVSS 3.1
5.3 · AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Category
OWASP API4:2023 Unrestricted Resource Consumption · CWE-307
Description
200 failed logins for one test account in 60 seconds were all processed (test limited to our own account).
Remediation
Rate limit per account and IP, progressive delay after 5 failures, alerting on spikes.
Status
Open · planned for 4.2.1

Medium SEC-04 Sessions stay valid after logout and password change

CVSS 3.1
4.8 · AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
Category
OWASP A07:2021 Identification and Authentication Failures · CWE-613
Description
Access tokens are JWTs valid for 24 h without server-side revocation.
Remediation
15-minute access tokens with refresh tokens; revoke on password change and logout.
Status
Open · design agreed, 4.3

Medium SEC-05 CSV export allows formula injection

CVSS 3.1
4.6 · AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N
Category
CWE-1236 Improper Neutralization of Formula Elements in a CSV File
Description
A customer name starting with = is exported unchanged; opening the CSV in a spreadsheet evaluates it as a formula.
Remediation
Prefix cells starting with = + - @ with an apostrophe in all CSV exports.
Status
Open · 4.2.1

Low SEC-06 Missing Content-Security-Policy and Permissions-Policy headers

Description
Web app and portal send HSTS and X-Content-Type-Options, but no CSP or Permissions-Policy.
Remediation
CSP in report-only mode first, then enforce. Would have limited SEC-02.

Low SEC-07 Password reset reveals whether an email has an account

CVSS 3.1
3.7 · AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N
Description
POST /auth/reset returns 404 User not found for unknown emails.
Remediation
Always return 200 with the same message.

Low SEC-08 Stack traces in 500 responses

CVSS 3.1
3.7 · AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N
Description
Malformed JSON in POST /customers returned a stack trace with library versions.
Status
Fixed in 4.2.0-rc3 · retested

Low SEC-09 Invite links don't expire after use

CVSS 3.1
3.1 · AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
Description
An accepted invite link still shows the sign-up form (account creation is blocked, but the email is shown).
Remediation
Invalidate the token on acceptance.

Authorization matrix

EndpointAdminFinanceAccountantSalesViewerOther tenantNo token
GET /invoices✓✓✓✓✓✕✕
GET /invoices/{id}✓✓✓✓✓✕✕
GET /invoices/{id}/pdf✓✓✓✓✓!✕
POST /invoices✓✓✕✓✕✕✕
POST /invoices/{id}/send✓✓✕✓✕!✕
DELETE /invoices/{id}✓✕✕✕✕✕✕
POST /payments✓✓✕✕✕✕✕
POST /payments/{id}/refund✓✓✕✕✕✕✕
GET /customers✓✓✓✓✓✕✕
GET /customers/export.csv✓✓✕✓✕✕✕
GET /reports/revenue✓✓✓✕✓✕✕
POST /users/invite✓✕✕✕✕✕✕
PATCH /settings/company✓✕✕✕✕✕✕
POST /webhooks✓✕✕✕✕✕✕
GET /audit-events✓✕✓✕✕✕✕

15 endpoints × 7 actors = 105 authorization checks. ✓ allowed as designed, ✕ denied as designed, ! finding (SEC-01, fixed).

Test checklist 94 checks · 82 pass · 9 fail · 3 n/a

CategoryChecksPassFailN/A
Information gathering77––
Configuration and deployment1082–
Authentication12831
Authorization1091–
Session management871–
Input validation141121
Business logic99––
Files and uploads66––
API specific88––
Cryptography55––
Client side54–1

Information gathering

IDCheckResultFinding
INF-01Fingerprint web server and frameworksPass–
INF-02Review robots.txt and sitemap for hidden pathsPass–
INF-03Identify entry points (UI and API)Pass–
INF-04Map application architecturePass–
INF-05Review client-side code for secretsPass–
INF-06Check for exposed .git or backupsPass–
INF-07Enumerate subdomains in scopePass–

Configuration and deployment

IDCheckResultFinding
CON-01TLS versions and ciphersPass–
CON-02HSTS present and preloadedPass–
CON-03Security headersFailSEC-06
CON-04HTTP methods allowedPass–
CON-05Admin interfaces exposedPass–
CON-06Default credentialsPass–
CON-07Directory listingPass–
CON-08Error handling leaks detailsFailSEC-08
CON-09CORS policyPass–
CON-10Cloud storage permissionsPass–

Authentication

IDCheckResultFinding
AUT-01Credentials over encrypted channelPass–
AUT-02Default and weak passwordsPass–
AUT-03Lockout mechanismFailSEC-03
AUT-04Bypass authentication schemaPass–
AUT-05Remember-meN/A–
AUT-06Browser cache of sensitive pagesPass–
AUT-07Password policyPass–
AUT-08Password resetFailSEC-07
AUT-09MFA enforcement for adminsPass–
AUT-10SSO (SAML) assertion validationPass–
AUT-11Account enumeration on loginPass–
AUT-12Invite flowFailSEC-09

Authorization

IDCheckResultFinding
AUT-01Path traversal / file includePass–
AUT-02Bypass authorization schemaPass–
AUT-03Privilege escalation between rolesPass–
AUT-04Object-level authorization (BOLA)FailSEC-01
AUT-05Function-level authorizationPass–
AUT-06Tenant isolation in filtersPass–
AUT-07Tenant isolation in pagination cursorsPass–
AUT-08Tenant isolation in exportsPass–
AUT-09Tenant isolation in webhooksPass–
AUT-10Mass assignment of tenantId/rolePass–

Session management

IDCheckResultFinding
SES-01Session token randomnessPass–
SES-02Cookie attributesPass–
SES-03Session fixationPass–
SES-04CSRF protectionPass–
SES-05Logout invalidates sessionFailSEC-04
SES-06Session timeoutPass–
SES-07Token in URLPass–
SES-08Concurrent sessionsPass–

Input validation

IDCheckResultFinding
INP-01Reflected XSSPass–
INP-02Stored XSSFailSEC-02
INP-03DOM XSSPass–
INP-04SQL injectionPass–
INP-05NoSQL injectionN/A–
INP-06Command injectionPass–
INP-07Server-side template injectionPass–
INP-08SSRF via webhook URLPass–
INP-09SSRF via file import URLPass–
INP-10XML external entities (UBL import)Pass–
INP-11HTTP parameter pollutionPass–
INP-12CSV formula injectionFailSEC-05
INP-13Header injection in emailsPass–
INP-14Open redirectPass–

Business logic

IDCheckResultFinding
BUS-01Negative amounts and quantitiesPass–
BUS-02Overpayment and refunds above paidPass–
BUS-03Race condition: double paymentPass–
BUS-04Race condition: double refundPass–
BUS-05Invoice number manipulationPass–
BUS-06Workflow bypass (send cancelled invoice)Pass–
BUS-07Discount over 100%Pass–
BUS-08Currency switch after paymentPass–
BUS-09Abuse of free trial limitsPass–

Files and uploads

IDCheckResultFinding
FIL-01File type validation server-sidePass–
FIL-02File size limitPass–
FIL-03Malicious file content (polyglot)Pass–
FIL-04Uploaded files served from separate domainPass–
FIL-05Download links expirePass–
FIL-06Path in file namePass–

API specific

IDCheckResultFinding
API-01API9 inventory: undocumented endpointsPass–
API-02API3 excessive data exposurePass–
API-03API4 rate limiting on APIPass–
API-04API6 sensitive business flowsPass–
API-05API8 misconfigurationPass–
API-06API10 unsafe consumption of webhooksPass–
API-07Versioning: v1 endpoints still reachablePass–
API-08Error messages consistentPass–

Cryptography

IDCheckResultFinding
CRY-01Password storage algorithmPass–
CRY-02Secrets in responsesPass–
CRY-03Webhook signature algorithmPass–
CRY-04JWT algorithm and key handlingPass–
CRY-05Random number generation for tokensPass–

Client side

IDCheckResultFinding
CLI-01Clickjacking protectionPass–
CLI-02Sensitive data in local storagePass–
CLI-03Third-party scriptsPass–
CLI-04Subresource integrity on CDN scriptsN/A–
CLI-05postMessage handlingPass–

Retest

FindingFixRetest 23 OctResult
SEC-01Tenant check moved to a shared middlewareBoth endpoints 404 for other tenants; 15 negative tests addedFixed
SEC-08Generic error body in productionMalformed JSON → 400 with message onlyFixed
SEC-02 – SEC-07, SEC-09Planned for 4.2.1 / 4.3Retest scheduled after the fixesPending

Scope and rules of engagement

In scopeWeb app, customer portal, Public API 2.4, SSO login, file upload, webhooks
Out of scopeProduction, denial-of-service, social engineering, payment provider, office network, physical
Accounts2 test tenants × 5 roles, 1 SSO test user, 3 API clients
LimitsMax 10 requests/s for automated scans; brute-force tests limited to own test accounts
DataSynthetic data only; no production data accessed
ContactsCTO (escalation), on-call engineer (availability during the window)
Critical findingsReported the same day by phone and email (SEC-01 reported 13 Oct, 15:20)

Timeline

DateActivity
6 OctScope, authorisation and test accounts agreed
12 OctReconnaissance, configuration, authentication
13 OctAuthorization testing; SEC-01 found and reported the same day
14 OctInput validation, business logic
15 OctFiles, API-specific, cryptography, client side
16 OctVerification of findings, draft report
23 OctRetest 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