ChatGPT can generate Supabase Row Level Security policies that are syntactically valid, but it cannot be trusted to generate secure ones without review, because it does not know your data model or your threat model and, like AI-generated code in general, tends to produce policies that are too permissive or incomplete. The classic failures are a policy that effectively allows everyone, an ownership check that only confirms the user is logged in rather than that they own the row, and a missing write-side check that lets a user create or change rows belonging to someone else. The function it uses to identify the user, auth.uid(), is itself safe; the mistakes are in the policy logic around it. So use ChatGPT to draft a policy, but review it, test it as different users, and never deploy an AI-generated policy unverified.
Short answer
ChatGPT is a useful drafting aid and an unreliable security author for Row Level Security, so treat its output as a starting point you must verify. Per Supabase's Row Level Security docs, a policy must be enabled on the table and must correctly scope access, using USING for what rows are visible and WITH CHECK for what can be written, and getting either wrong exposes data. Research supports the caution: a Stanford study found developers with AI assistants wrote less secure code while being more confident it was safe. So let ChatGPT draft policies, then confirm Row Level Security is enabled, both clauses are present, and access is denied when you test as another user.
Can ChatGPT write RLS policies at all?
Yes, ChatGPT can produce Row Level Security policies that are valid SQL and often look correct, which is exactly what makes relying on them risky. It knows the syntax of a policy, the common patterns like comparing the authenticated user to an owner column, and the Supabase helper functions, so its output usually runs without error.
The problem is that correct syntax is not the same as correct security. ChatGPT does not know which of your tables hold sensitive data, who should see what, or how an attacker might abuse a gap, so it fills those gaps with plausible defaults that are frequently too open. So the honest position is that ChatGPT can write a policy, but whether that policy actually protects your data is a question only you can answer by reviewing and testing it, which the rest of the sections show how to do.
The most common ChatGPT RLS mistakes
The failures cluster into a short, predictable list. The most dangerous is a policy that is effectively open, such as one that permits any row, which grants everyone access and defeats the point of Row Level Security. Close behind is an ownership check that only verifies the user is authenticated rather than that they own the specific row, which lets any logged-in user reach any row. Then there is omitting the write-side check, so read access is scoped but a user can still insert or update rows they should not.
Two structural mistakes round out the list. ChatGPT may hand you a policy while forgetting that Row Level Security has to be enabled on the table for the policy to take effect, so nothing is actually enforced. And it may write a policy for one operation, typically reading, while leaving other operations ungoverned or applying the policy to the wrong database role.
The auth.uid() mistakes
The function ChatGPT uses to identify the current user, auth.uid(), is safe and correct: it returns the authenticated user's id from their token, evaluated on the server where the client cannot forge it, so basing access on it is the right approach. The mistakes are not in the function but in how the generated policy uses it. The frequent one is a policy whose condition is only that auth.uid() is not null, which merely checks that someone is logged in and therefore lets any authenticated user access every row, when the intent was to restrict each user to their own.
The correct pattern compares auth.uid() to the row's owner column, so a user sees and changes only rows where they are the owner. Other errors ChatGPT makes include comparing auth.uid() to the wrong column, or scoping reads with it but forgetting to apply the same ownership condition to writes. So when you review an AI-generated policy, check that auth.uid() is compared to the right ownership column, not merely tested for being logged in, and that the same ownership logic governs both reads and writes.
USING versus WITH CHECK
A specific and costly ChatGPT mistake is omitting WITH CHECK, and understanding the difference is what lets you catch it. In a Row Level Security policy, the USING clause decides which existing rows are visible to a query and which existing rows an update or delete is allowed to touch, while the WITH CHECK clause decides which new or modified rows are allowed to be written. So USING protects reads and the targeting of writes, and WITH CHECK protects the content of writes.
If ChatGPT gives you a policy with only USING, a user might be correctly limited to their own rows for reading, yet able to insert a row that claims a different owner, or update a row to reassign it, because nothing checks the values being written. So an insert or update policy needs a WITH CHECK clause that enforces the same ownership condition on the written data, for example that the owner column of the new row equals the authenticated user. When reviewing a generated write policy, the first thing to confirm is that WITH CHECK is present and matches your ownership rule.
Enable RLS and cover every operation
Two setup steps are easy for ChatGPT to skip and easy for you to forget. First, Row Level Security must be enabled on the table, because a table without it is fully accessible to anyone with the public key regardless of any policy you wrote, while a table with it enabled and no matching policy denies all access.
Second, policies are per operation, so you need to account for select, insert, update, and delete rather than assume one policy covers everything. ChatGPT often produces a read policy and stops, leaving writes either wide open or fully blocked depending on your setup. So confirm that each operation your app performs has an appropriate policy, and that the policies target the correct role, typically the authenticated role rather than the anonymous one, so access matches who should be doing what.
What about Postgres injection?
For ordinary Row Level Security policies, SQL injection is not the main risk, because a policy that compares auth.uid() to a column is a fixed condition with no place for attacker input, so a standard ownership policy is not injectable.
Injection becomes a concern only when generated code goes beyond a simple comparison, for example a helper function that a policy calls which builds dynamic SQL from input, or a policy that references user-controllable data unsafely. If you ask ChatGPT for a more complex policy or a SECURITY DEFINER function, review it for any string-concatenated SQL and make sure inputs are handled as parameters, not spliced into a query. So the rule is that plain auth.uid()-based policies are safe from injection, while any generated function or dynamic query needs the same injection review you would give any SQL.
The common mistakes at a glance
Knowing what to look for turns review into a checklist. The table below maps the frequent ChatGPT RLS errors to the fix.
| Mistake | Why it is unsafe | Fix |
|---|---|---|
| Effectively open policy | Grants everyone access | Scope to the owner with auth.uid() |
| Only checks logged in | Any user reaches any row | Compare auth.uid() to the owner column |
| Missing WITH CHECK | Users can write others' rows | Add WITH CHECK with the ownership rule |
| RLS not enabled | The policy does nothing | Enable Row Level Security on the table |
| Only one operation covered | Writes ungoverned or blocked | Add policies for all needed operations |
Read the first three rows as the high-severity checks, since an open policy, a logged-in-only check, or a missing WITH CHECK each exposes data outright.
How to verify an AI-generated policy
Working through these checks confirms a generated policy is actually safe. The checklist below covers them.
| Step | Action | Done? |
|---|---|---|
| Enable RLS | Confirm Row Level Security is on for the table | [ ] |
| Check ownership logic | auth.uid() compared to the owner column | [ ] |
| Confirm WITH CHECK | Write policies enforce ownership on new rows | [ ] |
| Cover all operations | Policies for select, insert, update, delete | [ ] |
| Review any function | No unsafe dynamic SQL in helpers | [ ] |
| Test as another user | A second user cannot see or change your rows | [ ] |
The step teams skip most is testing as another user, since that is the only check that proves the policy denies access it should deny, rather than merely looking correct.
Where a scan fits
Because an AI-generated policy can look right while leaving data open, verifying the result rather than trusting the generator is where safety comes from, and an objective check helps you find what a review misses.
A scanner like PTKD.com analyzes your app build and flags issues such as tables exposed without Row Level Security, access relied on from the client without server-side rules, and an exposed service role key, mapped to OWASP MASVS. To be clear about the boundary: PTKD does not write or grade your Row Level Security policies, which are your Supabase setup, and it does not replace testing them as different users. It helps you catch the exposures that an AI-generated policy most often leaves, so a policy ChatGPT drafted does not ship as an open door.
What to take away
- ChatGPT can generate syntactically valid Supabase Row Level Security policies, but not reliably secure ones, so treat its output as a draft to review and test.
- The common failures are effectively open policies, ownership checks that only confirm the user is logged in, and missing WITH CHECK clauses that let users write other people's rows.
- auth.uid() is safe because it comes from the verified token; the mistakes are in the policy logic, so compare it to the owner column and apply the same rule to writes.
- Enable Row Level Security on the table and cover select, insert, update, and delete for the right role, since a policy without RLS enabled does nothing.
- Plain auth.uid() policies are not injectable, but review any generated function for unsafe dynamic SQL, and use a tool like PTKD.com to catch tables left exposed.



