CyberSauce

CyberSauce / sample-report

Sample deliverable

Security Audit Report

Intentionally vulnerable fixture owned by CyberSauce

Client
Demo Application
Dates
2026-07-28 – 2026-07-29
Scope
Source repository + local configuration (no production systems)
Auditor
Michael Keenan / CyberSauce

Executive summary

Overall risk rating: Critical. Findings: 2 Critical, 2 High, 1 Medium, 1 Low.

The most urgent issue is a service-role API key shipped in the browser bundle, which would allow any visitor to bypass database access controls and modify tenant data. Combined with disabled row-level security on profiles, the application is not safe to expose to real users.

This report was produced against an intentionally vulnerable demo application owned by CyberSauce. It is a real report on a real fixture — not marketing fiction — and doubles as the accuracy test harness for the audit pipeline.

Scope and methodology

Static review of the demo repository: dependency and secret scanning patterns, configuration review, and manual inspection of authentication, authorization, and multi-tenant boundaries. Out of scope: network pentest, cloud IAM, load testing, and formal compliance mapping.

Findings

F-01Critical

Row-level security disabled on profiles table

CWE-862 · A01:2021 Broken Access Control

What it is

The profiles table is readable with the anon key and has no row-level security policies. Any visitor can enumerate every user’s profile.

Why it matters

A curious user or scraper can dump names, emails, and roles for the entire tenant — the exact failure class behind recent vibe-code showcase breaches.

Evidence

supabase/migrations/001_init.sql — CREATE TABLE profiles …; RLS not enabled. Client uses NEXT_PUBLIC_SUPABASE_ANON_KEY=****ab12

How to fix

  1. Enable RLS: ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
  2. Add policies so users select/update only their own row (auth.uid() = id).
  3. Verify with the anon key that cross-user reads return empty.

Paste-back fix prompt

                Enable Supabase RLS on the profiles table. Add policies so authenticated users can SELECT/UPDATE only rows where id = auth.uid(). Do not allow anon role to read other users’ profiles. Show the SQL migration.
              
F-02Critical

Service-role API key embedded in client bundle

CWE-798 · A07:2021 Identification and Authentication Failures

What it is

A Supabase service-role key is imported into a client component and ships in the browser JavaScript bundle.

Why it matters

Anyone who view-sources the app can impersonate the backend, bypass RLS, and read or modify the entire database — and rack up unlimited API spend.

Evidence

src/lib/supabase-admin.ts exported into src/app/dashboard/page.tsx; key redacted …x9f2

How to fix

  1. Remove service-role usage from any file imported by client components.
  2. Move privileged operations to a server-only route or Edge Function.
  3. Rotate the exposed key immediately.

Paste-back fix prompt

                Find any Supabase service-role key usage in client-side code and move it to a server-only API route. Rotate the exposed key. Keep only the anon key in NEXT_PUBLIC_* env vars.
              
F-03High

Admin gate enforced only in React UI

CWE-602 · A01:2021 Broken Access Control

What it is

The /admin page hides controls when role !== "admin" in the browser, but the underlying /api/admin/users endpoint does not verify role server-side.

Why it matters

Changing a localStorage flag or calling the API directly grants admin actions to any logged-in user.

Evidence

src/app/admin/page.tsx (client check only); src/app/api/admin/users/route.ts — no role verification

How to fix

  1. Verify session and role on every admin API route before returning data.
  2. Treat UI checks as convenience only, never as authorization.

Paste-back fix prompt

                Add server-side authorization to all /api/admin/* routes. Reject requests unless the authenticated user has role=admin in the database. Remove reliance on client-only role checks.
              
F-04High

Broken object-level authorization on order detail

CWE-639 · A01:2021 Broken Access Control

What it is

GET /api/orders/[id] returns any order by ID without checking that the order belongs to the current user.

Why it matters

Changing the ID in the URL or API call exposes other customers’ order contents and PII — classic BOLA/IDOR.

Evidence

src/app/api/orders/[id]/route.ts — select by id only, no owner filter

How to fix

  1. Filter by both id and owner_id = auth.uid() (or equivalent).
  2. Return 404 for unauthorized IDs (avoid confirming existence).

Paste-back fix prompt

                Fix IDOR on the order detail API: only return an order if it belongs to the authenticated user. Use parameterized queries. Return 404 when unauthorized.
              
F-05Medium

.env committed and served from public folder

CWE-200 · A05:2021 Security Misconfiguration

What it is

A .env file with database and API credentials is present in public/ and is downloadable from the deployed origin.

Why it matters

Attackers routinely probe /.env. A successful hit hands over the full configuration without needing a vulnerability chain.

Evidence

public/.env.example renamed to public/.env in fixture; contains DATABASE_URL=…****4c1a

How to fix

  1. Delete secrets from public/; rotate every exposed credential.
  2. Ensure .env is gitignored and not copied into static output.
  3. Block /.env and /.git at the reverse proxy.

Paste-back fix prompt

                Remove any .env files from public/ or static directories. Confirm .gitignore covers .env. Rotate all credentials that were exposed. Add server rules to return 404 for /.env and /.git.
              
F-06Low

Unauthenticated /debug/admin route

CWE-306 · A01:2021 Broken Access Control

What it is

A leftover debug route at /debug/admin lists users without authentication, intended for local development.

Why it matters

If deployed, anyone can enumerate accounts. Low severity only because the fixture marks it clearly — in production it would be High.

Evidence

src/app/debug/admin/page.tsx — no auth guard

How to fix

  1. Delete debug routes before deploy, or guard with auth and non-production env checks.
  2. Add a CI check that fails if /debug paths exist in production builds.

Paste-back fix prompt

                Remove or hard-disable /debug/* routes in production builds. Gate any remaining diagnostics behind authentication and NODE_ENV !== production.
              

Prioritized roadmap

Today

  • Rotate exposed service-role key (F-02)
  • Enable RLS on profiles (F-01)
  • Remove public/.env and rotate DB URL (F-05)

This week

  • Server-side admin authorization (F-03)
  • Fix order IDOR (F-04)
  • Delete or guard /debug/admin (F-06)

This month

  • Add regression tests for authZ
  • Proxy-level blocks for /.env and /.git
  • Threat model multi-tenant boundaries

Limitations and disclaimer

This assessment is point-in-time and scope-bounded. It does not guarantee security, certify compliance, or replace a formal penetration test where one is required. Findings reflect the demo application as reviewed on the dates above. Production systems change; re-test after remediation.

Book a Launch Readiness Audit →