Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nconf mutates defaults #315

Open
dvisztempacct opened this issue Apr 5, 2019 · 4 comments
Open

Nconf mutates defaults #315

dvisztempacct opened this issue Apr 5, 2019 · 4 comments

Comments

@dvisztempacct
Copy link

For some reason, nconf mutates the value passed to defaults:

$ cat ./index.js
const nconf = require('nconf')
const defaults = {
  foo: {
    bar: 'default-foobar'
  }
}
nconf.argv()
  .env('__')
  .defaults(defaults)
nconf.get()
console.log(defaults.foo.bar == 'default-foobar')
$ node ./index
true
$ foo__bar=nope node ./index
false
@dvisztempacct
Copy link
Author

 $ npm ls nconf
[email protected] /home/hdon/nconf-bug
└── [email protected]

@ebickle
Copy link
Contributor

ebickle commented Jun 13, 2019

Confirmed the issue here as well.

It's a bug in /lib/nconf/stores/memory.js(200). If the value being merged is an object and a key of the same name doesn't exist in the target, the object reference is assigned rather than a copy. That works fine for a single merge, but once a subsequent merge happens nconf will start rewriting the referenced object.

The same appears to be true for arrays.

The code will need to be modified so that object and array values that don't exist in the target are deep cloned.

@LongLiveCHIEF
Copy link

So I think i'm running into this, but want to confirm. I'm creating a defaults object and one of the default values is a function that auto-generates a password.

I'm usiing .env() .argv() and .defaults , and then after all of that, I'm using nconf.use('memory') and then mapping over each key to make sure all the keys are in the memory store. Regardless of what order i call .defaults() it's generating a new password each time, instead of using the value found in the env store.

I'm intended to write a submit a PR for a new format called shellvars, but until then I want to confirm this is due to this bug. Can you check out the code below and confirm?

Toggle to view code
#!/usr/bin/env node
const path = require('path')
require('dotenv').config({
  path: path.join(__dirname, '..', '.env')
})
const nconf = require('nconf')
const generatePassword = require('password-generator')
const fs = require('fs')

const configKeys = ['POSTGRES_DB', 'POSTGRES_USER', 'POSTGRES_PASSWORD', 'DB_HOST', 'DB_PORT', 'sslmode']
const defaults = {
'POSTGRES_DB': 'my_db',
'POSTGRES_USER': 'mydbadmin',
'POSTGRES_PASSWORD': generatePassword(12, false),
'DB_HOST': 'localhost',
'DB_PORT': 5432,
'sslmode': 'disable'
}
// local env vars into config object,
// only loading keys specified,
// and casting values to types if possible
nconf
.defaults(defaults)
.env(configKeys)
.argv({
parseValues: true
})

const config = configKeys.reduce((accumulator,value)=>{
return {
...accumulator,
[value]: nconf.get(value)
}
},{})
//DATABASE_URL used by pgweb
const DB_URL = postgres://${config.POSTGRES_USER}:${config.POSTGRES_PASSWORD}@postgres:${config.DB_PORT}/${config.POSTGRES_DB}?sslmode=${config.sslmode}
// POSTGRES_URL used by server api
const PG_URL = postgres://${config.POSTGRES_USER}:${config.POSTGRES_PASSWORD}@${config.DB_HOST}:5432/${config.POSTGRES_DB}

nconf.use('memory')
// map merged configs into a single store and set POSTGRES_URL and DATABASE_URL to that config store
Object.keys(configKeys).map( key => nconf.set(key, nconf.get(key)))
nconf.set('DATABASE_URL', DB_URL)
nconf.set('POSTGRES_URL', PG_URL)
// load and save config store to disk in shellvars format
nconf.load((err,data) => {
if (err) {
console.error('error loading config into memory')
return
}
let config = Object.keys(data)
.filter(key => key.length >= 5)
.reduce((accumulator,value,index) => {
return ${accumulator}${value}=${data[value]}\n
},'')

fs.writeFile( path.resolve(__dirname, '.env'), config, err => {
  if (err) console.error('Error saving cc-server config', err.code, err.message)
  console.log('cc-server config saved')
})

})


@mmadson
Copy link

mmadson commented Aug 22, 2024

For those looking for a quick and dirty fix:

Replace:

  if (typeof target[key] !== 'object' || Array.isArray(target[key])) {
    target[key] = value;
    return true;
  }

with

  if (typeof target[key] !== 'object' || Array.isArray(target[key])) {
    target[key] = JSON.parse(JSON.stringify(value));
    return true;
  }

in memory.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants