CVE-2023-34232: Improper Neutralization of Special Elements used in a Command ('Command Injection') (CWE-77)
value: snowflake-connector-nodejs, a NodeJS driver for Snowflake, is vulnerable to command injection via single sign on (SSO) browser URL authentication in versions prior to 1.6.21. In order to exploit the potential for command injection, an attacker would need to be successful in (1) establishing a malicious resource and (2) redirecting users to utilize the resource. The attacker could set up a malicious, publicly accessible server which responds to the SSO URL with an attack payload. If the attacker then tricked a user into visiting the maliciously crafted connection URL, the user’s local machine would render the malicious payload, leading to a remote code execution. This attack scenario can be mitigated through URL whitelisting as well as common anti-phishing resources. Version 1.6.21 contains a patch for this issue.
CWE description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2023-06-08T21:15Z
Repository: https://github.com/snowflakedb/snowflake-connector-nodejs
Language: JavaScript
Files changed: lib/connection/result/column.js, package.json, test/integration/testDataType.js, test/integration/testUtil.js, test/unit/connection/result/result_test_variant.js
Commit date: 2023-04-17 11:11:20 -0700
Commit message: Replace eval() for parsing JSON strings (#465)

* Replace eval() with JSON.parse() for parsing JSON strings

* Replace eval with safe-eval for parsing JSON strings

* Replace eval with better-eval for parsing JSON strings

* Use assert without strict for comparing objects created by better-eval

* Use assert without strict for comparing objects created by better-eval

* Use assert without strict for comparing objects created by better-eval

---------

Co-authored-by: ilesh garish <111810784+sfc-gh-igarish@users.noreply.github.com>
Diff: diff --git a/lib/connection/result/column.js b/lib/connection/result/column.js
index 3a363db..91dd673 100644
--- a/lib/connection/result/column.js
+++ b/lib/connection/result/column.js
@@ -7,10 +7,11 @@ var Errors = require('../../errors');
 var BigNumber = require('bignumber.js');
 var SfTimestamp = require('./sf_timestamp');
 var SqlTypes = require('./data_types').SqlTypes;
 var bigInt = require('big-integer');
 var { XMLParser, XMLValidator } = require("fast-xml-parser");
+var betterEval = require("better-eval");
 
 var NULL_UPPERCASE = 'NULL';
 
 /**
  * Creates a new Column.
@@ -549,11 +550,11 @@ function convertRawVariant(rawColumnValue, column, context)
   // if the input is a non-empty string, convert it to a json object
   if (Util.string.isNotNullOrEmpty(rawColumnValue))
   {
     try
     {
-      ret = eval("(" + rawColumnValue + ")");
+      ret = betterEval("(" + rawColumnValue + ")");
     }
     catch (parseError)
     {
       // check if raw string is in XML format
       // ensure each tag is enclosed and all attributes and elements are valid
diff --git a/package.json b/package.json
index 2c2f7ba..6236f6d 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,11 @@
     "asn1.js-rfc2560": "^5.0.0",
     "asn1.js-rfc5280": "^3.0.0",
 	"async": "^3.2.3",
     "aws-sdk": "^2.878.0",
     "axios": "^0.27.2",
+    "better-eval": "^1.3.0",
     "big-integer": "^1.6.43",
     "bignumber.js": "^2.4.0",
     "binascii": "0.0.2",
     "browser-request": "^0.3.3",
     "debug": "^3.2.6",
diff --git a/test/integration/testDataType.js b/test/integration/testDataType.js
index 18e358a..e3dface 100644
--- a/test/integration/testDataType.js
+++ b/test/integration/testDataType.js
@@ -232,11 +232,14 @@ describe('Test DataType', function ()
           {
             testUtil.executeQueryAndVerify(
               connection,
               selectVariant,
               [{'COLA': {a: 1, b: [1, 2, 3, -Infinity, undefined], c: {a: 1}}}],
-              callback
+              callback,
+              null,
+              true,
+              false
             );
           }],
         done
       );
     });
@@ -257,11 +260,14 @@ describe('Test DataType', function ()
           {
             testUtil.executeQueryAndVerify(
               connection,
               selectArray,
               [{'COLA': ['a', 1]}],
-              callback
+              callback,
+              null,
+              true,
+              false
             );
           }],
         done
       );
     });
diff --git a/test/integration/testUtil.js b/test/integration/testUtil.js
index 56b6482..4c661ea 100644
--- a/test/integration/testUtil.js
+++ b/test/integration/testUtil.js
@@ -49,14 +49,15 @@ module.exports.executeCmd = function (connection, sql, callback, bindArray)
 module.exports.checkError = function (err)
 {
   assert.ok(!err, JSON.stringify(err));
 };
 
-module.exports.executeQueryAndVerify = function (connection, sql, expected, callback, bindArray, normalize)
+module.exports.executeQueryAndVerify = function (connection, sql, expected, callback, bindArray, normalize, strict)
 {
   // Sometimes we may not want to normalize the row first
   normalize = (typeof normalize !== "undefined" && normalize != null) ? normalize : true;
+  strict = (typeof strict !== "undefined" && strict != null) ? strict : true;
   var executeOptions = {};
   executeOptions.sqlText = sql;
   executeOptions.complete = function (err, stmt)
   {
     assert.ok(!err, JSON.stringify(err));
@@ -65,11 +66,18 @@ module.exports.executeQueryAndVerify = function (connection, sql, expected, call
     stream.on('readable', function ()
     {
       var row;
       while ((row = stream.read()) !== null)
       {
-        assert.deepStrictEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
+        if (strict)
+        {
+          assert.deepStrictEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
+        }
+        else
+        {
+          assert.deepEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
+        }
         rowCount++;
       }
     });
     stream.on('error', function (err)
     {
diff --git a/test/unit/connection/result/result_test_variant.js b/test/unit/connection/result/result_test_variant.js
index 2127508..64ea37c 100644
--- a/test/unit/connection/result/result_test_variant.js
+++ b/test/unit/connection/result/result_test_variant.js
@@ -57,22 +57,22 @@ describe('Result: test variant', function ()
       ResultTestCommon.testResult(
         ResultTestCommon.createResultOptions(response),
         function (row)
         {
           // variant
-          assert.deepStrictEqual(row.getColumnValue('C1'), {a: 1});
-          assert.strictEqual(
+          assert.deepEqual(row.getColumnValue('C1'), {a: 1});
+          assert.equal(
             row.getColumnValueAsString('C1'), JSON.stringify({a: 1}));
 
           // object
-          assert.deepStrictEqual(row.getColumnValue('C2'), {a: 1});
-          assert.strictEqual(
+          assert.deepEqual(row.getColumnValue('C2'), {a: 1});
+          assert.equal(
             row.getColumnValueAsString('C2'), JSON.stringify({a: 1}));
 
           // array
-          assert.deepStrictEqual(row.getColumnValue('C3'), [1, 2]);
-          assert.strictEqual(
+          assert.deepEqual(row.getColumnValue('C3'), [1, 2]);
+          assert.equal(
             row.getColumnValueAsString('C3'), JSON.stringify([1, 2]));
         },
         function (result)
         {
           done();

Vulnerable code:       ret = eval("(" + rawColumnValue + ")");
              callback
              callback
module.exports.executeQueryAndVerify = function (connection, sql, expected, callback, bindArray, normalize)
        assert.deepStrictEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
          assert.deepStrictEqual(row.getColumnValue('C1'), {a: 1});
          assert.strictEqual(
          assert.deepStrictEqual(row.getColumnValue('C2'), {a: 1});
          assert.strictEqual(
          assert.deepStrictEqual(row.getColumnValue('C3'), [1, 2]);
          assert.strictEqual(
Fixed code: var betterEval = require("better-eval");
      ret = betterEval("(" + rawColumnValue + ")");
    "better-eval": "^1.3.0",
              callback,
              null,
              true,
              false
              callback,
              null,
              true,
              false
module.exports.executeQueryAndVerify = function (connection, sql, expected, callback, bindArray, normalize, strict)
  strict = (typeof strict !== "undefined" && strict != null) ? strict : true;
        if (strict)
        {
          assert.deepStrictEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
        }
        else
        {
          assert.deepEqual(normalize ? normalizeRowObject(row) : row, expected[rowCount]);
        }
          assert.deepEqual(row.getColumnValue('C1'), {a: 1});
          assert.equal(
          assert.deepEqual(row.getColumnValue('C2'), {a: 1});
          assert.equal(
          assert.deepEqual(row.getColumnValue('C3'), [1, 2]);
          assert.equal(