@npmcli/config
Advanced tools
Comparing version 4.2.2 to 5.0.0
198
lib/index.js
@@ -54,2 +54,6 @@ // TODO: set the scope config from package.json or explicit cli config | ||
const { | ||
ErrInvalidAuth, | ||
} = require('./errors.js') | ||
// types that can be saved back to | ||
@@ -284,7 +288,2 @@ const confFileTypes = new Set([ | ||
// warn if anything is not valid | ||
process.emit('time', 'config:load:validate') | ||
this.validate() | ||
process.emit('timeEnd', 'config:load:validate') | ||
// set this before calling setEnvs, so that we don't have to share | ||
@@ -294,13 +293,2 @@ // symbols, as that module also does a bunch of get operations | ||
process.emit('time', 'config:load:credentials') | ||
const reg = this.get('registry') | ||
const creds = this.getCredentialsByURI(reg) | ||
// ignore this error because a failed set will strip out anything that | ||
// might be a security hazard, which was the intention. | ||
try { | ||
this.setCredentialsByURI(reg, creds) | ||
// eslint-disable-next-line no-empty | ||
} catch (_) {} | ||
process.emit('timeEnd', 'config:load:credentials') | ||
// set proper globalPrefix now that everything is loaded | ||
@@ -405,3 +393,5 @@ this.globalPrefix = this.get('prefix') | ||
let valid = true | ||
for (const [entryWhere] of this.data.entries()) { | ||
const authProblems = [] | ||
for (const entryWhere of this.data.keys()) { | ||
// no need to validate our defaults, we know they're fine | ||
@@ -414,3 +404,44 @@ // cli was already validated when parsed the first time | ||
valid = valid && ret | ||
if (['global', 'user', 'project'].includes(entryWhere)) { | ||
// after validating everything else, we look for old auth configs we no longer support | ||
// if these keys are found, we build up a list of them and the appropriate action and | ||
// attach it as context on the thrown error | ||
// first, keys that should be removed | ||
for (const key of ['_authtoken', '-authtoken']) { | ||
if (this.get(key, entryWhere)) { | ||
authProblems.push({ action: 'delete', key, where: entryWhere }) | ||
} | ||
} | ||
// NOTE we pull registry without restricting to the current 'where' because we want to | ||
// suggest scoping things to the registry they would be applied to, which is the default | ||
// regardless of where it was defined | ||
const nerfedReg = nerfDart(this.get('registry')) | ||
// keys that should be nerfed but currently are not | ||
for (const key of ['_auth', '_authToken', 'username', '_password']) { | ||
if (this.get(key, entryWhere)) { | ||
// username and _password must both exist in the same file to be recognized correctly | ||
if (key === 'username' && !this.get('_password', entryWhere)) { | ||
authProblems.push({ action: 'delete', key, where: entryWhere }) | ||
} else if (key === '_password' && !this.get('username', entryWhere)) { | ||
authProblems.push({ action: 'delete', key, where: entryWhere }) | ||
} else { | ||
authProblems.push({ | ||
action: 'rename', | ||
from: key, | ||
to: `${nerfedReg}:${key}`, | ||
where: entryWhere, | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (authProblems.length) { | ||
throw new ErrInvalidAuth(authProblems) | ||
} | ||
return valid | ||
@@ -431,2 +462,36 @@ } else { | ||
// fixes problems identified by validate(), accepts the 'problems' property from a thrown | ||
// ErrInvalidAuth to avoid having to check everything again | ||
repair (problems) { | ||
if (!problems) { | ||
try { | ||
this.validate() | ||
} catch (err) { | ||
// coverage skipped here because we don't need to test re-throwing an error | ||
// istanbul ignore next | ||
if (err.code !== 'ERR_INVALID_AUTH') { | ||
throw err | ||
} | ||
problems = err.problems | ||
} finally { | ||
if (!problems) { | ||
problems = [] | ||
} | ||
} | ||
} | ||
for (const problem of problems) { | ||
// coverage disabled for else branch because it doesn't do anything and shouldn't | ||
// istanbul ignore else | ||
if (problem.action === 'delete') { | ||
this.delete(problem.key, problem.where) | ||
} else if (problem.action === 'rename') { | ||
const old = this.get(problem.from, problem.where) | ||
this.set(problem.to, old, problem.where) | ||
this.delete(problem.from, problem.where) | ||
} | ||
} | ||
} | ||
// Returns true if the value is coming directly from the source defined | ||
@@ -653,2 +718,3 @@ // in default definitions, if the current value for the key config is | ||
} | ||
const conf = this.data.get(where) | ||
@@ -658,13 +724,10 @@ conf[_raw] = { ...conf.data } | ||
// upgrade auth configs to more secure variants before saving | ||
if (where === 'user') { | ||
const reg = this.get('registry') | ||
const creds = this.getCredentialsByURI(reg) | ||
// we ignore this error because the failed set already removed | ||
// anything that might be a security hazard, and it won't be | ||
// saved back to the .npmrc file, so we're good. | ||
try { | ||
this.setCredentialsByURI(reg, creds) | ||
// eslint-disable-next-line no-empty | ||
} catch (_) {} | ||
// if email is nerfed, then we want to de-nerf it | ||
const nerfed = nerfDart(this.get('registry')) | ||
const email = this.get(`${nerfed}:email`, 'user') | ||
if (email) { | ||
this.delete(`${nerfed}:email`, 'user') | ||
this.set('email', email, 'user') | ||
} | ||
} | ||
@@ -697,4 +760,2 @@ | ||
if (def === nerfed) { | ||
// do not delete email, that shouldn't be nerfed any more. | ||
// just delete the nerfed copy, if one exists. | ||
this.delete(`-authtoken`, 'user') | ||
@@ -706,2 +767,7 @@ this.delete(`_authToken`, 'user') | ||
this.delete(`username`, 'user') | ||
// de-nerf email if it's nerfed to the default registry | ||
const email = this.get(`${nerfed}:email`, 'user') | ||
if (email) { | ||
this.set('email', email, 'user') | ||
} | ||
} | ||
@@ -719,25 +785,6 @@ this.delete(`${nerfed}:_authToken`, 'user') | ||
const nerfed = nerfDart(uri) | ||
const def = nerfDart(this.get('registry')) | ||
if (def === nerfed) { | ||
// remove old style auth info not limited to a single registry | ||
this.delete('_password', 'user') | ||
this.delete('username', 'user') | ||
this.delete('_auth', 'user') | ||
this.delete('_authtoken', 'user') | ||
this.delete('-authtoken', 'user') | ||
this.delete('_authToken', 'user') | ||
} | ||
// email is either provided, a top level key, or nothing | ||
email = email || this.get('email', 'user') | ||
// email used to be nerfed always. if we're using the default | ||
// registry, de-nerf it. | ||
if (nerfed === def) { | ||
email = email || | ||
this.get('email', 'user') || | ||
this.get(`${nerfed}:email`, 'user') | ||
if (email) { | ||
this.set('email', email, 'user') | ||
} | ||
} | ||
// field that hasn't been used as documented for a LONG time, | ||
@@ -779,11 +826,13 @@ // and as of npm 7.10.0, isn't used at all. We just always | ||
const nerfed = nerfDart(uri) | ||
const def = nerfDart(this.get('registry')) | ||
const creds = {} | ||
const deprecatedAuthWarning = [ | ||
'`_auth`, `_authToken`, `username` and `_password` must be scoped to a registry.', | ||
'see `npm help npmrc` for more information.', | ||
].join(' ') | ||
// email is handled differently, it used to always be nerfed and now it never should be | ||
// if it's set nerfed to the default registry, then we copy it to the unnerfed key | ||
// TODO: evaluate removing 'email' from the credentials object returned here | ||
const email = this.get(`${nerfed}:email`) || this.get('email') | ||
if (email) { | ||
if (nerfed === def) { | ||
this.set('email', email, 'user') | ||
} | ||
creds.email = email | ||
@@ -800,9 +849,4 @@ } | ||
const defaultToken = nerfDart(this.get('registry')) && this.get('_authToken') | ||
const tokenReg = this.get(`${nerfed}:_authToken`) || defaultToken | ||
const tokenReg = this.get(`${nerfed}:_authToken`) | ||
if (tokenReg) { | ||
if (tokenReg === defaultToken) { | ||
log.warn('config', deprecatedAuthWarning) | ||
} | ||
creds.token = tokenReg | ||
@@ -832,33 +876,3 @@ return creds | ||
// at this point, we can only use the values if the URI is the | ||
// default registry. | ||
const defaultNerf = nerfDart(this.get('registry')) | ||
if (nerfed !== defaultNerf) { | ||
return creds | ||
} | ||
const userDef = this.get('username') | ||
const passDef = this.get('_password') | ||
if (userDef && passDef) { | ||
log.warn('config', deprecatedAuthWarning) | ||
creds.username = userDef | ||
creds.password = Buffer.from(passDef, 'base64').toString('utf8') | ||
const auth = `${creds.username}:${creds.password}` | ||
creds.auth = Buffer.from(auth, 'utf8').toString('base64') | ||
return creds | ||
} | ||
// Handle the old-style _auth=<base64> style for the default | ||
// registry, if set. | ||
const auth = this.get('_auth') | ||
if (!auth) { | ||
return creds | ||
} | ||
log.warn('config', deprecatedAuthWarning) | ||
const authDecode = Buffer.from(auth, 'base64').toString('utf8') | ||
const authSplit = authDecode.split(':') | ||
creds.username = authSplit.shift() | ||
creds.password = authSplit.join(':') | ||
creds.auth = auth | ||
// at this point, nothing else is usable so just return what we do have | ||
return creds | ||
@@ -865,0 +879,0 @@ } |
{ | ||
"name": "@npmcli/config", | ||
"version": "4.2.2", | ||
"version": "5.0.0", | ||
"files": [ | ||
@@ -19,5 +19,2 @@ "bin/", | ||
"snap": "tap", | ||
"preversion": "npm test", | ||
"postversion": "npm publish", | ||
"prepublishOnly": "git push origin --follow-tags", | ||
"lint": "eslint \"**/*.js\"", | ||
@@ -31,7 +28,11 @@ "postlint": "template-oss-check", | ||
"check-coverage": true, | ||
"coverage-map": "map.js" | ||
"coverage-map": "map.js", | ||
"nyc-arg": [ | ||
"--exclude", | ||
"tap-snapshots/**" | ||
] | ||
}, | ||
"devDependencies": { | ||
"@npmcli/eslint-config": "^3.0.1", | ||
"@npmcli/template-oss": "3.6.0", | ||
"@npmcli/template-oss": "4.5.0", | ||
"tap": "^16.0.1" | ||
@@ -50,8 +51,8 @@ }, | ||
"engines": { | ||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" | ||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0" | ||
}, | ||
"templateOSS": { | ||
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", | ||
"version": "3.6.0" | ||
"version": "4.5.0" | ||
} | ||
} |
@@ -92,2 +92,3 @@ # `@npmcli/config` | ||
conf.load().then(() => { | ||
conf.validate() | ||
console.log('loaded ok! some-key = ' + conf.get('some-key')) | ||
@@ -214,2 +215,6 @@ }).catch(er => { | ||
Invalid auth options will cause this method to throw an error with a `code` | ||
property of `ERR_INVALID_AUTH`, and a `problems` property listing the specific | ||
concerns with the current configuration. | ||
If `where` is not set, then all config objects are validated. | ||
@@ -223,2 +228,10 @@ | ||
### `config.repair(problems)` | ||
Accept an optional array of problems (as thrown by `config.validate()`) and | ||
perform the necessary steps to resolve them. If no problems are provided, | ||
this method will call `config.validate()` internally to retrieve them. | ||
Note that you must `await config.save('user')` in order to persist the changes. | ||
### `config.isDefault(key)` | ||
@@ -225,0 +238,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50826
12
1118
261