CVE-2020-7597: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: codecov-node npm module before 3.6.5 allows remote attackers to execute arbitrary commands.The value provided as part of the gcov-root argument is executed by the exec function within lib/codecov.js. This vulnerability exists due to an incomplete fix of CVE-2020-7596.
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: 8.8
Published: 2020-02-17T19:15Z
Repository: https://github.com/codecov/codecov-node
Language: JSON
Files changed: .gitignore, lib/codecov.js, package-lock.json, package.json, test/index.test.js
Commit date: 2020-02-04 10:41:09 -0500
Commit message: [CE-1330] Escaping args (#167)

* Escaping args


Diff: diff --git a/.gitignore b/.gitignore
index 432d08d..d2eb52c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,5 +25,6 @@ build/Release
 # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
 node_modules
 
 lib-cov/
 coverage.json
+.vs-code
diff --git a/lib/codecov.js b/lib/codecov.js
index 0e850aa..ad0cb2a 100644
--- a/lib/codecov.js
+++ b/lib/codecov.js
@@ -3,11 +3,10 @@ var path = require('path')
 var request = require('teeny-request').teenyRequest
 var urlgrey = require('urlgrey')
 var jsYaml = require('js-yaml')
 var walk = require('ignore-walk')
 var execSync = require('child_process').execSync
-var validator = require('validator')
 
 var detectProvider = require('./detect')
 
 var version = 'v' + require('../package.json').version
 
@@ -392,28 +391,28 @@ var upload = function(args, on_success, on_failure) {
       }
       var gcov
       if (!isWindows) {
         gcov =
           'find ' +
-          (args.options['gcov-root'] || root) +
+          (sanitizeVar(args.options['gcov-root']) || root) +
           " -type f -name '*.gcno' " +
           gcg +
           ' -exec ' +
-          (validator.escape(args.options['gcov-exec']) || 'gcov') +
+          (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
           ' ' +
-          (validator.escape(args.options['gcov-args']) || '') +
+          (sanitizeVar(args.options['gcov-args']) || '') +
           ' {} +'
       } else {
         // @TODO support for root
         // not straight forward due to nature of windows command dir
         gcov =
           'for /f "delims=" %g in (\'dir /a-d /b /s *.gcno ' +
           gcg +
           "') do " +
-          (args.options['gcov-exec'] || 'gcov') +
+          (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
           ' ' +
-          (args.options['gcov-args'] || '') +
+          (sanitizeVar(args.options['gcov-args']) || '') +
           ' %g'
       }
       debug.push(gcov)
       console.log('    $ ' + gcov)
       execSync(gcov)
@@ -554,11 +553,16 @@ var upload = function(args, on_success, on_failure) {
     debug: debug,
     url: codecov_endpoint,
   }
 }
 
+function sanitizeVar(arg) {
+  return arg.replace(/&/g, '')
+}
+
 module.exports = {
+  sanitizeVar: sanitizeVar,
   upload: upload,
   version: version,
   sendToCodecovV2: sendToCodecovV2,
   sendToCodecovV3: sendToCodecovV3,
 }
diff --git a/package-lock.json b/package-lock.json
index 1337609..d3ff0bf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6146,15 +6146,10 @@
       "requires": {
         "spdx-correct": "^3.0.0",
         "spdx-expression-parse": "^3.0.0"
       }
     },
-    "validator": {
-      "version": "12.2.0",
-      "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
-      "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
-    },
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
       "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
       "dev": true,
diff --git a/package.json b/package.json
index cc7a839..7ff6ade 100644
--- a/package.json
+++ b/package.json
@@ -33,12 +33,11 @@
   "dependencies": {
     "argv": "0.0.2",
     "ignore-walk": "3.0.3",
     "js-yaml": "3.13.1",
     "teeny-request": "6.0.1",
-    "urlgrey": "0.4.4",
-    "validator": "12.2.0"
+    "urlgrey": "0.4.4"
   },
   "devDependencies": {
     "eslint": "^5.16.0",
     "eslint-config-prettier": "^4.1.0",
     "husky": "4.2.1",
diff --git a/test/index.test.js b/test/index.test.js
index 7e2c92f..7fd1143 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -274,6 +274,12 @@ describe('Codecov', function() {
       options: { dump: true, yml: 'foo.yml', disable: 'detect' },
     })
     expect(res.query.yaml).toBe(process.cwd() + '/foo.yml')
     mockFs.restore()
   })
+
+  it('can sanitize inputs', function() {
+    expect(codecov.sanitizeVar('real & run unsafe & command')).toEqual(
+      'real  run unsafe  command'
+    )
+  })
 })

Vulnerable code: var validator = require('validator')
          (args.options['gcov-root'] || root) +
          (validator.escape(args.options['gcov-exec']) || 'gcov') +
          (validator.escape(args.options['gcov-args']) || '') +
          (args.options['gcov-exec'] || 'gcov') +
          (args.options['gcov-args'] || '') +
    "validator": {
      "version": "12.2.0",
      "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz",
      "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ=="
    },
    "urlgrey": "0.4.4",
    "validator": "12.2.0"
Fixed code: .vs-code
          (sanitizeVar(args.options['gcov-root']) || root) +
          (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
          (sanitizeVar(args.options['gcov-args']) || '') +
          (sanitizeVar(args.options['gcov-exec']) || 'gcov') +
          (sanitizeVar(args.options['gcov-args']) || '') +
function sanitizeVar(arg) {
  return arg.replace(/&/g, '')
}

  sanitizeVar: sanitizeVar,
    "urlgrey": "0.4.4"

  it('can sanitize inputs', function() {
    expect(codecov.sanitizeVar('real & run unsafe & command')).toEqual(
      'real  run unsafe  command'
    )
  })