Update Fix
This commit is contained in:
61
node_modules/mysql2/lib/packets/handshake_response.js
generated
vendored
61
node_modules/mysql2/lib/packets/handshake_response.js
generated
vendored
@@ -16,22 +16,47 @@ class HandshakeResponse {
|
||||
this.authPluginData2 = handshake.authPluginData2;
|
||||
this.compress = handshake.compress;
|
||||
this.clientFlags = handshake.flags;
|
||||
// TODO: pre-4.1 auth support
|
||||
let authToken;
|
||||
if (this.passwordSha1) {
|
||||
authToken = auth41.calculateTokenFromPasswordSha(
|
||||
this.passwordSha1,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
|
||||
// Accept pre-calculated authToken and authPluginName from caller
|
||||
// This allows the caller to optimize by using the server's preferred auth method
|
||||
if (
|
||||
handshake.authToken !== undefined &&
|
||||
handshake.authPluginName !== undefined
|
||||
) {
|
||||
// Validate types to fail fast with clear errors
|
||||
if (!Buffer.isBuffer(handshake.authToken)) {
|
||||
throw new TypeError(
|
||||
'HandshakeResponse authToken must be a Buffer when provided'
|
||||
);
|
||||
}
|
||||
if (typeof handshake.authPluginName !== 'string') {
|
||||
throw new TypeError(
|
||||
'HandshakeResponse authPluginName must be a string when provided'
|
||||
);
|
||||
}
|
||||
this.authToken = handshake.authToken;
|
||||
this.authPluginName = handshake.authPluginName;
|
||||
} else {
|
||||
authToken = auth41.calculateToken(
|
||||
this.password,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
// Fallback to legacy behavior: calculate mysql_native_password token
|
||||
// TODO: pre-4.1 auth support
|
||||
let authToken;
|
||||
if (this.passwordSha1) {
|
||||
authToken = auth41.calculateTokenFromPasswordSha(
|
||||
this.passwordSha1,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
} else {
|
||||
authToken = auth41.calculateToken(
|
||||
this.password,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
}
|
||||
this.authToken = authToken;
|
||||
this.authPluginName = 'mysql_native_password';
|
||||
}
|
||||
this.authToken = authToken;
|
||||
|
||||
this.charsetNumber = handshake.charsetNumber;
|
||||
this.encoding = CharsetToEncoding[handshake.charsetNumber];
|
||||
this.connectAttributes = handshake.connectAttributes;
|
||||
@@ -62,8 +87,12 @@ class HandshakeResponse {
|
||||
packet.writeNullTerminatedString(this.database, encoding);
|
||||
}
|
||||
if (isSet('PLUGIN_AUTH')) {
|
||||
// TODO: pass from config
|
||||
packet.writeNullTerminatedString('mysql_native_password', 'latin1');
|
||||
// Use the auth plugin name specified by the caller (optimized for server's preference)
|
||||
// or fall back to mysql_native_password for backward compatibility
|
||||
packet.writeNullTerminatedString(
|
||||
this.authPluginName || 'mysql_native_password',
|
||||
'latin1'
|
||||
);
|
||||
}
|
||||
if (isSet('CONNECT_ATTRS')) {
|
||||
const connectAttributes = this.connectAttributes || {};
|
||||
|
||||
82
node_modules/mysql2/lib/packets/packet.js
generated
vendored
82
node_modules/mysql2/lib/packets/packet.js
generated
vendored
@@ -406,8 +406,8 @@ class Packet {
|
||||
readNullTerminatedString(encoding) {
|
||||
const start = this.offset;
|
||||
let end = this.offset;
|
||||
while (this.buffer[end]) {
|
||||
end = end + 1; // TODO: handle OOB check
|
||||
while (end < this.end && this.buffer[end] !== 0x00) {
|
||||
end = end + 1;
|
||||
}
|
||||
this.offset = end + 1;
|
||||
return StringParser.decode(this.buffer, encoding, start, end);
|
||||
@@ -516,16 +516,20 @@ class Packet {
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
// copy-paste from https://github.com/mysqljs/mysql/blob/master/lib/protocol/Parser.js
|
||||
// adapted from https://github.com/mysqljs/mysql/blob/dc9c152a87ec51a1f647447268917243d2eab1fd/lib/protocol/Parser.js
|
||||
parseGeometryValue() {
|
||||
const buffer = this.readLengthCodedBuffer();
|
||||
let offset = 4;
|
||||
if (buffer === null || !buffer.length) {
|
||||
return null;
|
||||
}
|
||||
const bufferLength = buffer.length;
|
||||
function parseGeometry() {
|
||||
let x, y, i, j, numPoints, line;
|
||||
let x, y, i, j, numPoints, numRings, num, line;
|
||||
let result = null;
|
||||
if (offset + 5 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
const byteOrder = buffer.readUInt8(offset);
|
||||
offset += 1;
|
||||
const wkbType = byteOrder
|
||||
@@ -534,6 +538,9 @@ class Packet {
|
||||
offset += 4;
|
||||
switch (wkbType) {
|
||||
case 1: // WKBPoint
|
||||
if (offset + 16 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -545,12 +552,21 @@ class Packet {
|
||||
result = { x: x, y: y };
|
||||
break;
|
||||
case 2: // WKBLineString
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
numPoints = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (numPoints > (bufferLength - offset) / 16) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = numPoints; i > 0; i--) {
|
||||
if (offset + 16 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -563,19 +579,30 @@ class Packet {
|
||||
}
|
||||
break;
|
||||
case 3: // WKBPolygon
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const numRings = byteOrder
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
numRings = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (numRings > (bufferLength - offset) / 4) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = numRings; i > 0; i--) {
|
||||
if (offset + 4 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
numPoints = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
line = [];
|
||||
for (j = numPoints; j > 0; j--) {
|
||||
if (offset + 16 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -593,11 +620,16 @@ class Packet {
|
||||
case 5: // WKBMultiLineString
|
||||
case 6: // WKBMultiPolygon
|
||||
case 7: // WKBGeometryCollection
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const num = byteOrder
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
num = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (num > (bufferLength - offset) / 9) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = num; i > 0; i--) {
|
||||
result.push(parseGeometry());
|
||||
@@ -660,14 +692,27 @@ class Packet {
|
||||
if (len === null) {
|
||||
return null;
|
||||
}
|
||||
if (len === 0) {
|
||||
return 0; // TODO: assert? exception?
|
||||
}
|
||||
|
||||
// For numbers with many digits (>17), use built-in parseFloat to avoid
|
||||
// precision loss from accumulated rounding errors in repeated *10 operations.
|
||||
// This fixes issues #2928 (MAX_VALUE doubles) and #3690 (DECIMAL(36,18))
|
||||
// where very large numbers or numbers with many fractional digits lose precision.
|
||||
// The threshold of 17 is based on IEEE 754 double precision (~15-17 significant digits).
|
||||
// Testing shows minimal performance impact as most real-world numbers are shorter.
|
||||
if (len > 17) {
|
||||
const str = this.buffer.toString('utf8', this.offset, this.offset + len);
|
||||
this.offset += len;
|
||||
return Number.parseFloat(str);
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
const end = this.offset + len;
|
||||
let factor = 1;
|
||||
let pastDot = false;
|
||||
let charCode = 0;
|
||||
if (len === 0) {
|
||||
return 0; // TODO: assert? exception?
|
||||
}
|
||||
if (this.buffer[this.offset] === minus) {
|
||||
this.offset++;
|
||||
factor = -1;
|
||||
@@ -681,9 +726,13 @@ class Packet {
|
||||
pastDot = true;
|
||||
this.offset++;
|
||||
} else if (charCode === exponent || charCode === exponentCapital) {
|
||||
this.offset++;
|
||||
const exponentValue = this.parseInt(end - this.offset);
|
||||
return (result / factor) * Math.pow(10, exponentValue);
|
||||
// Scientific notation detected - bail out to parseFloat for exact match.
|
||||
// Manual calculation with Math.pow(10, exp) cannot match parseFloat()
|
||||
// exactly for most non-zero exponents due to accumulated rounding errors.
|
||||
const start = end - len;
|
||||
const str = this.buffer.toString('utf8', start, end);
|
||||
this.offset = end;
|
||||
return Number.parseFloat(str);
|
||||
} else {
|
||||
result *= 10;
|
||||
result += this.buffer[this.offset] - 48;
|
||||
@@ -831,11 +880,10 @@ class Packet {
|
||||
if (n === null) {
|
||||
return this.writeInt8(0xfb);
|
||||
}
|
||||
// TODO: check that n is out of int precision
|
||||
this.writeInt8(0xfe);
|
||||
this.buffer.writeUInt32LE(n, this.offset);
|
||||
this.buffer.writeUInt32LE(n >>> 0, this.offset);
|
||||
this.offset += 4;
|
||||
this.buffer.writeUInt32LE(n >> 32, this.offset);
|
||||
this.buffer.writeUInt32LE(Math.floor(n / 0x100000000), this.offset);
|
||||
this.offset += 4;
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user