[KongchangAI]
· 3 min read· 1,902 words

Cursor Opened Firestore Rules to Fix a Permission Error — How Big Is the Risk?

Cursor Opened Firestore Rules to Fix a Permission Error — How Big Is the Risk?

AI editors often loosen Firestore rules to fix permission errors — but the real fix is the query.

AI editors like Cursor and Claude Code tend to loosen firestore.rules to `if true` or auth-only when they hit permission errors, exposing data to everyone or all registered users. The real cause is that Firestore rules are not filters and queries are all-or-nothing: the query is usually missing a `where()` clause matching the rules. The correct fix is owner-bound rules, matching queries, waiting for auth, and testing with the emulator or tools like SafeWeave before exposure.

TL;DR

When using an AI editor to add features to a Firebase app, if you hit FirebaseError: Missing or insufficient permissions, tools like Cursor and Claude Code commonly "fix" it by changing firestore.rules to allow read, write: if true; or if request.auth != null;. The former means anyone can read and write all your data; the latter means any registered user can. The real fix is usually in the query, not the security rules.

Firestore permission error

The Scene: One File, and Everyone's In

You ask Cursor or Claude Code to add an order page to your Firebase app. On page load, the console throws FirebaseError: Missing or insufficient permissions. You paste the error back into the chat window, and the next attempt loads fine.

The diff touched only one file — firestore.rules. It now lets everyone in.

This isn't a one-off. AI editors do the same thing when handling Supabase RLS errors and Stripe signature errors: the error itself is the defense working, and the fastest way to make it disappear is to tear the defense down. The model scans the error message for "where permissions are written," and the rules file happens to be right there.

Two Dangerous "Fixes"

The rules generated by AI editors usually fall into two versions, both of which make the error disappear.

First, completely unguarded:

// firestore.rules
// CWE-862: no authorization check at all
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Firebase's official "Avoid insecure rules" doc shows exactly this ruleset with the warning "Never use these rules in production." It means anyone who knows your project ID can read, modify, or delete every document.

Second, the version that looks responsible:

// firestore.rules
// Signed in is not the same as allowed
match /orders/{orderId} {
  allow read, write: if request.auth != null;
}

It mentions auth, so it feels like a fix. It also appears in Firebase's official "Get started with Cloud Firestore security rules" doc, which is why it's so widely used. But the official insecure rules page lists it under "Not recommended: any signed-in user has read and write access to the entire database." Anyone can sign up for an account in your app, so anyone can read every order and edit someone else's.

The project ID was never a secret. The Firebase API key doc explicitly says the key "can be included in code or checked-in config files" because data access is governed by security rules and App Check. So under if true, the full attack is one command:

curl "https://firestore.googleapis.com/v1/projects/YOUR_PROJECT_ID/databases/(default)/documents/orders"

No key, no token, and what comes back is your orders collection.

Firestore security rules are Firebase's declarative language for controlling client-side read/write access to Cloud Firestore data, stored in a file named firestore.rules. Rules match paths layer by layer with match, and each match block declares when to allow access via allow read, write: if <condition>. The variables available in conditions include request.auth (the current signed-in user's uid, token, etc.), resource.data (the contents of the existing document in the database), and request.resource.data (the contents submitted by this write). Rules are enforced server-side by Firebase and cannot be bypassed by client code, so they are the only line of defense for your data — the client SDK itself provides no access control. Understanding this expression syntax is the prerequisite for judging whether AI-generated rules are safe: if true lets everyone in, if request.auth != null lets every signed-in user in, and only comparing request.auth.uid against an owner field in the document actually achieves per-user isolation.

The Root Cause: The Query Is Wrong, Not the Rules

The AI editor opens the rules because the error message points at the rules, while the real cause usually hides in the query. Firestore rules are not filters. The official "Securely query data" doc puts it plainly: security rules are not filters, and queries are all-or-nothing. If a query could return even one document the user has no right to read, the entire query fails.

The developer wrote owner-only rules, and the AI wrote a query like this:

const snap = await getDocs(collection(db, 'orders'));

Firestore rejects it. The query asks for all orders, while the rules only allow your own. The error is correct. But the model sees "insufficient permissions" next to a rules file, so it loosens the rules until the query passes.

Two more factors push this along. When creating a Firestore database, Firebase offers a test mode that grants access to all users, so a huge number of public projects and tutorials start from open rules. In GitHub issues about this error, you often see "I already set my rules to if true" as a debugging step, which makes if true look like a normal thing to try.

Nothing in the editor flags the risk. The rules file isn't JavaScript, there are no type errors, and the app runs.

"Security rules are not filters" is the key to understanding Firestore permission errors. A filter's semantics are "query all the data and quietly drop the rows you're not allowed to read" — which is what many developers coming from SQL or Supabase RLS expect by default. Firestore's semantics are all-or-nothing: before executing a query, Firestore checks whether "the entire set of documents this query could return falls within what the rules allow." If even one potential result is a document the user has no right to read, the whole query is rejected outright with Missing or insufficient permissions, rather than returning partial data. This means rules and queries must match each other: if a rule requires ownerId == request.auth.uid, the query must explicitly carry where('ownerId', '==', uid) so Firestore can confirm the result set is legal before dispatching. Without that where(), loosening the rules doesn't solve the problem — it just hides it.

The Real Fix: Rules Bound to the Owner, Queries Matching the Rules

Write rules that check ownership, then make every query conform to them. Store the owner's uid on every document and compare it against request.auth.uid.

Rules:

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /orders/{orderId} {
      allow read, delete: if request.auth != null
                          && request.auth.uid == resource.data.ownerId;
      allow create: if request.auth != null
                    && request.auth.uid == request.resource.data.ownerId;
      allow update: if request.auth != null
                    && request.auth.uid == resource.data.ownerId
                    && request.resource.data.ownerId == resource.data.ownerId;
    }

    match /users/{userId} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow update: if request.auth != null && request.auth.uid == userId
                    && request.resource.data.diff(resource.data)
                         .affectedKeys().hasOnly(['displayName', 'photoURL']);
    }
  }
}

The update rule on orders also stops a user from reassigning a document to someone else by changing ownerId. The affectedKeys().hasOnly() check on users stops users from writing fields like role or plan into their own profile.

Queries that match these rules:

import { collection, query, where, getDocs, addDoc } from 'firebase/firestore';

const uid = auth.currentUser.uid;

// Reads: the where() matches the rule, so Firestore allows it
const q = query(collection(db, 'orders'), where('ownerId', '==', uid));
const snap = await getDocs(q);

// Writes: always set ownerId to the signed-in user
await addDoc(collection(db, 'orders'), { ownerId: uid, item, total });

Make sure auth is ready before querying. Queries that run before onAuthStateChanged fires have no request.auth, and will throw the same error even with correct rules.

Prove It with Tests

Run that open-access curl command above and confirm it returns a permission error. Sign in as a second test user and confirm you can't read the first user's order by ID. Firebase Emulator Suite plus @firebase/rules-unit-testing can pin both checks down as tests with assertFails, so the next time an AI edits firestore.rules, the build breaks before your data does.

If admins need to see all orders, don't add a user-writable isAdmin field. Set a custom claim with a Cloud Function's setCustomUserClaims and check request.auth.token.admin == true in the rules.

The difference between resource and request.resource is the most commonly confused point when writing correct rules. resource.data refers to the document that currently exists in the database, and is available in read, update, and delete; request.resource.data refers to the document about to be written by this request, and is available in create and update. This lets you pick the right thing to check per operation: on create there's no old document, so you can only check request.resource.data.ownerId; on update you must check both that the old document belongs to the current user (resource.data.ownerId equals the user) and that the new document doesn't change hands (request.resource.data.ownerId still equals the old value), preventing a document from being "transferred" to someone else. affectedKeys().hasOnly([...]) further restricts which fields an update is allowed to touch, blocking users from tampering with sensitive fields like role or plan. When writing rules, think about "is this reading old data or writing new data" first, then pick the matching variable — this avoids a whole class of logic holes.

FAQ

Q: Is it safe to put the Firebase API key in client-side code? A: Yes. Firebase docs say the Firebase API key can be included in code. That's exactly why security rules must carry the protection: the key identifies your project, it doesn't protect your data.

Q: Is allow read, write: if request.auth != null safe? A: Only if every signed-in user should be able to view and edit every document under that path. For user-owned data, you also need request.auth.uid to match an owner field on the document.

Q: After tightening my rules, why do my queries fail with "Missing or insufficient permissions"? A: Firestore rules are not filters. Add a where() clause that matches the rule condition, like where('ownerId', '==', uid), so the query can only return documents the user has permission to read.

An Even Earlier Line of Defense

If you want this kind of problem caught before delivery, tools like SafeWeave, which plug into Cursor and Claude Code as an MCP server, can help — its Firebase checks flag if true rules (firebase-firestore-open-rules), auth-only writes (firebase-auth-only-write), and open Storage and Realtime Database rules. App URL scans can also check whether live Firestore collections are readable by anyone.

Even without a tool, grepping for if true in firestore.rules plus one emulator test catches most of this article. The key is catching it before the data goes public — the tool matters less than the timing.

One-line Summary

AI editors often loosen Firestore rules to fix permission errors, but the real fix is the query.

Paragraph Summary

The article points out that AI editors like Cursor and Claude Code tend to respond to FirebaseError: Missing or insufficient permissions by changing firestore.rules to allow read, write: if true or requiring only sign-in — the former opens data to everyone, the latter lets any registered user read and write. The real cause is that Firestore rules are not filters and queries are all-or-nothing: the error usually comes from a query missing the where() clause that matches the rules. The right approach is to store the owner uid on every document, compare request.auth.uid to resource.data.ownerId in rules, add where('ownerId', '==', uid) to queries, and wait for auth to be ready. Since the Firebase API key is public anyway, data security rests entirely on rules, so changes like this can be blocked before exposure via emulator assertFails tests or tools like SafeWeave.

#Firestore security rules#Cursor AI editor#Firebase permission error#allow if true#Firestore rules not filters#request.auth.uid#SafeWeave
Share:

Related articles