Sample report
Launch Audit report: an open-source Next.js app
This is a sample, published so you can see exactly what a paid Launch Audit gives you before you pay. It is a real audit of a public open-source project (MIT licence), not a customer's app. Nobody paid for it, and nobody's live site was touched: the code was read on my machine only.
Why the project is not named: the problem below is still in the project's code and has not been fixed yet. Many people have copied it into their own apps, so the project name, file paths and exact code are left out until its maintainer has been told and has had time to fix it.
- Prepared by
- Vishwas, Furnace
- Date
- 18 October 2026
- Stack
- Next.js, Prisma, a sign-in library, Stripe
- Tools
- Furnace launch-check --details, then a hand check
What this report is, and is not
An automated check of one commit of the code, with every finding checked by hand by a person, at one point in time. It is not a penetration test, a certification, or a guarantee. It does not say the app is secure. Anything not listed under "What was checked" was not looked at.
Summary
A signed-out visitor who has a post's id can delete or rewrite that post. Furnace found it, and reading the code confirms it. Fix it before anything else: it is a few lines of code (below). Everything else is an improvement, not a blocker.
| Confirmed | Likely | Questions for you |
| 1 | 0 | 0 |
What the hand check changed (this is what you pay a person for)
- Furnace listed 1 problem, 3 questions and 3 improvements. After checking by hand: 1 confirmed problem, 0 open questions, 3 improvements, and 1 finding dropped as a false alarm.
- Furnace's two questions about the post endpoints ("is there a login check?", "is there an ownership check?") have the same answer as F1, so they are folded into it.
- Furnace's "4 submitted values have no visible validation" was wrong: each one is checked with a schema on the next line, and the Stripe webhook checks Stripe's signature. Dropped. (A false alarm in Furnace, since fixed in Furnace.)
Findings
F1 · High (Critical if post ids are ever visible to others) · Confirmed
Anyone with a post's id, signed in or not, can delete or rewrite it
- Where
- The post API's delete and update handlers, through the shared "does this user own this post?" helper.
- What happens
- The helper counts posts matching the post's id and the signed-in user's id. With nobody signed in, the user id is
undefined, and Prisma leaves out a filter whose value is undefined. The check then matches the post by its id alone and says "yes". So a request with no login can delete any post, or replace its title and content. The attacker needs the post's id. Ids are long random strings, so they are hard to guess, but they show up in links, browser history, screenshots and logs. An id is not a password, and the code treats it as one.
- How I checked
- Read the handlers and the helper. A signed-in user asking for another user's post is refused correctly (the filter uses their own id and finds nothing); only the signed-out case slips through. Prisma's documented behaviour for
undefined in a filter is to ignore that field.
- Fix
- Refuse when there is no session, before the query, and use the non-optional id. Both handlers already turn
false into a 403.
async function verifyAccess(postId: string) {
const session = await getServerSession(authOptions)
if (!session?.user?.id) return false
const count = await db.post.count({
where: { id: postId, authorId: session.user.id },
})
return count > 0
}
- Check it's fixed
- Signed out, send a delete request for a real post id: it must answer 403 and the post must still exist. Signed in as another user: also 403.
Worth improving (does not block launch)
I1. No security headers in the code or host config · Medium
The Next.js config sets none. Your host may add some; check the live site's response headers. Add Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options and Referrer-Policy in the config's headers() (Furnace's report has the code).
I2. No rate limit on the post write endpoints · Low once F1 is fixed
After the fix they need a login. Before it they are open to everyone, which makes F1 worse: a script could empty the posts table quickly.
I3. No rate limit on the billing endpoint · Low
It needs a login and creates a Stripe checkout or billing-portal session per call. Stripe sessions cost nothing, but a signed-in script can create many; a per-user limit (10 a minute) is enough.
Questions for you
None open. (Furnace asked two; both are answered by F1.)
What was checked
- Logins on server endpoints: 9 endpoints. Problem found: F1. No other endpoint lets a signed-out visitor change data within these checks.
- Who owns what (one user reaching another's records): post and user endpoints. Signed-in users cannot reach each other's posts or profiles. Only the signed-out case in F1.
- Secrets in the code and its git history: no problems found within these checks.
- Payment webhooks: the Stripe webhook verifies Stripe's signature before acting.
- Dangerous input (code execution, SQL text, file paths): no problems found within these checks.
- Rate limits and paid services: see I2 and I3.
- Deployment settings and security headers: see I1.
What was not checked
The live site, hosting and network settings, the database itself, Stripe and email account settings, the 14 server pages Furnace lists as "not checked for login" (who may see each dashboard page was not reviewed one by one in this sample), packages with known weaknesses (not run for this sample), and anything newer than the audited commit.
What I did to keep this honest
- The finding above was traced in the code; nothing was passed on without being read.
- One Furnace finding was dropped as wrong, with the reason above.
- The code is a public repository; nothing private was used.
Re-check and signature
Re-check: a paid audit includes one free re-check within 14 days of delivery. You send the new commit when you have fixed things, and each finding is marked Closed, Still open or Changed, with how it was checked.
Signature: in a paid audit, the report's fingerprint is signed with Furnace (Ed25519) and sent with the signature file and the public key, so you can check it has not changed since it was signed. This sample is not signed.
Want this for your app?
Founding price: $99 for the first 10 audits, then $299. Paid before the audit starts. If it finds nothing you need to fix before launch, you get your money back.