Validra
Free guide

25 mistakes AI coding tools still make in production.

The exact methodology we use on every engagement, freely available. Six categories, twenty-five concrete vulnerabilities: why they happen, how to test for them, how to fix them.

01

Row-Level Security (RLS) and data access control

RLS disabled on a table
Critical

Why it happens

Supabase disables RLS by default on new tables. A vibe-coder who doesn't know this detail exposes the entire table to anyone holding the anon key, which is public by design.

How to test

SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND rowsecurity = false;

How to fix

ALTER TABLE table_name ENABLE ROW LEVEL SECURITY; then write explicit policies.

Overly permissive RLS policy (USING (true))
Critical

Why it happens

During development, a USING (true) makes permission errors disappear. Convenient to move fast, forgotten before launch.

How to test

Manually audit every policy in the Supabase dashboard, or via SELECT * FROM pg_policies;

How to fix

Replace it with a condition tied to auth.uid(), for example USING (auth.uid() = user_id).

Policy checked for SELECT but not for UPDATE or DELETE
High

Why it happens

AI generators often create a correct read policy but forget to duplicate the same logic for the other operations. INSERT, UPDATE, and DELETE each need their own policy.

How to test

Create two test accounts, attempt an UPDATE or DELETE query from account A on a row owned by account B, using the anon key.

How to fix

One policy per operation (FOR SELECT, FOR UPDATE, FOR DELETE, FOR INSERT), never a single loosely-scoped FOR ALL policy.

Foreign key relationships bypassing policies
High

Why it happens

A protected table references an unprotected table. An attacker goes through the weaker table to reach the protected table's data via a join.

How to test

Map every foreign key, and check RLS on each related table, not just the main one.

How to fix

RLS enabled and consistent across the entire schema, not reviewed table by table in isolation.

Poorly scoped RPC functions (SECURITY DEFINER)
Critical

Why it happens

PostgreSQL functions exposed through the Supabase API with SECURITY DEFINER run with the creator's privileges, not the caller's. This bypasses RLS if written carelessly.

How to test

List every exposed RPC function and check whether it properly filters by the calling user.

How to fix

Add an explicit auth.uid() check inside the function; never assume RLS applies automatically.

02

Exposed secrets and keys

service_role key used on the client
Critical

Why it happens

The service_role key bypasses RLS entirely. A developer in a hurry uses it to make things work during testing, and it's left in the final code.

How to test

Grep the source code and the shipped JS bundle for "service_role" or the Supabase JWT key pattern.

How to fix

The service_role key should only ever exist server-side (Edge Functions, API routes), never in a variable exposed to the browser.

Third-party API keys exposed in the client bundle
Critical

Why it happens

Confusion between public (NEXT_PUBLIC_*) and private environment variables. A wrong prefix exposes a secret key to the browser.

How to test

Inspect the final JS bundle for key patterns (sk_live_, sk-, and similar).

How to fix

Every secret key must stay free of the NEXT_PUBLIC_ prefix, called only from the server.

Secrets committed to Git history
High to critical depending on the key

Why it happens

A .env file added by mistake in an early commit, then deleted, but it stays in Git history and remains retrievable.

How to test

Scan the full history with gitleaks or trufflehog, not just the current code.

How to fix

Immediately rotate any key found (change the key, not just remove it from the code), and get .gitignore right from the start.

Environment variables missing from .env.example
Low, but an aggravating factor for other vulnerabilities

Why it happens

Without clear documentation of which variables are public versus private, every new environment repeats the same mistakes.

How to test

Check for consistency between .env.example, the project documentation, and actual usage in the code.

How to fix

Explicitly document every variable with a "PUBLIC" or "SECRET, SERVER ONLY" comment.

03

Storage buckets

Storage bucket public by default
High

Why it happens

Supabase Storage offers public buckets to simplify displaying images. A developer picks "public" to make things work fast, including for sensitive documents.

How to test

List every bucket and its public or private status, then attempt direct URL access with no authentication.

How to fix

Private buckets by default, plus a signed access policy (createSignedUrl) for any content not meant for the general public.

Predictable file URLs
Medium to high depending on content sensitivity

Why it happens

Naming uploaded files with an auto-incremented ID or the original, non-random filename makes it possible to guess or enumerate other users' files.

How to test

Upload a file, observe the naming pattern, try incrementing or guessing other URLs.

How to fix

Random UUID filenames, never tied to a guessable internal ID.

Missing bucket policies despite a "private" bucket
High

Why it happens

Marking a bucket as private in the interface isn't enough. Without an RLS policy on storage.objects, access can remain poorly controlled depending on configuration.

How to test

Check RLS policies specifically on the storage schema, not just on business tables.

How to fix

Explicit policies on storage.objects tied to auth.uid() or the file owner.

04

Authentication and session management

No rate limiting on login
High

Why it happens

AI-generated templates rarely implement anti-brute-force protection by default. It's invisible until someone attacks it.

How to test

Script a series of rapid login attempts and observe whether a lockout or delay kicks in.

How to fix

Rate limiting at the middleware level or through a dedicated service (Supabase Auth offers options, otherwise Upstash or Redis).

Insecure password reset
High

Why it happens

Reset token too short, non-expiring, or reusable multiple times.

How to test

Generate a reset link, verify its actual expiration, and try reusing it after it's been used once.

How to fix

Single-use token, short expiration (15 to 30 minutes), invalidated after use.

Roles and permissions checked only on the client
Critical

Why it happens

Hiding an "Admin" button in the interface gives the illusion of security, but the underlying API remains directly accessible.

How to test

Call admin endpoints or actions directly via an HTTP client using a non-admin account, bypassing the interface entirely.

How to fix

Every role check must be duplicated server-side (API route, Edge Function, or RLS policy), never only as a React conditional.

Session or JWT passed in the URL
High

Why it happens

A debugging or implementation shortcut: a token passed as a query parameter instead of a secure header or cookie.

How to test

Watch network requests during authentication and look for tokens in query parameters.

How to fix

Tokens only in an Authorization header or an httpOnly, secure cookie, never in a URL.

No email verification before account activation
Medium

Why it happens

Simplifies onboarding during development, forgotten in the production configuration.

How to test

Create an account with an unverified email and try accessing the product's features.

How to fix

Enable mandatory email confirmation in the authentication settings before public launch.

05

API surface and input validation

Endpoints or Edge Functions with no authentication check
Critical

Why it happens

An endpoint created for an internal test, never protected, forgotten in production.

How to test

List every deployed endpoint and function, and test each one with no authentication header.

How to fix

Systematic authentication middleware, with an explicit check at the start of every function.

No server-side input validation
High

Why it happens

Validation is done on the React side (forms) but never duplicated on the API side. An attacker bypasses the interface and sends data directly.

How to test

Send malformed or unexpected payloads directly to the API (wrong types, missing fields, out-of-range values).

How to fix

Systematic server-side validation with a library like Zod, never blind trust in incoming data.

Overly permissive CORS configuration
Medium

Why it happens

Access-Control-Allow-Origin set to a wildcard quickly resolves CORS errors during development, and is left that way in production.

How to test

Inspect the response headers of the API endpoints.

How to fix

An explicit allow-list of authorized domains in production.

Insecure direct object references (IDOR) on REST endpoints
Critical

Why it happens

An endpoint like /api/orders/123 doesn't check that the logged-in user actually owns order 123, only that the ID exists.

How to test

Access another user's resources by simply changing the ID in the URL or request.

How to fix

Systematic ownership checks on every request, never relying on the resource ID alone.

06

Third-party integrations and AI-specific risks

Webhooks with no signature verification
High to critical, direct financial impact if it's Stripe

Why it happens

A quickly built webhook handler processes any request it receives at the URL without checking that it genuinely came from the provider.

How to test

Send a forged request to the webhook endpoint with no valid signature and observe whether it gets processed.

How to fix

Systematically verify the cryptographic signature provided by the third-party service before any processing.

Prompt injection on AI features
Medium to high depending on what actions the LLM can access

Why it happens

User input is directly concatenated into a prompt sent to an LLM, letting the user manipulate the system instructions.

How to test

Try injecting instructions like "ignore the previous instructions and..." into fields that feed an AI prompt.

How to fix

A clear separation between system instructions and user input, input validation and filtering, and limits on what actions the LLM can trigger automatically.

LLM API key called from the client
High, financial risk if the key is abused

Why it happens

To prototype quickly, the AI API call is made directly from the front-end component instead of through a server route. This exposes the key and allows uncontrolled usage.

How to test

Inspect the browser's network requests while using an AI feature and look for direct calls to third-party AI APIs.

How to fix

Every AI generation call goes through a server route that holds the key, with per-user usage limits.

Outdated or vulnerable npm dependencies
Varies by vulnerability, from low to critical

Why it happens

AI tools generate working code but don't proactively update dependencies, some of which carry known vulnerabilities.

How to test

Run npm audit or pnpm audit regularly, and compare against the known vulnerability database.

How to fix

Regular updates, npm audit fix, and continuous monitoring through a tool like Dependabot.

What's next?

Just spotted one of these in your own project?

This list is our testing methodology, condensed. A full review goes further: manual access, prioritization by business impact, and a report you can hand straight to your team.