Server routes that answer whoever asks
API Route Missing Authentication
Why this workflow produces it
Read the reach and the occurrence count in the header above together, because they point in opposite directions. Few projects, many occurrences. The projects that have a server surface at all have a lot of unprotected handlers each, rather than many projects having one apiece.
The reason so few projects appear here is not a security property and should not be read as one. Most projects with this scaffold are client-only single-page apps — this is the smallest median project in the round by a wide margin — so most of them simply have no server on which to get this wrong.
Where a server does show up, it usually arrives late: the app already works, something needs to happen off the client, and a handler gets added to make that one thing possible. It inherits no conventions, because there were none to inherit. Nothing in the project establishes that a handler should check who is calling before it does the work.
And it never announces itself, because your own session is always present while you are testing. You never see the response the endpoint gives to somebody who is not signed in.
The change that prevents it
Write the rule down once, in your project instructions, so it applies to every handler generated after it rather than being remembered per handler.
Paste this into your project instructions, or into the prompt itself:
Every server route resolves the caller's identity as its first statement and returns 401 before doing any work if there is not one. Do this inside the handler itself, not only in a middleware matcher — a matcher does not cover routes added later. If an endpoint is deliberately public, put a comment above it saying why.
The scan that verifies it
Scan and confirm the count drops. Then do the part a scanner cannot do for you: call each remaining handler with no cookie and no authorization header, and read what comes back.
npx xploitscan@latest scan . -f json \ | jq '[.findings[] | select(.rule == "VC003" or .rule == "VC065")] | length'
VC065 is outside the free rule set. The free scan runs 30 of the 223 rules, so a free plan never evaluates it. This command also counts VC003, which a free scan does run — so the number it returns on a free plan is the VC003 count alone, and it says nothing either way about VC065.
Honest caveat. Both of these checks read a single file, apart from one narrow exception: for Supabase Edge Functions the scanner also reads supabase/config.toml to see whether the platform verifies a JWT before your function runs. Everything else is judged from the handler alone, which cuts both ways. A route genuinely gated by middleware still reads as unprotected — our false positive, not your bug — and a route whose gate is imported from a helper we cannot follow reads the same way. Treat each hit as a handler worth opening rather than a confirmed hole.