CVE-2023-26156: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: Versions of the package chromedriver before 119.0.1 are vulnerable to Command Injection when setting the chromedriver.path to an arbitrary system binary. This could lead to unauthorized access and potentially malicious actions on the host system.**Note:**An attacker must have access to the system running the vulnerable chromedriver library to exploit it. The success of exploitation also depends on the permissions and privileges of the process running chromedriver.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 7.5
Published: 2023-11-09T05:15Z
Repository: https://github.com/giggio/node-chromedriver
Language: JavaScript
Files changed: .npmignore, lib/chromedriver.js, package-lock.json, package.json, testStart.js, update.js
Commit date: 2023-11-07 19:06:37 -0300
Commit message: Hide private attributes from exports

Diff: diff --git a/.npmignore b/.npmignore
index bc4f15c..7195c76 100644
--- a/.npmignore
+++ b/.npmignore
@@ -3,10 +3,11 @@ tmp
 .eslintrc.json
 .gitignore
 Dockerfile
 *.sh
 testInstall.js
+testStart.js
 update.js
 *.tgz
 .vscode
 .editorconfig
 shippable.yml
diff --git a/lib/chromedriver.js b/lib/chromedriver.js
index 7d89b3d..d43d37d 100644
--- a/lib/chromedriver.js
+++ b/lib/chromedriver.js
@@ -1,47 +1,57 @@
 const fs = require('fs');
 const path = require('path');
 const tcpPortUsed = require('tcp-port-used');
 function getPortFromArgs(args) {
   let port = 9515;
-  if (!args) {
+  if (!args)
     return port;
-  }
   const portRegexp = /--port=(\d*)/;
   const portArg = args.find(function (arg) {
     return portRegexp.test(arg);
   });
-  if (portArg) {
+  if (portArg)
     port = parseInt(portRegexp.exec(portArg)[1]);
-  }
   return port;
 }
 process.env.PATH = path.join(__dirname, 'chromedriver') + path.delimiter + process.env.PATH;
-exports.path = process.platform === 'win32' ? path.join(__dirname, 'chromedriver', 'chromedriver.exe') : path.join(__dirname, 'chromedriver', 'chromedriver');
-exports.version = '119.0.6045.105';
-exports.start = function (args, returnPromise) {
-  let command = exports.path;
+const crpath = process.platform === 'win32' ? path.join(__dirname, 'chromedriver', 'chromedriver.exe') : path.join(__dirname, 'chromedriver', 'chromedriver');
+const version = '119.0.6045.105';
+let defaultInstance = null;
+
+function start(args, returnPromise) {
+  let command = crpath;
   if (!fs.existsSync(command)) {
     console.log('Could not find chromedriver in default path: ', command);
     console.log('Falling back to use global chromedriver bin');
     command = process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
   }
   const cp = require('child_process').spawn(command, args);
   cp.stdout.pipe(process.stdout);
   cp.stderr.pipe(process.stderr);
-  exports.defaultInstance = cp;
-  if (!returnPromise) {
+  defaultInstance = cp;
+  if (!returnPromise)
     return cp;
-  }
   const port = getPortFromArgs(args);
   const pollInterval = 100;
   const timeout = 10000;
   return tcpPortUsed.waitUntilUsed(port, pollInterval, timeout)
     .then(function () {
       return cp;
     });
-};
-exports.stop = function () {
-  if (exports.defaultInstance != null) {
-    exports.defaultInstance.kill();
+}
+
+function stop() {
+  if (defaultInstance != null)
+    defaultInstance.kill();
+  defaultInstance = null;
+}
+
+module.exports = {
+  path: crpath,
+  version,
+  start,
+  stop,
+  get defaultInstance() {
+    return defaultInstance;
   }
 };
diff --git a/package-lock.json b/package-lock.json
index f318a2d..1351ee2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,14 @@
 {
   "name": "chromedriver",
-  "version": "119.0.0",
+  "version": "119.0.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "chromedriver",
-      "version": "119.0.0",
+      "version": "119.0.1",
       "hasInstallScript": true,
       "license": "Apache-2.0",
       "dependencies": {
         "@testim/chrome-version": "^1.1.4",
         "axios": "^1.6.0",
diff --git a/package.json b/package.json
index a7902cd..3dce5c7 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "chromedriver",
-  "version": "119.0.0",
+  "version": "119.0.1",
   "keywords": [
     "chromedriver",
     "selenium"
   ],
   "description": "ChromeDriver for Selenium",
diff --git a/testStart.js b/testStart.js
new file mode 100755
index 0000000..2c4edf2
--- /dev/null
+++ b/testStart.js
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+
+"use strict";
+const chromedriver = require('./lib/chromedriver');
+
+async function run() {
+  console.log(`Starting chromedriver. Instance: ${JSON.stringify(chromedriver)}`);
+  await chromedriver.start(null, true);
+  console.log(`Started Chromedriver. Instance is null: ${chromedriver.defaultInstance === null}.`);
+  chromedriver.stop();
+  console.log(`Stopped Chromedriver. Instance is null: ${chromedriver.defaultInstance === null}.`);
+}
+
+run();
diff --git a/update.js b/update.js
index 73ecdfe..62fa16e 100755
--- a/update.js
+++ b/update.js
@@ -19,18 +19,18 @@ async function getLatest() {
     process.exit(1);
   }
 }
 
 /* Provided a new Chromedriver version such as 77.0.3865.40:
-   - update the version inside the ./lib/chromedriver helper file e.g. exports.version = '77.0.3865.40';
+   - update the version inside the ./lib/chromedriver helper file e.g. const version = '77.0.3865.40';
    - bumps package.json version number
    - add a git tag using the new node-chromedriver version
    - add a git commit, e.g. Bump version to 77.0.0
 */
 async function writeUpdate(newVersion, shouldCommit) {
   const helper = fs.readFileSync('./lib/chromedriver.js', 'utf8');
-  const versionExport = 'exports.version';
+  const versionExport = 'const version';
   const regex = new RegExp(`^.*${versionExport}.*$`, 'gm');
   const updated = helper.replace(regex, `${versionExport} = '${newVersion}';`);
   const currentMajor = semver.major(currentVersionInPackageJson);
   const newMajor = semver.major(semver.coerce(newVersion));
   const version = currentMajor !== newMajor ? `${newMajor}.0.0` : semver.inc(currentVersionInPackageJson, 'patch');

Vulnerable code:   if (!args) {
  }
  if (portArg) {
  }
exports.path = process.platform === 'win32' ? path.join(__dirname, 'chromedriver', 'chromedriver.exe') : path.join(__dirname, 'chromedriver', 'chromedriver');
exports.version = '119.0.6045.105';
exports.start = function (args, returnPromise) {
  let command = exports.path;
  exports.defaultInstance = cp;
  if (!returnPromise) {
  }
};
exports.stop = function () {
  if (exports.defaultInstance != null) {
    exports.defaultInstance.kill();
  "version": "119.0.0",
      "version": "119.0.0",
  "version": "119.0.0",
   - update the version inside the ./lib/chromedriver helper file e.g. exports.version = '77.0.3865.40';
  const versionExport = 'exports.version';
Fixed code: testStart.js
  if (!args)
  if (portArg)
const crpath = process.platform === 'win32' ? path.join(__dirname, 'chromedriver', 'chromedriver.exe') : path.join(__dirname, 'chromedriver', 'chromedriver');
const version = '119.0.6045.105';
let defaultInstance = null;

function start(args, returnPromise) {
  let command = crpath;
  defaultInstance = cp;
  if (!returnPromise)
}

function stop() {
  if (defaultInstance != null)
    defaultInstance.kill();
  defaultInstance = null;
}

module.exports = {
  path: crpath,
  version,
  start,
  stop,
  get defaultInstance() {
    return defaultInstance;
  "version": "119.0.1",
      "version": "119.0.1",
  "version": "119.0.1",
#!/usr/bin/env node

"use strict";
const chromedriver = require('./lib/chromedriver');

async function run() {
  console.log(`Starting chromedriver. Instance: ${JSON.stringify(chromedriver)}`);
  await chromedriver.start(null, true);
  console.log(`Started Chromedriver. Instance is null: ${chromedriver.defaultInstance === null}.`);
  chromedriver.stop();
  console.log(`Stopped Chromedriver. Instance is null: ${chromedriver.defaultInstance === null}.`);
}

run();
   - update the version inside the ./lib/chromedriver helper file e.g. const version = '77.0.3865.40';
  const versionExport = 'const version';