CVE-2024-21508 (JavaScript, published 2024-04-11T05:15Z)
Description: value: Versions of the package mysql2 before 3.9.4 are vulnerable to Remote Code Execution (RCE) via the readCodeFor function due to improper validation of the supportBigNumbers and bigNumberStrings values.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/sidorares/node-mysql2
Files changed:
lib/parsers/binary_parser.js
lib/parsers/text_parser.js
test/esm/unit/parsers/big-numbers-strings-sanitization.test.mjs
test/esm/unit/parsers/support-big-numbers-sanitization.test.mjs
Commit message: fix(security): improve supportBigNumbers and bigNumberStrings sanitization (#2572)

Fixes a potential RCE attack vulnerability reported by Vsevolod Kokorin (Slonser) of Solidlab

Vulnerable code:
  const supportBigNumbers =
    options.supportBigNumbers || config.supportBigNumbers;
  const bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
  const supportBigNumbers =
    options.supportBigNumbers || config.supportBigNumbers;
  const bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
      string: function(encoding = field.encoding) {
          console.warn(`typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``);
      buffer: function() {
      geometry: function() {
      }
  parserFn('(function () {')(
    'return class TextRow {'
  );
  parserFn("this.packet = packet;");
    parserFn("const result = {};");
    for (let i=0; i < fields.length; i++) {
    for (let i=0; i < resultTablesArray.length; i++) {
        fields[i].table + options.nestTables + fields[i].name
        options
        parserFn(`${lvalue} = options.typeCast(this.wrap${i}, function() { return ${readCode} });`);
      }  else {
      parserFn.toString()
    return parserFn.toFunction({wrap});

Fixed code:
  const supportBigNumbers = Boolean(
    options.supportBigNumbers || config.supportBigNumbers,
  );
  const bigNumberStrings = Boolean(
    options.bigNumberStrings || config.bigNumberStrings,
  );
  const supportBigNumbers = Boolean(
    options.supportBigNumbers || config.supportBigNumbers,
  );
  const bigNumberStrings = Boolean(
    options.bigNumberStrings || config.bigNumberStrings,
  );
      string: function (encoding = field.encoding) {
          console.warn(
            `typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``,
          );
      buffer: function () {
      geometry: function () {
      },
  parserFn('(function () {')('return class TextRow {');
  parserFn('this.packet = packet;');
    parserFn('const result = {};');
    for (let i = 0; i < fields.length; i++) {
    for (let i = 0; i < resultTablesArray.length; i++) {
        fields[i].table + options.nestTables + fields[i].name,
        options,
        parserFn(
          `${lvalue} = options.typeCast(this.wrap${i}, function() { return ${readCode} });`,
        );
      } else {
      parserFn.toString(),
    return parserFn.toFunction({ wrap });
import { describe, test, assert } from 'poku';
import { createConnection, describeOptions } from '../../../common.test.cjs';

const connection = createConnection().promise();

const sql = 'SELECT 9007199254740991+100 AS `total`';

describe('bigNumberStrings Sanitization', describeOptions);

Promise.all([
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: true,
      bigNumberStrings: true,
    });

    assert.strictEqual(
      typeof results[0].total,
      'string',
      'Valid bigNumberStrings enabled',
    );
  }),
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: false,
      bigNumberStrings: false,
    });

    assert.strictEqual(
      typeof results[0].total,
      'number',
      'Valid bigNumberStrings disabled',
    );
  }),

  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: 'text',
      bigNumberStrings: 'text',
    });

    assert.strictEqual(
      typeof results[0].total,
      'string',
      'bigNumberStrings as a random string should be enabled',
    );
  }),
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: '',
      bigNumberStrings: '',
    });

    assert.strictEqual(
      typeof results[0].total,
      'number',
      'bigNumberStrings as an empty string should be disabled',
    );
  }),
]).then(async () => {
  await connection.end();
});
import { describe, test, assert } from 'poku';
import { createConnection, describeOptions } from '../../../common.test.cjs';

const connection = createConnection().promise();

const sql = 'SELECT 9007199254740991+100 AS `total`';

describe('supportBigNumbers Sanitization', describeOptions);

Promise.all([
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: true,
    });

    assert.strictEqual(
      typeof results[0].total,
      'string',
      'Valid supportBigNumbers enabled',
    );
  }),
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: false,
    });

    assert.strictEqual(
      typeof results[0].total,
      'number',
      'Valid supportBigNumbers disabled',
    );
  }),

  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: 'text',
    });

    assert.strictEqual(
      typeof results[0].total,
      'string',
      'supportBigNumbers as a random string should be enabled',
    );
  }),
  test(async () => {
    const [results] = await connection.query({
      sql,
      supportBigNumbers: '',
    });

    assert.strictEqual(
      typeof results[0].total,
      'number',
      'supportBigNumbers as an empty string should be disabled',
    );
  }),
]).then(async () => {
  await connection.end();
});