From 0a21792803639851b480fbd8cbcb5540ef974387 Mon Sep 17 00:00:00 2001 From: Scott Anderson <87314430+scott-ut@users.noreply.github.com> Date: Sun, 28 Nov 2021 11:54:40 +0000 Subject: [PATCH] Set rejectUnauthorized to true by default (#3149) Resolve CVE-2020-240-25 by setting rejectUnauthorized to true by default. Add configuration flag to override this to false if necessary. extract rejectUnauthorized download option to its own file. Add doc option to README.md. --- README.md | 15 ++++--- scripts/util/downloadoptions.js | 5 ++- scripts/util/rejectUnauthorized.js | 46 +++++++++++++++++++++ test/downloadoptions.js | 65 +++++++++++++++++++++++++++++- 4 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 scripts/util/rejectUnauthorized.js diff --git a/README.md b/README.md index b0cb5b78d..a1da0f363 100644 --- a/README.md +++ b/README.md @@ -596,12 +596,13 @@ When compiling a directory `--source-map` can either be a boolean value or a dir node-sass supports different configuration parameters to change settings related to the sass binary such as binary name, binary path or alternative download path. Following parameters are supported by node-sass: -Variable name | .npmrc parameter | Process argument | Value ------------------|------------------|--------------------|------ -SASS_BINARY_NAME | sass_binary_name | --sass-binary-name | path -SASS_BINARY_SITE | sass_binary_site | --sass-binary-site | URL -SASS_BINARY_PATH | sass_binary_path | --sass-binary-path | path -SASS_BINARY_DIR | sass_binary_dir | --sass-binary-dir | path +Variable name | .npmrc parameter | Process argument | Value +-------------------------|--------------------------|----------------------------|------ +SASS_BINARY_NAME | sass_binary_name | --sass-binary-name | path +SASS_BINARY_SITE | sass_binary_site | --sass-binary-site | URL +SASS_BINARY_PATH | sass_binary_path | --sass-binary-path | path +SASS_BINARY_DIR | sass_binary_dir | --sass-binary-dir | path +SASS_REJECT_UNAUTHORIZED | sass_reject_unauthorized | --sass-reject-unauthorized | value These parameters can be used as environment variable: @@ -615,6 +616,8 @@ As a process argument: * E.g. `npm install node-sass --sass-binary-site=http://example.com/` +If you are using self-signed certificates for your binary then `SASS_REJECT_UNAUTHORIZED` will override (rejectUnauthorized)[https://nodejs.org/docs/latest/api/tls.html#tls_tls_createserver_options_secureconnectionlistener]. + ## Post-install Build Install runs only two Mocha tests to see if your machine can use the pre-built [LibSass] which will save some time during install. If any tests fail it will build from source. diff --git a/scripts/util/downloadoptions.js b/scripts/util/downloadoptions.js index 23529716f..e9056b10e 100644 --- a/scripts/util/downloadoptions.js +++ b/scripts/util/downloadoptions.js @@ -1,5 +1,6 @@ var proxy = require('./proxy'), - userAgent = require('./useragent'); + userAgent = require('./useragent'), + rejectUnauthorized = require('./rejectUnauthorized'); /** * The options passed to request when downloading the bibary @@ -14,7 +15,7 @@ var proxy = require('./proxy'), */ module.exports = function() { var options = { - rejectUnauthorized: false, + rejectUnauthorized: rejectUnauthorized(), timeout: 60000, headers: { 'User-Agent': userAgent(), diff --git a/scripts/util/rejectUnauthorized.js b/scripts/util/rejectUnauthorized.js new file mode 100644 index 000000000..a1c801073 --- /dev/null +++ b/scripts/util/rejectUnauthorized.js @@ -0,0 +1,46 @@ +var pkg = require('../../package.json'); + +/** + * Get the value of a CLI argument + * + * @param {String} name + * @param {Array} args + * @api private + */ + function getArgument(name, args) { + var flags = args || process.argv.slice(2), + index = flags.lastIndexOf(name); + + if (index === -1 || index + 1 >= flags.length) { + return null; + } + + return flags[index + 1]; +} + +/** + * Get the value of reject-unauthorized + * If environment variable SASS_REJECT_UNAUTHORIZED is non-zero, + * .npmrc variable sass_reject_unauthorized or + * process argument --sass-reject_unauthorized is provided, + * set rejectUnauthorized to true + * Else set to false by default + * + * @return {Boolean} The value of rejectUnauthorized + * @api private + */ +module.exports = function() { + var rejectUnauthorized = false; + + if (getArgument('--sass-reject-unauthorized')) { + rejectUnauthorized = getArgument('--sass-reject-unauthorized'); + } else if (process.env.SASS_REJECT_UNAUTHORIZED !== '0') { + rejectUnauthorized = true; + } else if (process.env.npm_config_sass_reject_unauthorized) { + rejectUnauthorized = process.env.npm_config_sass_reject_unauthorized; + } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.rejectUnauthorized) { + rejectUnauthorized = pkg.nodeSassConfig.rejectUnauthorized; + } + + return rejectUnauthorized; +}; diff --git a/test/downloadoptions.js b/test/downloadoptions.js index de8963842..a6e2d9bae 100644 --- a/test/downloadoptions.js +++ b/test/downloadoptions.js @@ -8,7 +8,7 @@ describe('util', function() { describe('without a proxy', function() { it('should look as we expect', function() { var expected = { - rejectUnauthorized: false, + rejectUnauthorized: true, timeout: 60000, headers: { 'User-Agent': ua(), @@ -33,7 +33,7 @@ describe('util', function() { it('should look as we expect', function() { var expected = { - rejectUnauthorized: false, + rejectUnauthorized: true, proxy: proxy, timeout: 60000, headers: { @@ -57,6 +57,25 @@ describe('util', function() { delete process.env.HTTP_PROXY; }); + it('should look as we expect', function() { + var expected = { + rejectUnauthorized: true, + timeout: 60000, + headers: { + 'User-Agent': ua(), + }, + encoding: null, + }; + + assert.deepStrictEqual(opts(), expected); + }); + }); + + describe('with SASS_REJECT_UNAUTHORIZED set to false', function() { + beforeEach(function() { + process.env.SASS_REJECT_UNAUTHORIZED = '0'; + }); + it('should look as we expect', function() { var expected = { rejectUnauthorized: false, @@ -70,5 +89,47 @@ describe('util', function() { assert.deepStrictEqual(opts(), expected); }); }); + + describe('with SASS_REJECT_UNAUTHORIZED set to true', function() { + beforeEach(function() { + process.env.SASS_REJECT_UNAUTHORIZED = '1'; + }); + + it('should look as we expect', function() { + var expected = { + rejectUnauthorized: true, + timeout: 60000, + headers: { + 'User-Agent': ua(), + }, + encoding: null, + }; + + assert.deepStrictEqual(opts(), expected); + }); + }); + + describe('with npm_config_sass_reject_unauthorized set to true', function() { + beforeEach(function() { + process.env.npm_config_sass_reject_unauthorized = true; + }); + + it('should look as we expect', function() { + var expected = { + rejectUnauthorized: true, + timeout: 60000, + headers: { + 'User-Agent': ua(), + }, + encoding: null, + }; + + assert.deepStrictEqual(opts(), expected); + }); + + afterEach(function() { + process.env.npm_config_sass_reject_unauthorized = undefined; + }); + }); }); });