Skip to content

Can My Vibe-Coded App Get Hacked by SQL Injection?

5 min read

Can your vibe-coded app get hacked by SQL injection? Only if your code builds database queries by gluing user input into a text string. If your agent used a database client library (Supabase, Prisma, Drizzle, the standard Postgres or MySQL drivers) the normal way, you are almost certainly safe already, because those tools send user input separately from the query. The risk shows up in the handful of places where a query was written as raw text with variables mixed in.

Why this happens

SQL injection is old, and every modern library is built to prevent it. So why worry at all? Because an AI agent writes whatever gets the feature working, and sometimes the quickest path is a raw SQL string. If you asked for "search products by name" or "let admins run a custom report," the agent may have written something like "SELECT * FROM products WHERE name = '" + search + "'". That works perfectly in the demo. It also means a visitor who types ' OR '1'='1 into your search box can rewrite your query, and a more careful attacker can read or delete other tables entirely.

The agent built exactly what you asked for. Nobody asked "and make sure a hostile string in that box can't become a command," so nobody did.

How to check

You are looking for query strings that contain your variables, not for any particular library. Search your codebase:

grep -rniE "SELECT|INSERT|UPDATE|DELETE|WHERE" src/ | grep -E "\\+|\\$\\{|%s|f\"|f'|\\.format"

That surfaces raw SQL lines where a value is being concatenated or interpolated in. Then look for the deliberate escape hatches your ORM provides:

  • Prisma: $queryRawUnsafe, or a $queryRaw built from a plain string.
  • Supabase: an .rpc() call whose SQL function bodies concatenate arguments.
  • Node Postgres / MySQL: client.query("..." + value) instead of a values array.
  • Python: cursor.execute(f"... {value} ...") instead of passing params.

Any of these mixing user input into the text is the thing to fix. A query that passes values in a separate argument is fine.

The fix

  1. Use parameters, not string building. Give the driver placeholders and hand it the values separately. It quotes and escapes them for you.

    // vulnerable
    client.query("SELECT * FROM users WHERE email = '" + email + "'")
    
    // safe
    client.query("SELECT * FROM users WHERE email = $1", [email])
    
  2. On Prisma, prefer prisma.user.findMany({ where: { email } }). If you need raw SQL, use the tagged-template prisma.$queryRaw`...${email}...`, which parameterizes automatically, and never $queryRawUnsafe.

  3. In database functions (Supabase RPC), build dynamic SQL with format() and %L/%I, or use USING parameters, rather than concatenating text.

  4. Re-run the grep and confirm no user input remains inside a query string. Then test the fixed field by typing a single quote (') into it. A safe app treats it as ordinary text; a vulnerable one throws a SQL error.

The trap to avoid

Do not try to fix this by stripping quotes or blocking words like DROP and SELECT. Input filtering looks reassuring and misses cases endlessly, because attackers have decades of ways around it. Parameterized queries are the actual fix, and they are less code than the filter you were about to write. Escaping by hand is the same mistake in a different hat.

Where this fits

Raw-string queries are rare in a vibe-coded app but not zero, and the one that exists is usually in a search box or an admin tool, exactly where a curious beta user will poke. The free Readiness Report scans your code for query strings that mix in user input and points you at the specific lines, and the Finishing Pass rewrites them as parameterized queries for you. It sits alongside the same class of check as changing the user ID in an API request and who can read your database, all part of knowing whether the app is secure enough to launch.