Lab Note

Vibe Check

Building fast often comes at the price of misconfigurations. These misconfigurations in your app can have significant security implications down the road. In 2025 and early 2026, two high-profile incidents proved this point:

Moltbook (Supabase)

Security researchers discovered that the viral “social network for AI agents” had its entire Supabase production database exposed. The anon key was sitting in client-side JavaScript. With no Row Level Security enabled on the tables, anyone could read and write millions of records — including 1.5M+ API keys, private messages, and user data.

Tea App (Firebase)

Just months earlier, the women’s dating/safety app Tea suffered a major breach when a Firebase Storage bucket was left publicly accessible. Approximately 72,000 images were exposed, including ~13,000 verification selfies containing driver’s licenses and passport photos, plus images from old posts and direct messages. The bucket (a legacy system from a data migration) had no proper authentication or access controls. It was discovered after someone posted a direct download link on 4chan.

Both incidents were preventable with basic configuration hygiene.

Both were found by people simply poking at public endpoints and storage buckets. Neither required advanced exploits or “vibe-coded” AI gone rogue.

These stories matter especially to Flutter developers, because the two most popular backend choices in the Flutter ecosystem — Supabase and Firebase — make it dangerously easy to ship with exactly these kinds of misconfigurations.

Why Flutter Developers Are Particularly Exposed

supabase_flutter and the official Firebase packages make it easy to build powerful apps. But they rely on client-side keys and rules. When Row Level Security is missing in Supabase, or when Firebase Storage buckets or Realtime Database rules are left open, your users’ data becomes accessible to anyone who can extract the config from your app. This is why external verification and proper rules matter.

Practical Tool:

Posted on my Github are a series of bash scripts intendeded to help Flutter developers with a basic "vibe check" of their app's database security configuration. You can find them here:

https://github.com/digitalonyx/vibe-check

Secure Firebase Storage Rules (Prevent Another Tea Incident)

The Tea breach happened because a Firebase Storage bucket was left publicly readable with no authentication. Here are production-ready rules that would have blocked it.

Recommended Secure Baseline

service firebase.storage {
  match /b/{bucket}/o {

    // Users can only access their own files
    match /users/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null 
                          && request.auth.uid == userId;
    }

    // Public assets (avatars, public media) — read-only for everyone
    match /public/{allPaths=**} {
      allow read: if true;
      allow write: if false;           // or restrict to admins
    }

    // Everything else is denied by default
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

Strict Rules for Sensitive Data (Verification Photos, IDs, Private Media)

Use this pattern for anything high-risk like the Tea verification images:

service firebase.storage {
  match /b/{bucket}/o {

    // === HIGHLY SENSITIVE FILES (verification, IDs, private docs) ===
    match /users/{userId}/verification/{allPaths=**} {
      allow read, write: if request.auth != null 
                          && request.auth.uid == userId;
      
      // Extra hardening (recommended)
      // allow write: if request.auth.uid == userId
      //              && request.resource.size < 10 * 1024 * 1024
      //              && request.resource.contentType.matches('image/.*');
    }

    // === Normal user-generated content ===
    match /users/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null 
                          && request.auth.uid == userId;
    }

    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

Write-Once Verification Pattern (Extra Protection)

match /users/{userId}/verification/{fileName} {
  allow read: if request.auth != null && request.auth.uid == userId;
  allow write: if request.auth != null 
               && request.auth.uid == userId
               && !resource.exists;     // prevents overwriting
}

How to Apply These Rules

Fastest way:

Firebase Console → Storage → Rules tab Paste the rules Click Publish

Recommended (version controlled): firebase deploy --only storage

In your Flutter code, structure uploads like this so the rules work cleanly:

final ref = FirebaseStorage.instance
    .ref('users/${FirebaseAuth.instance.currentUser!.uid}/verification/photo.jpg');
await ref.putFile(file);

Recommended Flutter + Backend Security Habits

  • Run the Supabase and Firebase checker scripts before every internal release.

  • Structure Storage paths as users/{uid}/... (or users/{uid}/verification/... for sensitive files).

  • Start with the strict rules above and only open paths when you have a clear reason.

  • Use the Rules Playground in the Firebase Console to test before deploying.

  • When using AI to generate rules or schema, ask it to also output the matching security rules + verification commands.

// SIGNALS RECEIVED

Sniffing decentralized network packets...

// SEND A SIGNAL

Have you written a response to this post? Paste its URL below to send a Webmention: