Firebase Security Report

Rules file: test-fixtures/bad.rules

Scanned: 5/9/2026, 10:33:54 PM

D
Score: 40/100
2
Critical
2
High
0
Medium
1
Low/Info
▸ Active probe disabled. Findings inferred from rules file only. Add --project-id to probe live.

Coverage

6
match blocks
5
allow statements
7
checks run

Findings by severity

Findings (5)

[CRITICAL] Wildcard match `/{document=**}` with `if true` — ALL documents publicly readable/writable

critical

Target: /{document=**} (read,write)

The infamous Firebase data leak pattern. `match /{document=**} { allow read, write: if true; }` opens every collection in the database to anyone with the project ID. Common in dev rules left in production.

Details
{
  "path": "/{document=**}",
  "ops": [
    "read",
    "write"
  ],
  "condition": "true",
  "line": 7
}
Fix snippet (paste into firestore.rules)
// firestore.rules — replace catch-all "if true" with explicit deny + per-collection rules:
match /{document=**} {
  allow read, write: if false;        // explicit default-deny
}
match /users/{uid} {
  allow read, write: if request.auth != null && request.auth.uid == uid;
}
match /publicConfig/{doc} {
  allow read: if true;                 // ONLY read, ONLY this collection
  allow write: if false;
}

[CRITICAL] Rule contains `if true` literal — bypasses all security checks

critical

Target: /publicData/{doc} (read,write)

`allow read, write: if true;` always evaluates to true. Anyone with the project ID can read or write this path without auth. Almost always a leftover from local development.

Details
{
  "path": "/publicData/{doc}",
  "ops": [
    "read",
    "write"
  ],
  "condition": "true",
  "line": 12
}
Fix snippet (paste into firestore.rules)
// firestore.rules line 12:
// Replace 'if true' with an explicit ownership check.
match /publicData/{doc} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
}

[HIGH] Rule allows any signed-in user (`if request.auth != null`) without ownership check

high

Target: /users/{uid} (read,write)

`if request.auth != null` lets any signed-up user read/write the document, including anonymous-auth users. Identical to the PocketBase `@request.auth.id != ""` anti-pattern. Tighten with `request.auth.uid == resource.data.ownerId` or similar ownership check.

Details
{
  "path": "/users/{uid}",
  "ops": [
    "read",
    "write"
  ],
  "condition": "request.auth != null",
  "line": 17
}
Fix snippet (paste into firestore.rules)
// firestore.rules line 17:
// 'request.auth != null' lets ANY signed-up user (including anonymous-auth) read/write.
// Add ownership scoping:
match /users/{uid} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
}

[HIGH] Test mode rules: timestamp-based expiry already passed or about to expire

high

Target: /testMode/{doc} (read,write)

`request.time < timestamp.date(YYYY, MM, DD)` is the default test-mode rule Firebase generates. After the date passes, it becomes effectively `if false` (denies everything) — but BEFORE it passes, it's `if true` (open to all). If the date is in the future, this is wide open right now.

Details
{
  "path": "/testMode/{doc}",
  "ops": [
    "read",
    "write"
  ],
  "condition": "request.time < timestamp.date(2027, 1, 1)",
  "line": 22,
  "expiry_date": "2026-12-31",
  "currently_open": true
}
Fix snippet (paste into firestore.rules)
// firestore.rules line 22:
// Test-mode timestamp rule. EXPIRES 2026-12-31 — currently OPEN.
// Replace with proper auth rules:
match /testMode/{doc} {
  allow read, write: if request.auth != null;  // tighten further per-collection
}

[INFO] No explicit default-deny rule — relying on Firebase implicit deny

info

Target: (end of file)

Firebase denies by default if no rule matches, but having an explicit `match /{document=**} { allow read, write: if false; }` at the bottom is a defensive habit that prevents accidents from rule reorders.

Details
{
  "recommendation": "add `match /{document=**} { allow read, write: if false; }` at end"
}
Fix snippet (paste into firestore.rules)
// firestore.rules — add at end of service block:
match /{document=**} {
  allow read, write: if false;
}

Apply all fixes (paste into firestore.rules)

Review each block before deploying. Then: firebase deploy --only firestore:rules

// Wildcard match `/{document=**}` with `if true` — ALL documents publicly readable/writable (/{document=**} (read,write))
// firestore.rules — replace catch-all "if true" with explicit deny + per-collection rules:
match /{document=**} {
  allow read, write: if false;        // explicit default-deny
}
match /users/{uid} {
  allow read, write: if request.auth != null && request.auth.uid == uid;
}
match /publicConfig/{doc} {
  allow read: if true;                 // ONLY read, ONLY this collection
  allow write: if false;
}

// Rule contains `if true` literal — bypasses all security checks (/publicData/{doc} (read,write))
// firestore.rules line 12:
// Replace 'if true' with an explicit ownership check.
match /publicData/{doc} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
}

// Rule allows any signed-in user (`if request.auth != null`) without ownership check (/users/{uid} (read,write))
// firestore.rules line 17:
// 'request.auth != null' lets ANY signed-up user (including anonymous-auth) read/write.
// Add ownership scoping:
match /users/{uid} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
}

// Test mode rules: timestamp-based expiry already passed or about to expire (/testMode/{doc} (read,write))
// firestore.rules line 22:
// Test-mode timestamp rule. EXPIRES 2026-12-31 — currently OPEN.
// Replace with proper auth rules:
match /testMode/{doc} {
  allow read, write: if request.auth != null;  // tighten further per-collection
}

// No explicit default-deny rule — relying on Firebase implicit deny ((end of file))
// firestore.rules — add at end of service block:
match /{document=**} {
  allow read, write: if false;
}
Generated by firebase-security · Open source (MIT) · Static analysis runs locally; project-id probe sends only an unauthenticated GET.