#0306
A user-import feature accepts an array of { name, email } records and builds a single bulk INSERT statement for efficiency.
The buggy implementation concatenates every name and email directly into the VALUES list. Any injected quote in a name or email can break out of the string literal and execute arbitrary SQL.
A single malicious record like { name: "x'); DROP TABLE users;--", email: "e@f.com" } can destroy the entire table at import time.
Batch inserts are often written once and rarely reviewed. A single un-parameterized bulk import can give an attacker the ability to inject SQL into thousands of rows simultaneously, or drop critical tables during a data migration.
buildBatchInsert so every value is passed as a parameter rather than concatenated inline.($1,$2),($3,$4),... for each record.params array must contain all values flattened in order: [name1, email1, name2, email2, ...].{ sql: "INSERT INTO users (name,email) VALUES ", params: [] }.buildBatchInsert([{name:"x'); DROP TABLE users;--", email:"e@f.com"}])// FIX → { sql: '...VALUES ($1,$2)', params: ["x'); DROP...", 'e@f.com'] }
buildBatchInsert([{name:'alice',email:'a@b.com'},{name:'bob',email:'b@c.com'}])// → { sql: '...VALUES ($1,$2),($3,$4)', params: ['alice','a@b.com','bob','b@c.com'] }
sql must use sequential $N placeholders starting at $1.