This is what you receive after an audit: every finding with its severity, the evidence, the risk in plain words, and a recommendation.
SampleExample: this report describes a fictional application. It contains no real client, no real data, and no credentials.
Summary
Stack
Next.js, Supabase, a payment provider, deployed on Vercel
Findings
Critical: 1High: 3Medium: 3Low: 2
Recommendation
Harden, do not rebuild. Sign-in and the data model are sound; the findings can be fixed in focused work packages, starting with the critical and high-severity ones.
Findings
F-01 Row-level security is off on the invoices table
CriticalArea: Security hardening
Evidence
The invoices table has row-level security disabled, and the public key that ships with the browser app can read every row.
Risk
Any visitor can read every customer's invoices, including names and amounts.
Recommendation
Enable row-level security, add a policy that limits each row to its owner, and test it with two accounts before release.
Codesupabase/policies.sql
- -- no policy yet (row-level security disabled)+ alter table invoices enable row level security;+ create policy "owner reads own invoices" on invoices+ for select using (auth.uid() = user_id);
F-02 Service key in git history and in the browser bundle
HighArea: Security hardening
Evidence
A key with administrator rights appears in an early commit and is compiled into the JavaScript sent to the browser.
Risk
Anyone who opens the browser's developer tools can act as an administrator of the database.
Recommendation
Rotate the key now, remove it from the history, and move the calls that need it into a server function that reads it from the environment.
Codelib/supabase.ts
- const db = createClient(url, "sb_service_…")+ // server only: never import this module in client code+ const db = createClient(url, process.env.SUPABASE_SERVICE_KEY!)
F-03 Admin routes are protected only in the browser
HighArea: Security hardening
Evidence
The admin menu is hidden for normal users, but the API routes under /api/admin do not check the user's role.
Risk
Any signed-in user can call administrator functions directly.
Recommendation
Check the role on the server for every admin route and add one test per route that expects a refusal for a normal user.
F-04 The payment webhook accepts forged and repeated requests
HighArea: Stabilization & refactoring
Evidence
The webhook does not verify the payment provider's signature and creates a new invoice for every delivery, including retries.
Risk
A forged request can mark an invoice as paid, and a retry books the same payment twice.
Recommendation
Verify the signature, store each event identifier, and ignore events that were already processed.
Codeapi/webhook.ts
export async function POST(req: Request) {- const event = await req.json()+ const event = verifySignature(req, process.env.WEBHOOK_SECRET!)+ if (await alreadyProcessed(event.id)) return new Response(null, { status: 200 })
F-05 Login and password reset have no rate limit
MediumArea: Security hardening
Evidence
The login and password-reset endpoints accept any number of attempts per address and per account.
Risk
Passwords can be guessed by automated attempts, and the reset mail can be used to flood a user.
Recommendation
Limit attempts per account and per address, add a short lockout, and notify the user of repeated failures.
F-06 No tests, widespread any types, and copied data access
MediumArea: Stabilization & refactoring
Evidence
There are no automated tests, most functions are typed as any, and the same query is copied into many components.
Risk
Every change can break something unrelated, and nobody can tell until a user reports it.
Recommendation
Add tests around invoicing and sign-in first, replace any in the data layer, and move the copied queries into one module.
F-07 Staging shares the production database and backups were never restored
MediumArea: Production readiness & operations
Evidence
The staging site is configured with the production database URL, and the backup setting is on but no restore has ever been tried.
Risk
A test can destroy customer data, and a backup that was never restored may not work when it is needed.
Recommendation
Give staging its own database, restore the latest backup into it once, and write down the steps.
F-08 Two dependencies have known high-severity advisories
LowArea: Audit & triage
Evidence
The dependency audit lists two packages with known high-severity advisories, both pulled in indirectly.
Risk
Known weaknesses can be exploited without any knowledge of the app itself.
Recommendation
Update or replace the packages, enable automatic update checks, and re-run the audit before each release.
F-09 No error monitoring and no health check
LowArea: Production readiness & operations
Evidence
Errors are only visible in the browser console, and there is no endpoint that tells a monitor whether the app is alive.
Risk
Outages and failing payments are noticed by customers first.
Recommendation
Add error tracking, a health endpoint, and an alert to one person, and check them in the staging environment.
Want this for your app?
We start with an audit of fixed scope. You get a report like this one for your own code.
We use only essential, cookieless, anonymous analytics. Non-essential cookies or tracking scripts are only set with your consent. See our privacy policy for details.