"I vibe coded and shipped an app in three days. It got hacked. Twice."

1 day ago 5

Harley Kimball Profile picture

I vibe coded and shipped an app in three days. It got hacked. Twice. Here’s what I learned. 🧵

The app is a directory that aggregates security research profiles across various platforms like @Hacker0x01 , @Bugcrowd , @github , and more. The frontend connects to a Supabase instance, and I built it using Lovable and Cursor. All automation that feeds data into the database is handled separately. disclosedonline.com/directory

Originally, I planned to allow users to self-register using Supabase Auth and submit the profiles they wanted aggregated. As I started implementing this, I realized it would introduce risk as I'd need to manage both authentication (who users are) and authorization (what they're allowed to do) properly. I decided to scrap self-registration... but I missed something important. (More on that later.)

Bug #1: Leaking Emails:

In my first test, users were authenticated via Supabase, which required real email addresses. While inspecting network requests, I discovered that responses from the profiles table were exposing user email addresses, which is obviously bad if users ever trusted me with private info.

To fix it, I created a PostgreSQL view that queried only the fields I needed from profiles, excluding email. I updated the frontend to query that view instead. Seemed more secure… but I accidentally made things worse. (More on that soon.)

Launch & First Compromise:

After switching to the view and removing frontend insertion, I launched the app. I had Row-Level Security (RLS) enabled and thought read-only access meant I was safe.

Within 24 hours, a researcher showed they could insert, update, and delete records, even though the frontend didn’t expose those endpoints and RLS should have blocked it.

Why? The view I created didn’t respect RLS.

By default, PostgreSQL views execute with the privileges of the view owner. In my case, this was an admin role. Unless you define the view as SECURITY INVOKER, or apply extra guards, RLS policies on underlying tables are bypassed. That was the critical misconfiguration. I fixed it by revoking unsafe access to the view and restructuring how data is queried. Shoutout to the researcher who responsibly disclosed it (@Goofygiraffe06).

Second Compromise:

A day later, another researcher (@Kr1shna4garwal) reached out: they were able to create new profiles in the database, though (thankfully) they couldn’t modify or delete existing ones.

This wasn’t due to the view. Instead, I had left Supabase Auth enabled, even though I had removed self-registration from the frontend. That meant attackers could still register via email/password and act as authenticated users, subject to whatever RLS rules existed. After disabling sign-ups in Supabase’s Auth settings, the issue was resolved.

Key Takeaways:

Vibe coding ships fast, but security holes are the default.

Supabase + Postgres are powerful, but it’s easy to misconfigure them if you don’t understand their security model, especially around views and RLS.

If you use PostgreSQL views, remember: they don’t automatically respect RLS policies.

Disable Supabase Auth if you're not using it - even if your frontend doesn’t expose it.

Threat modeling matters. My app only handled public data, so the impact was low. If you’re handling PII/PHI, this kind of exposure could be catastrophic.

If you enjoyed this post, check out my newsletter: weekly bug bounty writeups, tools, and more: .getdisclosed.com

• • •

Missing some Tweet in this thread? You can try to force a refresh

Read Entire Article