PutThrough Product engineering studio
Book a build call

Sample audit report

This is exactly what you receive.

Not an excerpt and not a template — the complete report for one of our demonstration builds, in the format yours arrives in. Read it before you buy, and decide whether a document like this is worth $349 to you.

Download the sample report (PDF)

The same report as a PDF, in the format it is delivered in.

A demonstration build made for this purpose, not a client application. The findings below are real findings in that build.

Subject
Demonstration application — subscription dashboard
Stack
Lovable · React · Supabase · Postgres · Stripe · Vercel
Scope
Web application, database access rules, payment path, deployment configuration
Access provided
Read-only: repository, Supabase dashboard (viewer), Vercel project (viewer)
Turnaround
Two business days from access granted
Report version
1.0

01Summary

Summary

Not safe to launch. Four critical findings, two of which expose customer data to any visitor.

The application is functionally complete and the product logic is sound. Every finding below is in the layer between a working demonstration and a live service: access control, payment correctness, and the operational configuration needed to run and recover the system. None of it requires a rebuild. We estimate the full remediation at [n] hours.

Findings by severity

  • 4 Critical
  • 3 High
  • 3 Medium
  • 2 Low

How to read this

  • Severity is about consequence, not effort. A critical finding is one that exposes data, loses money, or stops the application working for everyone.
  • Every finding carries the evidence it was found from — a file and line, a configuration value, or a request we made. You can verify each one yourself.
  • Effort is our estimate of the work to fix it properly, including tests. It is the basis of the sprint quote, and we do not revise it upward later.
  • Findings we could not confirm with read-only access are listed as such rather than assumed either way.

02Findings

Findings

  1. PT-001 Critical

    Row-level security disabled on all public tables

    Evidence
    supabase/migrations/ — no policy statements present. Confirmed by an unauthenticated GET to /rest/v1/profiles?select=* with the anon key from the client bundle, which returned all [n] rows.
    Impact
    Any visitor can read and write every row of every table, including user records, subscription state and payment metadata. The anon key is published in the JavaScript bundle by design, so this requires no credentials and no exploitation — only a network tab.
    Recommended fix
    Enable RLS on every public table and write explicit per-role read and write policies. Add a test per table asserting that a user from one tenant cannot reach another’s rows, and run it in CI so a future migration cannot silently remove isolation.
    Effort
    [n] hours
  2. PT-002 Critical

    Service role key present in the client bundle

    Evidence
    src/lib/db.ts:14 — SUPABASE_SERVICE_ROLE_KEY read via import.meta.env and used in a browser-executed module. Recovered from the built asset at /_astro/db.<hash>.js.
    Impact
    The service role key bypasses row-level security entirely. Anyone with the bundle has unrestricted read and write access to the database regardless of what policies are added, which means fixing PT-001 alone would leave the system fully exposed.
    Recommended fix
    Rotate the key immediately. Move every call that requires elevated access behind server-side endpoints that check the caller. Add a build-time scan of client output for key patterns, failing the build on a match.
    Effort
    [n] hours
  3. PT-003 Critical

    Stripe webhook endpoint does not verify signatures

    Evidence
    api/webhooks/stripe.ts:8 — the handler parses the request body and acts on event.type without calling stripe.webhooks.constructEvent. No signing secret is referenced anywhere in the repository.
    Impact
    Any party who discovers the endpoint URL can post a forged event and grant themselves a paid subscription, cancel another customer’s, or alter billing state. The endpoint is discoverable, because it is a conventional path.
    Recommended fix
    Verify every event against the signing secret before acting on it, and reject anything that fails. Treat Stripe as the source of truth for subscription state rather than writing it from the event payload alone.
    Effort
    [n] hours
  4. PT-004 Critical

    Webhook handler is not idempotent

    Evidence
    api/webhooks/stripe.ts:22 — an insert on every checkout.session.completed event with no uniqueness constraint and no record of processed event IDs. Reproduced on a local copy by replaying one event three times, which created three subscription rows.
    Impact
    Stripe retries events on any non-2xx response and occasionally delivers duplicates in normal operation. The result is duplicate subscriptions, double entitlement and a database that disagrees with the payment provider about what the customer has paid for.
    Recommended fix
    Store the Stripe event ID with a unique constraint and short-circuit on a repeat. Make subscription writes upserts keyed on the Stripe subscription ID rather than inserts.
    Effort
    [n] hours
  5. PT-005 High

    Administrative actions are gated in the interface only

    Evidence
    src/components/AdminPanel.tsx:31 hides the controls behind a role check; the corresponding endpoints in api/admin/ perform no check of their own. Confirmed on a staging deployment by calling POST /api/admin/users/<id>/role directly with a standard user session.
    Impact
    Any authenticated user can perform administrative actions by calling the endpoints directly, including changing their own role. Hiding a control is a user-experience decision, not an access control.
    Recommended fix
    Enforce the role check server-side on every privileged endpoint. Keep the interface check as well, so users are not shown actions that will fail.
    Effort
    [n] hours
  6. PT-006 High

    No error tracking or structured logging in production

    Evidence
    No error reporting SDK in package.json. Server handlers catch and return a generic 500 without logging the cause; Vercel function logs show the status only.
    Impact
    Production failures are invisible. A broken payment path or a failing migration would be reported by customers rather than detected, and there would be no trace to diagnose from afterwards.
    Recommended fix
    Add error tracking with release tagging and source maps, structured logging on server handlers, and an uptime check on the critical paths. Route alerts somewhere a human reads.
    Effort
    [n] hours
  7. PT-007 High

    No database backups configured

    Evidence
    Supabase project settings — point-in-time recovery disabled, no scheduled export, no restore procedure in the repository.
    Impact
    A bad migration, an accidental delete or a successful attack against PT-001 would be unrecoverable. There is no point in the system’s history that could be returned to.
    Recommended fix
    Enable point-in-time recovery, schedule exports with retention, and document the restore. Execute the restore once into a scratch project, because an untested backup is an assumption.
    Effort
    [n] hours
  8. PT-008 Medium

    Staging and production share one database

    Evidence
    A single SUPABASE_URL across all Vercel environments; preview deployments connect to the production project.
    Impact
    Any preview deployment can write to live customer data, and a destructive migration tested on a branch runs against production. This also means no environment exists in which a restore can be rehearsed.
    Recommended fix
    Separate projects per environment with distinct credentials, and a seeded staging dataset that contains no real customer records.
    Effort
    [n] hours
  9. PT-009 Medium

    No rate limiting on authentication or payment endpoints

    Evidence
    No rate limiting middleware or platform configuration on api/auth/ or api/checkout/. On a staging deployment, [n] sequential sign-in attempts from one address were all served.
    Impact
    Credential stuffing against sign-in, and cost amplification against endpoints that call paid third-party APIs. Neither is prevented and neither would currently be noticed.
    Recommended fix
    Rate limit by address and by account on authentication, and per session on endpoints with a downstream cost. Return a 429 rather than failing silently.
    Effort
    [n] hours
  10. PT-010 Medium

    Server-side input validation missing on write endpoints

    Evidence
    api/profile/update.ts:11 spreads the request body into the database update without a schema. Validation exists in the client form only.
    Impact
    Fields the form does not expose can be written directly, including any column the access rules would otherwise permit. Combined with PT-001 this is a direct path to privilege escalation.
    Recommended fix
    Validate every request body against an explicit schema on the server, allow-listing fields rather than excluding known-bad ones.
    Effort
    [n] hours
  11. PT-011 Low

    Dependencies carry known advisories

    Evidence
    npm audit reports [n] advisories, [n] of them high, across [n] direct dependencies.
    Impact
    None currently reachable from application code, but the set grows over time and nothing is watching it.
    Recommended fix
    Update the affected packages, then enable automated dependency updates with CI gating so this stays at zero without anyone remembering to look.
    Effort
    [n] hours
  12. PT-012 Low

    Security headers not set

    Evidence
    Response headers on the production deployment carry no Content-Security-Policy, X-Content-Type-Options or Referrer-Policy.
    Impact
    Defence in depth only — no specific vulnerability depends on it. Worth doing because it is cheap and because it constrains the blast radius of a future injection defect.
    Recommended fix
    Add a content security policy and the standard header set at the edge, verified by a check in CI.
    Effort
    [n] hours

03Unconfirmed

Could not be confirmed with read-only access

Listed rather than assumed. Each would be resolved in the first hours of a sprint.

  • Whether the exposed service role key has already been used by a third party. This requires access to database audit logs, which are not available at the current Supabase plan.
  • Whether any duplicate subscription records in production have resulted in incorrect billing. Confirming this needs a reconciliation run against Stripe with write access to report on.
  • Whether historical customer data was accessed during the period PT-001 was live. Without request logs from before the audit window, this cannot be answered either way.

04Next

What we would do next

The four critical findings are the launch blockers and account for roughly half the estimated effort. We would fix those first, in that order, since PT-002 must be resolved before fixing PT-001 actually closes the exposure. The remaining findings are worth doing in the same sprint because the work overlaps, but the application could be launched safely once the criticals, PT-005 and PT-007 are closed.

Indicative sprint Launch Hardening Sprint, with the audit fee credited.

Download the sample report (PDF)

This report describes one of our demo rescues.

Get this for your app.

Same format, same depth, two business days from the moment you grant read-only access. If we find nothing material, you do not pay for it.

Prefer to write? Send your requirements

What do you need?

What it should do, who will use it, the must-haves, and any links. A few sentences is plenty.

Budget, if you have one in mind
When would you like to start?