nconf
Advanced tools
Comparing version 0.11.3 to 1.0.0-beta.0
v0.11.0 / Mon, 23 Nov 2020 | ||
========================== | ||
* [4122731](https://github.com/projects/nconf/commit/4122731) 0.11.0 (`Matt Hamann`) | ||
* [56794d1](https://github.com/projects/nconf/commit/56794d1) chore: upgrade deps to fix security vulns (`Matt Hamann`) | ||
v0.10.0 / Mon, 18 Dec 2017 | ||
========================== | ||
* [0ef652e](https://github.com/projects/nconf/commit/0ef652e) 0.10.0 (`Matt Hamann`) | ||
* [01f25fa](https://github.com/projects/nconf/commit/01f25fa) Regex as env separator (#288) (`Adrien Becchis`) | ||
* [16667be](https://github.com/projects/nconf/commit/16667be) Argv store separator (#291) (`Adrien Becchis`) | ||
v0.9.1 / Fri, 3 Nov 2017 | ||
======================== | ||
* [bac910a](https://github.com/projects/nconf/commit/bac910a) 0.9.1 (`Matt Hamann`) | ||
* [806f1b1](https://github.com/projects/nconf/commit/806f1b1) 0.9.1 (`Matt Hamann`) | ||
* [2bdf7e1](https://github.com/projects/nconf/commit/2bdf7e1) Clean Argv Store options (#290) (`Adrien Becchis`) | ||
@@ -17,0 +6,0 @@ * [b9321b2](https://github.com/projects/nconf/commit/b9321b2) transformer can now return an undefined key (#289) (`Adrien Becchis`) |
@@ -237,4 +237,4 @@ /* | ||
async.whilst(function () { | ||
return typeof response === 'undefined' && current < names.length; | ||
async.whilst(function (cb) { | ||
cb(null, typeof response === 'undefined' && current < names.length); | ||
}, function (next) { | ||
@@ -316,4 +316,4 @@ var store = self.stores[names[current]]; | ||
async.whilst(function() { | ||
return !result && keyIndex < keys.length; | ||
async.whilst(function(cb) { | ||
cb(null, !result && keyIndex < keys.length); | ||
}, function(next) { | ||
@@ -368,3 +368,3 @@ var key = keys[keyIndex]; | ||
} else { | ||
return true; | ||
return this; | ||
} | ||
@@ -371,0 +371,0 @@ |
@@ -23,11 +23,14 @@ /* | ||
this.type = 'argv'; | ||
this.readOnly = true; | ||
this.readOnly = options.readOnly !== undefined? options.readOnly : true; | ||
this.options = options; | ||
this.usage = usage; | ||
if(typeof options.parseValues === 'boolean') { | ||
this.parseValues = options.parseValues; | ||
delete options.parseValues; | ||
if(typeof options.readOnly === 'boolean') { | ||
this.readOnly = options.readOnly; | ||
delete options.readOnly; | ||
// FIXME; should not mutate options!!!! | ||
} else { | ||
this.parseValues = false; | ||
this.readOnly = true; | ||
} | ||
if (typeof options.transform === 'function')Â { | ||
@@ -39,8 +42,2 @@ this.transform = options.transform; | ||
} | ||
if (typeof options.separator === 'string' || options.separator instanceof RegExp) { | ||
this.separator = options.separator; | ||
delete options.separator; | ||
} else { | ||
this.separator = ''; | ||
} | ||
}; | ||
@@ -87,3 +84,9 @@ | ||
this.readOnly = false; | ||
var tempWrite = false; | ||
if(this.readOnly) { | ||
this.readOnly = false; | ||
tempWrite = true; | ||
} | ||
Object.keys(argv).forEach(function (key) { | ||
@@ -97,8 +100,3 @@ var val = argv[key]; | ||
if (self.separator) { | ||
self.set(common.key.apply(common, key.split(self.separator)), val); | ||
} | ||
else { | ||
self.set(key, val); | ||
} | ||
self.set(key, val); | ||
} | ||
@@ -110,3 +108,5 @@ }); | ||
this.readOnly = true; | ||
if (tempWrite) { | ||
this.readOnly = true; | ||
} | ||
return this.store; | ||
@@ -113,0 +113,0 @@ }; |
@@ -23,5 +23,4 @@ /* | ||
this.type = 'env'; | ||
this.readOnly = true; | ||
this.readOnly = options.readOnly !== undefined ? options.readOnly : true; | ||
this.whitelist = options.whitelist || []; | ||
this.separator = options.separator || ''; | ||
this.lowerCase = options.lowerCase || false; | ||
@@ -39,5 +38,2 @@ this.parseValues = options.parseValues || false; | ||
} | ||
if (typeof(options) === 'string' || options instanceof RegExp) { | ||
this.separator = options; | ||
} | ||
}; | ||
@@ -77,3 +73,9 @@ | ||
this.readOnly = false; | ||
var tempWrite = false; | ||
if(this.readOnly) { | ||
this.readOnly = false; | ||
tempWrite = true; | ||
} | ||
Object.keys(env).filter(function (key) { | ||
@@ -90,3 +92,3 @@ if (self.match && self.whitelist.length) { | ||
}).forEach(function (key) { | ||
var val = env[key]; | ||
@@ -98,13 +100,10 @@ | ||
if (self.separator) { | ||
self.set(common.key.apply(common, key.split(self.separator)), val); | ||
} | ||
else { | ||
self.set(key, val); | ||
} | ||
self.set(key, val); | ||
}); | ||
this.readOnly = true; | ||
if (tempWrite) { | ||
this.readOnly = true; | ||
} | ||
return this.store; | ||
}; | ||
@@ -8,6 +8,7 @@ /* | ||
var fs = require('fs'), | ||
var os = require('os'), | ||
fs = require('fs'), | ||
path = require('path'), | ||
util = require('util'), | ||
Secure = require('secure-keys'), | ||
crypto = require('crypto'), | ||
formats = require('../formats'), | ||
@@ -40,2 +41,3 @@ Memory = require('./memory').Memory; | ||
|| 2; | ||
this.eol = !(options.eol === false); | ||
@@ -55,8 +57,2 @@ if (this.secure) { | ||
} | ||
this.keys = new Secure({ | ||
secret: this.secure.secret, | ||
alg: this.secure.alg, | ||
format: this.format | ||
}); | ||
} | ||
@@ -85,3 +81,3 @@ | ||
// ### function saveToFile (path, value, callback) | ||
// #### @path {string} The path to the file where we save the configuration to | ||
// #### @path {string} The path to the file where we save the configuration to | ||
// #### @format {Object} Optional formatter, default behing the one of the store | ||
@@ -192,6 +188,18 @@ // #### @callback {function} Continuation to respond to when complete. | ||
if (this.secure) { | ||
data = this.keys.encrypt(data); | ||
var self = this; | ||
data = Object.keys(data).reduce(function (acc, key) { | ||
var value = format.stringify(data[key]); | ||
var iv = crypto.randomBytes(16); | ||
var cipher = crypto.createCipheriv(self.secure.alg, self.secure.secret, iv); | ||
var ciphertext = cipher.update(value, 'utf8', 'hex'); | ||
ciphertext += cipher.final('hex'); | ||
acc[key] = { alg: self.secure.alg, value: ciphertext, iv: iv.toString('hex') }; | ||
return acc; | ||
}, {}); | ||
} | ||
return format.stringify(data, null, this.spacing); | ||
var stringified = format.stringify(data, null, this.spacing); | ||
var needsEOL = this.eol && stringified.slice(-1) !== os.EOL; | ||
return stringified + (needsEOL ? os.EOL : ''); | ||
}; | ||
@@ -207,7 +215,20 @@ | ||
if (!this.secure) { | ||
return parsed; | ||
if (this.secure) { | ||
var self = this; | ||
parsed = Object.keys(parsed).reduce(function (acc, key) { | ||
var value = parsed[key]; | ||
if (!value.iv) { | ||
throw new Error('Your encrypted file is outdated (encrypted without iv). Please re-encrypt your file using a pre-v1 release of nconf, v0.10 or above.'); | ||
} | ||
let decipher = crypto.createDecipheriv(value.alg, self.secure.secret, Buffer.from(value.iv, 'hex')); | ||
var plaintext = decipher.update(value.value, 'hex', 'utf8'); | ||
plaintext += decipher.final('utf8'); | ||
acc[key] = self.format.parse(plaintext); | ||
return acc; | ||
}, {}); | ||
} | ||
return this.keys.decrypt(parsed); | ||
return parsed; | ||
@@ -214,0 +235,0 @@ }; |
@@ -8,4 +8,12 @@ /* | ||
var common = require('../common'); | ||
const common = require('../common'); | ||
const DEFAULT_ACCESS_SEPARATOR = ':'; | ||
const DEFAULT_INPUT_SEPARATOR = '__'; | ||
// Helper function for preparing a string for regex matching | ||
function escapeRegExp(string) { | ||
return typeof string === 'string' && string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string | ||
} | ||
// | ||
@@ -26,5 +34,11 @@ // ### function Memory (options) | ||
this.loadFrom = options.loadFrom || null; | ||
this.logicalSeparator = options.logicalSeparator || ':'; | ||
this.accessSeparator = options.accessSeparator || DEFAULT_ACCESS_SEPARATOR; | ||
this.inputSeparator = options.inputSeparator || DEFAULT_INPUT_SEPARATOR; | ||
this.parseValues = options.parseValues || false; | ||
this.disableDefaultAccessSeparator = options.disableDefaultAccessSeparator || false; | ||
if (typeof(options) === 'string' || options instanceof RegExp) { | ||
this.inputSeparator = options; | ||
} | ||
if (this.loadFrom) { | ||
@@ -35,2 +49,19 @@ this.store = common.loadFilesSync(this.loadFrom); | ||
Memory.prototype._normalizeKey = function (key) { | ||
let inputSeparator = this.inputSeparator; | ||
if (inputSeparator instanceof RegExp) { | ||
inputSeparator = inputSeparator.source; | ||
} else { | ||
inputSeparator = escapeRegExp(inputSeparator); | ||
} | ||
let separatorRegexStr = `${escapeRegExp(this.accessSeparator)}|${inputSeparator}`; | ||
if (!this.disableDefaultAccessSeparator) { | ||
separatorRegexStr += `|${DEFAULT_ACCESS_SEPARATOR}`; | ||
} | ||
const separatorRegEx = new RegExp(separatorRegexStr, 'g'); | ||
return key && key.replace(separatorRegEx, this.accessSeparator); | ||
} | ||
// | ||
@@ -43,3 +74,3 @@ // ### function get (key) | ||
var target = this.store, | ||
path = common.path(key, this.logicalSeparator); | ||
path = common.path(this._normalizeKey(key), this.accessSeparator); | ||
@@ -73,3 +104,3 @@ // | ||
var target = this.store, | ||
path = common.path(key, this.logicalSeparator); | ||
path = common.path(this._normalizeKey(key), this.accessSeparator); | ||
@@ -128,3 +159,3 @@ if (path.length === 0) { | ||
value = target, | ||
path = common.path(key, this.logicalSeparator); | ||
path = common.path(key, this.accessSeparator); | ||
@@ -177,3 +208,3 @@ // | ||
target = this.store, | ||
path = common.path(key, this.logicalSeparator), | ||
path = common.path(key, this.accessSeparator), | ||
fullKey = key; | ||
@@ -212,3 +243,3 @@ | ||
return Object.keys(value).every(function (nested) { | ||
return self.merge(common.keyed(self.logicalSeparator, fullKey, nested), value[nested]); | ||
return self.merge(common.keyed(self.accessSeparator, fullKey, nested), value[nested]); | ||
}); | ||
@@ -215,0 +246,0 @@ }; |
{ | ||
"name": "nconf", | ||
"description": "Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging.", | ||
"version": "0.11.3", | ||
"version": "1.0.0-beta.0", | ||
"author": "Charlie Robbins <charlie.robbins@gmail.com>", | ||
@@ -22,21 +22,23 @@ "contributors": [ | ||
"dependencies": { | ||
"async": "^1.4.0", | ||
"ini": "^2.0.0", | ||
"secure-keys": "^1.0.0", | ||
"yargs": "^16.1.1" | ||
"yargs": "^17.0.0" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^3.1.0", | ||
"eslint": "^4.9.0", | ||
"istanbul": "^0.4.5", | ||
"nconf-yaml": "^1.0.2", | ||
"vows": "^0.8.3" | ||
"async": "^3.0.0", | ||
"coveralls": "^3.0.2", | ||
"eslint": "^7.0.0", | ||
"istanbul": "^0.4.1", | ||
"jest": "^27.0.0", | ||
"nconf-yaml": "^1.0.2" | ||
}, | ||
"main": "./lib/nconf", | ||
"scripts": { | ||
"test": "vows test/*-test.js test/**/*-test.js --spec", | ||
"cover": "istanbul cover vows -- test/*-test.js test/**/*-test.js --spec", | ||
"test": "jest --verbose", | ||
"cover": "jest --coverage", | ||
"coveralls": "cat coverage/lcov.info | coveralls", | ||
"lint": "eslint ." | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"engines": { | ||
@@ -43,0 +45,0 @@ "node": ">= 0.4.0" |
@@ -11,4 +11,4 @@ # nconf | ||
``` js | ||
var fs = require('fs'), | ||
nconf = require('nconf'); | ||
// sample.js | ||
var nconf = require('nconf'); | ||
@@ -43,3 +43,3 @@ // | ||
nconf.save(function (err) { | ||
fs.readFile('path/to/your/config.json', function (err, data) { | ||
require('fs').readFile('path/to/your/config.json', function (err, data) { | ||
console.dir(JSON.parse(data.toString())) | ||
@@ -50,6 +50,6 @@ }); | ||
If you run the above script: | ||
If you run the below script: | ||
``` bash | ||
$ NODE_ENV=production sample.js --foo bar | ||
$ NODE_ENV=production node sample.js --foo bar | ||
``` | ||
@@ -130,3 +130,3 @@ | ||
``` js | ||
nconf.add('supplied', { type: 'literal', store: { 'some': 'config' }); | ||
nconf.add('supplied', { type: 'literal', store: { 'some': 'config' } }); | ||
nconf.add('user', { type: 'file', file: '/path/to/userconf.json' }); | ||
@@ -137,3 +137,3 @@ nconf.add('global', { type: 'file', file: '/path/to/globalconf.json' }); | ||
### nconf.any(names, callback) | ||
Given a set of key names, gets the value of the first key found to be truthy. The key names can be given as separate arguments | ||
Given a set of key names, gets the value of the first key found to be truthy. The key names can be given as separate arguments | ||
or as an array. If the last argument is a function, it will be called with the result; otherwise, the value is returned. | ||
@@ -188,3 +188,21 @@ | ||
``` | ||
You can also chain `.required()` calls when needed. for example when a configuration depends on another configuration store | ||
```js | ||
config | ||
.argv() | ||
.env() | ||
.required([ 'STAGE']) //here you should have STAGE otherwise throw an error | ||
.file( 'stage', path.resolve( 'configs', 'stages', config.get( 'STAGE' ) + '.json' ) ) | ||
.required([ 'OAUTH:redirectURL']) // here you should have OAUTH:redirectURL, otherwise throw an error | ||
.file( 'oauth', path.resolve( 'configs', 'oauth', config.get( 'OAUTH:MODE' ) + '.json' ) ) | ||
.file( 'app', path.resolve( 'configs', 'app.json' ) ) | ||
.required([ 'LOGS_MODE']) // here you should haveLOGS_MODE, otherwise throw an error | ||
.add( 'logs', { | ||
type: 'literal', | ||
store: require( path.resolve( 'configs', 'logs', config.get( 'LOGS_MODE' ) + '.js') ) | ||
} ) | ||
.defaults( defaults ); | ||
``` | ||
## Storage Engines | ||
@@ -195,2 +213,6 @@ | ||
All built-in storage engines inherit from the Memory store. | ||
Basic usage: | ||
``` js | ||
@@ -200,2 +222,15 @@ nconf.use('memory'); | ||
#### Options | ||
The options defined below apply to all storage engines that inherit from Memory. | ||
##### `accessSeparator: string` (default: `':'`) | ||
Defines the separator used to get or set data using the `get()` and `set()` methods. Even if this is changed, the default "colon" separator will be available unless explicitly disabled (see `disableDefaultAccessSeparator`). | ||
##### `inputSeparator: string` (default: `'__'`) | ||
This option is used by the `argv` and `env` storage engines when loading values. Since most systems only allow dashes, underscores, and alphanumeric characters in environment variables and command line arguments, the `inputSeparator` provides a mechanism for loading hierarchical values from these sources. | ||
##### `disableDefaultAccessSeparator: {true|false}` (default: `false`) | ||
Disables the default access separator of `':'`, which is always available otherwise. This is mainly used to preserve legacy behavior. It can also be used to set keys that contain the default separator (e.g. `{ 'some:long:key' : 'some value' }`). | ||
### Argv | ||
@@ -214,3 +249,3 @@ Responsible for loading the values parsed from `process.argv` by `yargs` into the configuration hierarchy. See the [yargs option docs](https://github.com/bcoe/yargs#optionskey-opt) for more on the option format. | ||
The input `obj` contains two properties passed in the following format: | ||
``` | ||
``` js | ||
{ | ||
@@ -224,3 +259,3 @@ key: '<string>', | ||
The function may return either an object in the asme format as the input or a value that evaluates to false. | ||
The function may return either an object in the same format as the input or a value that evaluates to false. | ||
If the return value is falsey, the entry will be dropped from the store, otherwise it will replace the original key/value. | ||
@@ -289,3 +324,3 @@ | ||
The input `obj` contains two properties passed in the following format: | ||
``` | ||
``` js | ||
{ | ||
@@ -304,2 +339,5 @@ key: '<string>', | ||
#### `readOnly: {true|false}` (default: `true`) | ||
Allow values in the env store to be updated in the future. The default is to not allow items in the env store to be updated. | ||
#### Examples | ||
@@ -314,3 +352,3 @@ | ||
// | ||
// Can also specify a separator for nested keys (instead of the default ':') | ||
// Can also specify an input separator for nested keys | ||
// | ||
@@ -322,4 +360,4 @@ nconf.env('__'); | ||
// | ||
// Can also lowerCase keys. | ||
// Especially handy when dealing with environment variables which are usually | ||
// Can also lowerCase keys. | ||
// Especially handy when dealing with environment variables which are usually | ||
// uppercased while argv are lowercased. | ||
@@ -339,3 +377,3 @@ // | ||
nconf.env({ | ||
separator: '__', | ||
inputSeparator: '__', | ||
match: /^whatever_matches_this_will_be_whitelisted/ | ||
@@ -394,13 +432,15 @@ whitelist: ['database__host', 'only', 'load', 'these', 'values', 'if', 'whatever_doesnt_match_but_is_whitelisted_gets_loaded_too'], | ||
This will encrypt each key using [`crypto.createCipher`](https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password), defaulting to `aes-256-ctr`. The encrypted file contents will look like this: | ||
This will encrypt each key using [`crypto.createCipheriv`](https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options), defaulting to `aes-256-ctr`. The encrypted file contents will look like this: | ||
``` | ||
``` js | ||
{ | ||
"config-key-name": { | ||
"alg": "aes-256-ctr", // cipher used | ||
"value": "af07fbcf" // encrypted contents | ||
"value": "af07fbcf", // encrypted contents | ||
"iv": "49e7803a2a5ef98c7a51a8902b76dd10" // initialization vector | ||
}, | ||
"another-config-key": { | ||
"alg": "aes-256-ctr", // cipher used | ||
"value": "e310f6d94f13" // encrypted contents | ||
"value": "e310f6d94f13", // encrypted contents | ||
"iv": "b654e01aed262f37d0acf200be193985" // initialization vector | ||
}, | ||
@@ -407,0 +447,0 @@ } |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 11 instances in 1 package
2
472
6
0
100646
6
13
1467
+ Addedcliui@8.0.1(transitive)
+ Addedyargs@17.7.2(transitive)
+ Addedyargs-parser@21.1.1(transitive)
- Removedasync@^1.4.0
- Removedsecure-keys@^1.0.0
- Removedasync@1.5.2(transitive)
- Removedcliui@7.0.4(transitive)
- Removedsecure-keys@1.0.0(transitive)
- Removedyargs@16.2.0(transitive)
- Removedyargs-parser@20.2.9(transitive)
Updatedyargs@^17.0.0