Grey-box assessment of the Acme web application, REST API, and Supabase backend, validated by hand by senior security engineers.
This report contains confidential security findings about Acme systems. Distribution is restricted to the named recipients below. Handle in accordance with your internal data-classification policy.
| Title | Web Application & API Penetration Test - Acme, Inc. |
| Classification | Confidential |
| Engagement reference | VS-2026-0617 |
| Assessment type | Grey-box penetration test (unauthenticated + authenticated) |
| Testing window | 9 June 2026 – 11 June 2026 |
| Report date | 13 June 2026 |
| Retest date | 20 June 2026 |
| Lead engineer | R. Mehta - OSCP, OSWE |
| Engineer | D. Alvarez - OSEP, eWPTX |
| QA / reviewer | S. Okafor - OSCP, CRTP |
| Delivered with | BrokenIntent (testing partner) |
| Primary contact | J. Whitfield - VP Engineering |
| Technical contact | P. Nguyen - Security Lead |
| Authorization | Signed SOW & scope - 06 Jun 2026 |
| Distribution | Restricted - named recipients only |
| Version | Date | Author | Notes |
|---|---|---|---|
| v0.1 | 11 Jun 2026 | R. Mehta | Internal draft - findings complete |
| v1.0 | 13 Jun 2026 | S. Okafor | QA review, delivered to client |
| v1.1 | 20 Jun 2026 | R. Mehta | Retest results & attestation appended |
This document describes live security weaknesses and must not be redistributed, in whole or in part, without written authorization. If you received this in error, delete it and notify hello@vibesecurely.com.
| 1 · Executive summary | 4 |
| 2 · Risk posture & findings overview | 5 |
| 3 · Scope, approach & methodology | 6 |
| 4 · Risk-rating methodology (CVSS v3.1) | 7 |
| 5 · Findings summary table | 8 |
| 6 · Detailed findings | 9 |
| 6.1 · VS-01 - Read/modify any user's orders (IDOR) | 9 |
| 6.2 · VS-02 - Account takeover via email change | 10 |
| 6.3 · VS-03 - Unrestricted file upload → RCE | 11 |
| 6.4 · VS-04 - Blind SQL injection in search | 12 |
| 6.5 · VS-05 - Supabase RLS disabled (public read) | 13 |
| 6.6 · Remaining findings (VS-06 – VS-14) | 14 |
| 7 · Strategic recommendations | 15 |
| 8 · Retest results & attestation | 16 |
| 9 · Appendix - tooling & glossary | 17 |
Section 1 is written for leadership - no tooling, just business risk and the bottom line. Sections 5–6 are written for engineers - every finding has reproduction steps, proof-of-concept, and a concrete fix. Section 8 is for your buyer's security team - retest verification and a signed attestation. Hand the whole document over as-is.
Acme engaged VibeSecurely to perform a grey-box penetration test of its web application, REST API, and Supabase backend ahead of an enterprise customer's security review. Testing ran from 9–11 June 2026 against a staging environment that mirrors production. The objective: determine whether an external attacker, or a low-privileged authenticated user, could read other customers' data, take over accounts, or execute code.
The product is well-built in places, but shipped three critical and five high-severity flaws that - chained - allow full compromise of customer data. The most serious (VS-01) lets any authenticated user read and modify other users' orders by changing one numeric ID. Combined with the unauthenticated email change (VS-02) and the file-upload flaw (VS-03), an attacker could take over accounts and execute code in the application's origin. We confirmed each with proof-of-concept and stopped short of bulk extraction by agreement.
An attacker with a standard account could reach other customers' data within hours - no special tooling required. Every fix is well-understood and scoped to under two engineering weeks. Remediate VS-01, VS-02, and VS-03 before the next release; none require an architectural change.
Fix the access-control findings (VS-01, VS-05, VS-07) as a single workstream - they share a root cause and a shared fix: centralized, server-side authorization. Request a retest as remediation lands; VibeSecurely re-validates each fix and updates this report's attestation.
| Category | Findings | Highest severity | Residual after fix |
|---|---|---|---|
| A01 - Broken Access Control | 4 | Critical | Low |
| A07 - Identification & Auth Failures | 4 | Critical | Low |
| A03 - Injection | 1 | High | Low |
| A04 - Insecure Design | 2 | Critical | Low |
| A05 - Security Misconfiguration | 3 | Medium | Low |
1. Foothold. Register a standard user account (self-service, no verification).
2. Horizontal access. Use the IDOR on /api/orders/{id} (VS-01) to read other users' orders and PII.
3. Takeover. Change a victim's email with no re-auth (VS-02), then password-reset into their account.
4. Code execution. Upload an HTML/JS file (VS-03) served from the application origin to run code and harvest sessions at scale.
Each step was demonstrated in an isolated staging environment with explicit authorization. No production data was accessed; no records were exfiltrated in bulk.
| Asset | Type | Environment | Access level |
|---|---|---|---|
| app.acme.io | Web application | Staging | Unauthenticated + authenticated |
| app.acme.io/api | REST API (28 endpoints) | Staging | Unauthenticated + authenticated |
| Supabase project (Postgres + Storage) | Backend-as-a-service | Staging | Anon key + config review |
VibeSecurely combines broad automated attack-surface mapping with hands-on testing by senior security engineers, who manually validate, exploit, and rule out false positives. Every finding in this report was reproduced by hand. Our process aligns to:
Automated scanners reliably miss business-logic and access-control flaws - exactly the issues that did the most damage here (VS-01, VS-02, VS-07). Broad tooling provides coverage across the API quickly; experienced testers provide the judgment to chain findings into real attacks and discard noise. Three of the top findings would not have been caught by tooling alone.
Each finding is scored with CVSS v3.1 for base technical severity, then adjusted for Acme's business context (data sensitivity, exposure, exploit complexity) to produce the final risk rating used throughout this report.
| Severity | CVSS | Meaning | Expected response |
|---|---|---|---|
| Critical | 9.0–10.0 | Direct path to mass data loss or full compromise; trivial to exploit. | Fix immediately. |
| High | 7.0–8.9 | Serious impact to confidentiality, integrity, or availability. | Fix in current sprint. |
| Medium | 4.0–6.9 | Meaningful risk, usually requiring a precondition or chaining. | Next release cycle. |
| Low | 0.1–3.9 | Limited impact; hardening / defense-in-depth. | As capacity allows. |
| AV - Attack Vector | N = Network (exploitable remotely) |
| AC - Attack Complexity | L = Low (no special conditions) |
| PR - Privileges Required | L = Low (any authenticated user) |
| UI - User Interaction | N = None (no victim action needed) |
| S - Scope | U = Unchanged |
| C / I / A - Impact | Confidentiality / Integrity / Availability - None, Low, or High |
All 14 findings, ordered by severity. Retest status reflects the 20 June 2026 re-validation.
| ID | Finding | OWASP | Severity | CVSS | Retest |
|---|---|---|---|---|---|
| VS-01 | Read/modify any user's orders (IDOR) | A01 | Critical | 9.1 | Fixed ✓ |
| VS-02 | Account takeover via unauthenticated email change | A07 | Critical | 9.3 | Fixed ✓ |
| VS-03 | Unrestricted file upload → remote code execution | A04 | Critical | 9.6 | Fixed ✓ |
| VS-04 | Blind SQL injection in product search | A03 | High | 8.2 | Fixed ✓ |
| VS-05 | Supabase RLS disabled - public table read | A01 | High | 8.1 | Fixed ✓ |
| VS-06 | No rate limiting on login (credential stuffing) | A07 | High | 7.5 | Fixed ✓ |
| VS-07 | Price tampering via client-controlled cart | A04 | High | 7.4 | Fixed ✓ |
| VS-08 | Broken object-level auth on user profiles (BOLA) | A01 | High | 7.1 | Fixed ✓ |
| VS-09 | Wildcard CORS with credentials | A05 | Medium | 5.3 | Fixed ✓ |
| VS-10 | Stripe key exposed in client bundle | A05 | Medium | 5.1 | Fixed ✓ |
| VS-11 | Session cookie missing Secure / SameSite | A05 | Medium | 4.6 | Fixed ✓ |
| VS-12 | Verbose API error messages leak stack traces | A05 | Low | 3.4 | Fixed ✓ |
| VS-13 | Username enumeration on signup | A07 | Low | 3.1 | Accepted |
| VS-14 | Weak password policy (8 chars, no breach check) | A07 | Low | 2.8 | Accepted |
GET /api/orders/{id} returns an order based solely on the numeric id. The server checks that the caller is authenticated, but never that the order belongs to the caller. Any authenticated user can read - and via PUT, modify - every other user's order by incrementing the ID. IDs are sequential.
app.acme.io/api/orders/{id} (GET, PUT, DELETE)
# Authenticated as user 1042, request order 1041 $ curl -s https://app.acme.io/api/orders/1041 \ -H "Authorization: Bearer eyJhbGciOi...<user-1042-token>" { "id": 1041, "email": "victim@acme.io", "total": "$240.00", "address": "21 Pine St, Austin TX", "card_last4": "4242" } # 200 OK - not the caller's order
Full read/write access to every customer's orders and PII. A single script enumerates the entire order table in minutes - a reportable data breach and an instant fail of any customer's security review.
Enforce object-level authorization on every record fetch: verify order.user_id === session.user_id (or use Postgres row-level security) before returning data. Never treat a client-supplied ID as proof of ownership. Apply the same check to PUT/DELETE.
Fixed ✓ - ownership now enforced in middleware; cross-user access returns 403. Re-validated 20 Jun 2026.
POST /api/account/email updates the account email with no re-authentication and no confirmation to the old address. With a hijacked session - or via CSRF - an attacker changes a victim's email, then triggers a password reset to fully take over the account.
# Change the logged-in user's email - no password required $ curl -s -X POST https://app.acme.io/api/account/email \ -H "Authorization: Bearer <session>" \ -d '{"email":"attacker@evil.com"}' { "ok": true } # no confirmation to the old address
Any session compromise (XSS, a borrowed device, a leaked token) escalates to permanent account takeover, including any payment methods and data on the account.
Require the current password (or a fresh re-auth) to change email; send a confirmation link to the old address; invalidate active sessions on change. Add CSRF protection to all state-changing routes.
Fixed ✓ - re-auth + old-address confirmation now required. Re-validated 20 Jun 2026.
The avatar upload (POST /api/upload) accepts any file type and stores it under the web root with its original name. Uploading an HTML/JS file and requesting it back executes attacker code in the application's own origin - stealing sessions and pivoting.
# Upload an HTML payload as an "avatar" $ curl -s -X POST https://app.acme.io/api/upload \ -H "Authorization: Bearer <session>" \ -F "file=@xss.html;type=text/html" { "url": "/uploads/xss.html" } # → served executable at https://app.acme.io/uploads/xss.html
Same-origin code execution: an attacker can steal any visiting user's session, deface the application, or stage further attacks. Combined with VS-02, this is full multi-account compromise.
Allowlist MIME types and extensions, generate random server-side filenames, store uploads off-origin (e.g. Supabase Storage / object storage) and serve with Content-Disposition: attachment. Never serve user uploads as executable content from the application origin.
Fixed ✓ - uploads moved to object storage with type allowlist + random names. Re-validated 20 Jun 2026.
The q parameter on GET /api/search is concatenated directly into a SQL query. A time-based payload confirms injection - opening the door to a full database read, including users and session tokens.
# Time-based blind confirmation - response delays ~5s $ curl -s -G https://app.acme.io/api/search \ --data-urlencode "q=' OR SLEEP(5)-- -" # 200 OK after ~5.0s → injectable
An unauthenticated attacker can read arbitrary database contents over time, including credentials and PII - a direct path to a full data breach.
Use parameterized queries / prepared statements (or the ORM query builder). Never build SQL by string concatenation. Add a WAF rule as defense-in-depth.
Fixed ✓ - query rewritten with bound parameters. Re-validated 20 Jun 2026.
The public.profiles table was created with Row-Level Security disabled. Because the Supabase anon key is - by design - shipped to the browser, anyone can query the table directly and read every user's row, bypassing the application entirely.
# Query Supabase REST directly with the public anon key $ curl -s "https://<proj>.supabase.co/rest/v1/profiles?select=*" \ -H "apikey: <public-anon-key>" [ { "id": 1, "email": "a@acme.io", "phone": "+1…" }, … ] # every row returned - RLS off
The entire user table is readable by anyone who views the site's JavaScript - a full disclosure of user records. Misconfigured row-level security is among the most common and most damaging issues in Supabase-backed applications.
Enable RLS on every table (alter table … enable row level security) and add explicit policies (e.g. user_id = auth.uid()). Audit all tables - RLS is off by default on new tables.
Fixed ✓ - RLS enabled with per-user policies across all tables. Re-validated 20 Jun 2026.
High, medium, and low findings in brief. Each was reproduced and retested; full reproduction steps are available on request.
Login has no throttling, lockout, or CAPTCHA - thousands of credential-stuffing attempts per minute succeed silently.
Fix: per-IP + per-account rate limiting with backoff, lockout after repeated failures, offer MFA.
Checkout trusts client-supplied price and quantity; a negative quantity or altered price yields a $0 (or credited) order.
Fix: compute totals server-side from trusted catalog data; reject non-positive quantities; never accept price from the client.
GET /api/users/{id} returns any user's full profile and PII by iterating the ID - same root cause as VS-01, second endpoint.
Fix: centralized authorization middleware enforcing ownership/role on every object access.
Access-Control-Allow-Origin: * with credentials lets any site make authenticated requests on a victim's behalf.
Fix: reflect only an allowlist of trusted origins; never pair * with credentials.
A Stripe restricted key was hardcoded in the front-end JavaScript, readable by anyone who opens DevTools.
Fix: move the key server-side; rotate the exposed key; use only publishable keys client-side.
VS-11 (session cookie flags · 4.6), VS-12 (verbose errors · 3.4), VS-13 (username enumeration · 3.1), and VS-14 (weak password policy · 2.8) are detailed in the engagement appendix. VS-13 and VS-14 were formally risk-accepted with compensating controls.
Beyond the individual fixes, three changes would prevent this whole class of issue from recurring as the application grows.
Most findings share one root cause: the application trusts client-supplied IDs and roles. Add a single authorization layer that checks ownership/role on every object access - don't patch endpoints one at a time. Enable Supabase RLS on every table as a backstop.
Prices, quantities, file types, and SQL inputs must be validated and computed on the server. Treat everything from the browser as hostile.
These patterns are easy to reintroduce as the codebase grows. A lightweight pre-release checklist (authorization, RLS, secrets, rate limits) plus a periodic penetration test keeps new code from reopening fixed holes.
On 20 June 2026, VibeSecurely re-tested all findings after Acme's remediation. All 3 critical and all 5 high findings are fixed and verified. Twelve of fourteen findings are remediated; two low-severity findings were formally risk-accepted with documented compensating controls.
VibeSecurely confirms that, as of 20 June 2026, the critical and high-severity vulnerabilities identified in this assessment of the Acme application have been remediated and re-validated. No critical or high-severity issues remain open. This attestation may be shared with prospective customers and their security teams.
Signed - R. Mehta (OSCP, OSWE), Lead Engineer, VibeSecurely · in partnership with BrokenIntent.
Manual testing with Burp Suite Professional and custom scripts; automated attack-surface mapping; Supabase/Postgres configuration review. All exploitation was performed by hand and confirmed.
| IDOR / BOLA | Accessing another user's object by changing its identifier, with no ownership check. |
| RLS | Row-Level Security - Postgres/Supabase policies that restrict which rows a query can return. |
| SQLi | SQL injection - attacker-controlled input altering a database query. |
| CVSS | Common Vulnerability Scoring System - a 0–10 technical severity score. |
| RCE | Remote Code Execution - running attacker code on the server or in the application origin. |
This is a sample report prepared for an illustrative target (app.acme.io) to demonstrate the VibeSecurely deliverable.
VibeSecurely · hello@vibesecurely.com · vibesecurely.com · powered by BrokenIntent