Never Build Database Queries with String Concatenation
Impact: CRITICAL (prevents SQL injection and NoSQL injection attacks)
Constructing database queries by concatenating or interpolating user input creates injection vulnerabilities. This applies to raw SQL, ORM queries with raw segments, and NoSQL operations. Always use parameterized queries or ORM methods that handle escaping.
Incorrect (string interpolation in raw SQL):
// ❌ Direct interpolation — classic SQL injection vectorexport async function getUser(name: string) { const result = await prisma.$queryRaw` SELECT * FROM users WHERE name = '${name}' ` return result}// ❌ String concatenation in query builderexport async function searchProducts(term: string) { const query = `SELECT * FROM products WHERE name LIKE '%${term}%'` const result = await db.execute(query) return result}
Correct (parameterized queries):
// ✅ Prisma tagged template (auto-parameterized)export async function getUser(name: string) { const result = await prisma.$queryRaw( Prisma.sql`SELECT * FROM users WHERE name = ${name}` ) return result}// ✅ Better: use the ORM's query builderexport async function getUser(name: string) { return prisma.user.findFirst({ where: { name }, })}// ✅ Drizzle parameterized queryexport async function searchProducts(term: string) { return db .select() .from(products) .where(like(products.name, `%${term}%`))}
Also watch for Supabase:
// ❌ String interpolation in Supabase RPCconst { data } = await supabase.rpc('search_users', { query: `%${userInput}%` // Could be safe depending on the function, but risky pattern})// ✅ Use Supabase query builderconst { data } = await supabase .from('users') .select('*') .ilike('name', `%${userInput}%`) // Properly parameterized by the SDK