DEV Community

Charles Kern
Charles Kern

Posted on Originally published at safeweave.dev

Why Cursor Fixes Firestore Permission Errors by Opening the Rules

TL;DR

  • When Firestore throws FirebaseError: Missing or insufficient permissions, AI editors often "fix" it by rewriting firestore.rules to allow read, write: if true; or if request.auth != null;.
  • Your Firebase config ships in every browser bundle by design, so open rules mean anyone can read or overwrite the data, and auth-only rules mean any signed-up user can.
  • The real fix is usually in the query, not the rules: scope rules to the document owner, then add the matching where() so the query fits the rules.

You ask Cursor or Claude Code to add an orders page to a Firebase app. You load it and the console says FirebaseError: Missing or insufficient permissions. You paste the error back into chat.

The page loads on the next try. The diff is one file, firestore.rules, and it now lets everyone in.

This is the same move AI editors make with Supabase RLS errors and Stripe signature errors. The error is the guard doing its job, and the quickest way to make it go away is to remove the guard.

The Vulnerable Code

The bug is a security rule with no ownership condition. AI editors usually write one of two versions, and both make the error disappear.

The blunt one:

// 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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Firebase's own "Avoid insecure rules" page shows this exact ruleset with the warning "NEVER use this ruleset in production." It means anyone who knows your project ID can read, change, or delete every document.

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;
}
Enter fullscreen mode Exit fullscreen mode

This one feels like a fix because it mentions auth. It is also an example on Firebase's own "Get started with Cloud Firestore Security Rules" page, which is part of why it shows up so often. But Firebase's insecure-rules page lists it under "Not recommended: Any logged-in user has read and write access to your entire database." Anyone can create an account in your app, so anyone can read every order and edit anyone else's.

Your project ID is not a secret. Firebase's API key docs say Firebase API keys are "OK to include in code or checked-in config files" because access to data is controlled by Security Rules and App Check. So with if true, this is the whole attack:

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

No key, no token. The response is your orders collection.

Why This Keeps Happening

AI editors open the rules because the error message points at the rules, while the real cause is usually the query. Firestore rules are not filters. Firebase's "Securely query data" docs say it directly: security rules are not filters, and queries are all or nothing. If a query could return even one document the user is not allowed to read, the whole query fails.

So when a developer writes good owner-only rules and the AI writes this query:

const snap = await getDocs(collection(db, 'orders'));
Enter fullscreen mode Exit fullscreen mode

Firestore rejects it. The query asks for every order, and the rules only allow your own. The error is correct. But the model sees "insufficient permissions" next to a rules file and widens the rules until the query passes.

Two more things push it that way. When you create a Firestore database, Firebase offers a test mode that grants access to all users, so plenty of public projects and tutorials start from open rules. And GitHub issues about this error regularly include "my rules are already set to if true" as a debugging step, which makes if true look like a normal thing to try.

Nothing in the editor flags any of it. The rules file is not JavaScript, there is no type error, and the app works.

The Fix

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

The 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']);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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

The query that fits those 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 });
Enter fullscreen mode Exit fullscreen mode

Make sure you wait for auth before querying. A query that runs before onAuthStateChanged fires has no request.auth, which produces the same error even with correct rules.

Then prove it. Run the open-access curl above against your project and confirm it returns a permission error. Sign in as a second test user and confirm you cannot read the first user's order by ID. The Firebase Emulator Suite with @firebase/rules-unit-testing lets you keep both checks as tests with assertFails, so the next AI edit to firestore.rules breaks the build instead of your data.

If an admin needs to see every order, do not add an isAdmin field users can write. Set a custom claim from a Cloud Function with setCustomUserClaims and check request.auth.token.admin == true in the rule.

FAQ

Q: Is it safe to have my Firebase API key in client-side code?
A: Yes. Firebase's docs say Firebase API keys are OK to include in code. That is exactly why your Security Rules have to do the work: the key identifies your project, it does not protect your data.

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

Q: Why does my query fail with "Missing or insufficient permissions" after I tightened the rules?
A: Firestore rules are not filters. Add a where() clause that matches the rule's condition, for example where('ownerId', '==', uid), so the query can only return documents the user is allowed to read.

Want this caught before it ships? SafeWeave hooks into Cursor and Claude Code as an MCP server, and its Firebase checks on Cloud plans flag if true rules (firebase-firestore-open-rules), auth-only writes (firebase-auth-only-write), and open Storage and Realtime Database rules. An App URL scan also checks whether your live Firestore collections are readable by anyone. Even a pre-commit grep for if true in firestore.rules plus one emulator test will catch most of what's in this post. The important thing is catching it before your data is public, whatever tool you use.

Top comments (0)