Command injection in codecov (npm package)
Description
A command injection vulnerability in Codecov's npm package allowed attackers to execute arbitrary commands via backtick payloads, bypassing an earlier incomplete fix.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A command injection vulnerability in Codecov's npm package allowed attackers to execute arbitrary commands via backtick payloads, bypassing an earlier incomplete fix.
Vulnerability
Overview
The upload method in the codecov npm package (prior to version 3.7.1) contains a command injection vulnerability [1]. An earlier attempt to fix this issue (CVE-2020-7597) only blocked the & character, leaving the package still susceptible to injection using backticks, which are interpreted by the shell [1].
Attack
Vector and Prerequisites The vulnerability arises from the use of execSync to call system commands (e.g., git ls-files || hg locate) with unsanitized user-controllable input [3]. The fix replaced execSync with execFileSync, which does not spawn a shell, thus preventing command injection [3][4]. While the attack surface is considered low, exploitation is possible if a malicious actor can influence the arguments passed to the upload method [1]. However, in typical usage—where the module is used directly in a build pipeline rather than as a library accepting external input—the risk is reduced [1].
Impact
Successful exploitation allows an attacker to execute arbitrary operating system commands on the host running the Codecov tool, potentially compromising the build environment, exfiltrating secrets, or pivoting to other systems within the CI/CD pipeline [1].
Mitigation
Users should upgrade to codecov version 3.7.1 or later, which resolves the vulnerability by using execFileSync instead of execSync [3][4]. No workarounds other than updating the package have been identified.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
codecovnpm | < 3.7.1 | 3.7.1 |
Affected products
2Patches
1c0711c656686Switch from execSync to execFileSync (#180)
3 files changed · +46 −18
.idea/.gitignore+7 −0 added@@ -0,0 +1,7 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ + +.idea/ \ No newline at end of file
lib/codecov.js+37 −18 modified@@ -4,21 +4,23 @@ var request = require('teeny-request').teenyRequest var urlgrey = require('urlgrey') var jsYaml = require('js-yaml') var walk = require('ignore-walk') +var execFileSync = require('child_process').execFileSync var execSync = require('child_process').execSync var detectProvider = require('./detect') var version = 'v' + require('../package.json').version -var patterns, - more_patterns = '' +var patterns = '' +var more_patterns = '' +var winPatterns = '' var isWindows = process.platform.match(/win32/) || process.platform.match(/win64/) if (!isWindows) { - patterns = - "-type f \\( -name '*coverage.*' " + + patterns = ( + "-type f -name '*coverage.*' " + "-or -name 'nosetests.xml' " + "-or -name 'jacoco*.xml' " + "-or -name 'clover.xml' " + @@ -29,7 +31,7 @@ if (!isWindows) { "-or -name '*.lcov' " + "-or -name 'gcov.info' " + "-or -name '*.gcov' " + - "-or -name '*.lst' \\) " + + "-or -name '*.lst' " + "-not -name '*.sh' " + "-not -name '*.data' " + "-not -name '*.py' " + @@ -76,9 +78,10 @@ if (!isWindows) { "-not -path '*/$bower_components/*' " + "-not -path '*/node_modules/*' " + "-not -path '*/conftest_*.c.gcov'" + ).split(' ') } else { - patterns = - '/a-d /b /s *coverage.* ' + + winPatterns = ( + '/a:-d /b /s *coverage.* ' + '/s nosetests.xml ' + '/s jacoco*.xml ' + '/s clover.xml ' + @@ -136,6 +139,7 @@ if (!isWindows) { '| findstr /i /v \\\\$bower_components\\ ' + '| findstr /i /v \\node_modules\\ ' + '| findstr /i /v \\conftest_.*\\.c\\.gcov ' + ).split(' ') } var sendToCodecovV2 = function( @@ -355,7 +359,7 @@ var upload = function(args, on_success, on_failure) { console.log('==> Building file structure') try { upload += - execSync('git ls-files || hg locate', { cwd: root }) + execFileSync('git', ['ls-files', '||', 'hg', 'locate'], { cwd: root }) .toString() .trim() + '\n<<<<<< network\n' } catch (err) { @@ -414,7 +418,7 @@ var upload = function(args, on_success, on_failure) { } debug.push(gcov) console.log(' $ ' + gcov) - execSync(gcov) + execFileSync(gcov) } catch (e) { console.log(' Failed to run gcov command.') } @@ -431,19 +435,23 @@ var upload = function(args, on_success, on_failure) { .toString() .trim() } else { - bowerrc = execSync('if exist .bowerrc type .bowerrc', { cwd: root }) - .toString() - .trim() + bowerrc = fs.existsSync('.bowerrc') } if (bowerrc) { bowerrc = JSON.parse(bowerrc).directory if (bowerrc) { if (!isWindows) { - more_patterns = - " -not -path '*/" + bowerrc.toString().replace(/\/$/, '') + "/*'" + more_patterns = ( + " -not -path '*/" + + bowerrc.toString().replace(/\/$/, '') + + "/*'" + ).split(' ') } else { - more_patterns = - '| findstr /i /v \\' + bowerrc.toString().replace(/\/$/, '') + '\\' + more_patterns = ( + '| findstr /i /v \\' + + bowerrc.toString().replace(/\/$/, '') + + '\\' + ).split(' ') } } } @@ -474,15 +482,26 @@ var upload = function(args, on_success, on_failure) { } else if ((args.options.disable || '').split(',').indexOf('search') === -1) { console.log('==> Scanning for reports') var _files + var _findArgs if (!isWindows) { - _files = execSync('find ' + root + ' ' + patterns + more_patterns) + // @TODO support for a root directory + // It's not straightforward due to the nature of the find command + _findArgs = [root].concat(patterns) + if (more_patterns) { + _findArgs.concat(more_patterns) + } + _files = execFileSync('find', _findArgs) .toString() .trim() .split('\n') } else { // @TODO support for a root directory // It's not straightforward due to the nature of the dir command - _files = execSync('dir ' + patterns + more_patterns) + _findArgs = [root].concat(winPatterns) + if (more_patterns) { + _findArgs.concat(more_patterns) + } + _files = execSync('dir ' + winPatterns.join(' ') + more_patterns) .toString() .trim() .split('\r\n')
README.md+2 −0 modified@@ -116,3 +116,5 @@ If you're seeing an **HTTP 400 error when uploading reports to S3**, make sure y - v3.6.3 Fix for AWS Codebuild & package updates - v3.6.4 Fix Cirrus CI - v3.7.0 Remove the X-Amz-Acl: public-read header + +.
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-5q88-cjfq-g2mhghsax_refsource_MISCADVISORY
- github.com/advisories/GHSA-xp63-6vf5-xf3vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-15123ghsaADVISORY
- github.com/codecov/codecov-node/commit/c0711c656686e902af2cd92d6aecc8074de4d83dghsax_refsource_MISCWEB
- github.com/codecov/codecov-node/pull/180ghsax_refsource_MISCWEB
- github.com/codecov/codecov-node/security/advisories/GHSA-xp63-6vf5-xf3vghsax_refsource_CONFIRMWEB
- lgtm.com/query/7714424068617023832ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.