Skip to content

Commit cee3fd9

Browse files
nlffritzy
authored andcommitted
deps: @npmcli/config@5.0.0
1 parent a35c784 commit cee3fd9

File tree

5 files changed

+143
-105
lines changed

5 files changed

+143
-105
lines changed
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict'
2+
3+
class ErrInvalidAuth extends Error {
4+
constructor (problems) {
5+
let message = 'Invalid auth configuration found: '
6+
message += problems.map((problem) => {
7+
if (problem.action === 'delete') {
8+
return `\`${problem.key}\` is not allowed in ${problem.where} config`
9+
} else if (problem.action === 'rename') {
10+
return `\`${problem.from}\` must be renamed to \`${problem.to}\` in ${problem.where} config`
11+
}
12+
}).join(', ')
13+
message += '\nPlease run `npm config fix` to repair your configuration.`'
14+
super(message)
15+
this.code = 'ERR_INVALID_AUTH'
16+
this.problems = problems
17+
}
18+
}
19+
20+
module.exports = {
21+
ErrInvalidAuth,
22+
}

node_modules/@npmcli/config/lib/index.js

+106-92
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ const parseField = require('./parse-field.js')
5151
const typeDescription = require('./type-description.js')
5252
const setEnvs = require('./set-envs.js')
5353

54+
const {
55+
ErrInvalidAuth,
56+
} = require('./errors.js')
57+
5458
// types that can be saved back to
5559
const confFileTypes = new Set([
5660
'global',
@@ -280,26 +284,10 @@ class Config {
280284
await this.loadGlobalConfig()
281285
process.emit('timeEnd', 'config:load:global')
282286

283-
// warn if anything is not valid
284-
process.emit('time', 'config:load:validate')
285-
this.validate()
286-
process.emit('timeEnd', 'config:load:validate')
287-
288287
// set this before calling setEnvs, so that we don't have to share
289288
// symbols, as that module also does a bunch of get operations
290289
this[_loaded] = true
291290

292-
process.emit('time', 'config:load:credentials')
293-
const reg = this.get('registry')
294-
const creds = this.getCredentialsByURI(reg)
295-
// ignore this error because a failed set will strip out anything that
296-
// might be a security hazard, which was the intention.
297-
try {
298-
this.setCredentialsByURI(reg, creds)
299-
// eslint-disable-next-line no-empty
300-
} catch (_) {}
301-
process.emit('timeEnd', 'config:load:credentials')
302-
303291
// set proper globalPrefix now that everything is loaded
304292
this.globalPrefix = this.get('prefix')
305293

@@ -399,15 +387,58 @@ class Config {
399387
validate (where) {
400388
if (!where) {
401389
let valid = true
402-
for (const [entryWhere] of this.data.entries()) {
390+
const authProblems = []
391+
392+
for (const entryWhere of this.data.keys()) {
403393
// no need to validate our defaults, we know they're fine
404394
// cli was already validated when parsed the first time
405395
if (entryWhere === 'default' || entryWhere === 'builtin' || entryWhere === 'cli') {
406396
continue
407397
}
408398
const ret = this.validate(entryWhere)
409399
valid = valid && ret
400+
401+
if (['global', 'user', 'project'].includes(entryWhere)) {
402+
// after validating everything else, we look for old auth configs we no longer support
403+
// if these keys are found, we build up a list of them and the appropriate action and
404+
// attach it as context on the thrown error
405+
406+
// first, keys that should be removed
407+
for (const key of ['_authtoken', '-authtoken']) {
408+
if (this.get(key, entryWhere)) {
409+
authProblems.push({ action: 'delete', key, where: entryWhere })
410+
}
411+
}
412+
413+
// NOTE we pull registry without restricting to the current 'where' because we want to
414+
// suggest scoping things to the registry they would be applied to, which is the default
415+
// regardless of where it was defined
416+
const nerfedReg = nerfDart(this.get('registry'))
417+
// keys that should be nerfed but currently are not
418+
for (const key of ['_auth', '_authToken', 'username', '_password']) {
419+
if (this.get(key, entryWhere)) {
420+
// username and _password must both exist in the same file to be recognized correctly
421+
if (key === 'username' && !this.get('_password', entryWhere)) {
422+
authProblems.push({ action: 'delete', key, where: entryWhere })
423+
} else if (key === '_password' && !this.get('username', entryWhere)) {
424+
authProblems.push({ action: 'delete', key, where: entryWhere })
425+
} else {
426+
authProblems.push({
427+
action: 'rename',
428+
from: key,
429+
to: `${nerfedReg}:${key}`,
430+
where: entryWhere,
431+
})
432+
}
433+
}
434+
}
435+
}
410436
}
437+
438+
if (authProblems.length) {
439+
throw new ErrInvalidAuth(authProblems)
440+
}
441+
411442
return valid
412443
} else {
413444
const obj = this.data.get(where)
@@ -423,6 +454,40 @@ class Config {
423454
}
424455
}
425456

457+
// fixes problems identified by validate(), accepts the 'problems' property from a thrown
458+
// ErrInvalidAuth to avoid having to check everything again
459+
repair (problems) {
460+
if (!problems) {
461+
try {
462+
this.validate()
463+
} catch (err) {
464+
// coverage skipped here because we don't need to test re-throwing an error
465+
// istanbul ignore next
466+
if (err.code !== 'ERR_INVALID_AUTH') {
467+
throw err
468+
}
469+
470+
problems = err.problems
471+
} finally {
472+
if (!problems) {
473+
problems = []
474+
}
475+
}
476+
}
477+
478+
for (const problem of problems) {
479+
// coverage disabled for else branch because it doesn't do anything and shouldn't
480+
// istanbul ignore else
481+
if (problem.action === 'delete') {
482+
this.delete(problem.key, problem.where)
483+
} else if (problem.action === 'rename') {
484+
const old = this.get(problem.from, problem.where)
485+
this.set(problem.to, old, problem.where)
486+
this.delete(problem.from, problem.where)
487+
}
488+
}
489+
}
490+
426491
// Returns true if the value is coming directly from the source defined
427492
// in default definitions, if the current value for the key config is
428493
// coming from any other different source, returns false
@@ -644,21 +709,19 @@ class Config {
644709
if (!confFileTypes.has(where)) {
645710
throw new Error('invalid config location param: ' + where)
646711
}
712+
647713
const conf = this.data.get(where)
648714
conf[_raw] = { ...conf.data }
649715
conf[_loadError] = null
650716

651-
// upgrade auth configs to more secure variants before saving
652717
if (where === 'user') {
653-
const reg = this.get('registry')
654-
const creds = this.getCredentialsByURI(reg)
655-
// we ignore this error because the failed set already removed
656-
// anything that might be a security hazard, and it won't be
657-
// saved back to the .npmrc file, so we're good.
658-
try {
659-
this.setCredentialsByURI(reg, creds)
660-
// eslint-disable-next-line no-empty
661-
} catch (_) {}
718+
// if email is nerfed, then we want to de-nerf it
719+
const nerfed = nerfDart(this.get('registry'))
720+
const email = this.get(`${nerfed}:email`, 'user')
721+
if (email) {
722+
this.delete(`${nerfed}:email`, 'user')
723+
this.set('email', email, 'user')
724+
}
662725
}
663726

664727
const iniData = ini.stringify(conf.data).trim() + '\n'
@@ -686,14 +749,17 @@ class Config {
686749
const nerfed = nerfDart(uri)
687750
const def = nerfDart(this.get('registry'))
688751
if (def === nerfed) {
689-
// do not delete email, that shouldn't be nerfed any more.
690-
// just delete the nerfed copy, if one exists.
691752
this.delete(`-authtoken`, 'user')
692753
this.delete(`_authToken`, 'user')
693754
this.delete(`_authtoken`, 'user')
694755
this.delete(`_auth`, 'user')
695756
this.delete(`_password`, 'user')
696757
this.delete(`username`, 'user')
758+
// de-nerf email if it's nerfed to the default registry
759+
const email = this.get(`${nerfed}:email`, 'user')
760+
if (email) {
761+
this.set('email', email, 'user')
762+
}
697763
}
698764
this.delete(`${nerfed}:_authToken`, 'user')
699765
this.delete(`${nerfed}:_auth`, 'user')
@@ -706,28 +772,9 @@ class Config {
706772

707773
setCredentialsByURI (uri, { token, username, password, email, certfile, keyfile }) {
708774
const nerfed = nerfDart(uri)
709-
const def = nerfDart(this.get('registry'))
710775

711-
if (def === nerfed) {
712-
// remove old style auth info not limited to a single registry
713-
this.delete('_password', 'user')
714-
this.delete('username', 'user')
715-
this.delete('_auth', 'user')
716-
this.delete('_authtoken', 'user')
717-
this.delete('-authtoken', 'user')
718-
this.delete('_authToken', 'user')
719-
}
720-
721-
// email used to be nerfed always. if we're using the default
722-
// registry, de-nerf it.
723-
if (nerfed === def) {
724-
email = email ||
725-
this.get('email', 'user') ||
726-
this.get(`${nerfed}:email`, 'user')
727-
if (email) {
728-
this.set('email', email, 'user')
729-
}
730-
}
776+
// email is either provided, a top level key, or nothing
777+
email = email || this.get('email', 'user')
731778

732779
// field that hasn't been used as documented for a LONG time,
733780
// and as of npm 7.10.0, isn't used at all. We just always
@@ -765,15 +812,17 @@ class Config {
765812
// this has to be a bit more complicated to support legacy data of all forms
766813
getCredentialsByURI (uri) {
767814
const nerfed = nerfDart(uri)
815+
const def = nerfDart(this.get('registry'))
768816
const creds = {}
769817

770-
const deprecatedAuthWarning = [
771-
'`_auth`, `_authToken`, `username` and `_password` must be scoped to a registry.',
772-
'see `npm help npmrc` for more information.',
773-
].join(' ')
774-
818+
// email is handled differently, it used to always be nerfed and now it never should be
819+
// if it's set nerfed to the default registry, then we copy it to the unnerfed key
820+
// TODO: evaluate removing 'email' from the credentials object returned here
775821
const email = this.get(`${nerfed}:email`) || this.get('email')
776822
if (email) {
823+
if (nerfed === def) {
824+
this.set('email', email, 'user')
825+
}
777826
creds.email = email
778827
}
779828

@@ -785,13 +834,8 @@ class Config {
785834
// cert/key may be used in conjunction with other credentials, thus no `return`
786835
}
787836

788-
const defaultToken = nerfDart(this.get('registry')) && this.get('_authToken')
789-
const tokenReg = this.get(`${nerfed}:_authToken`) || defaultToken
790-
837+
const tokenReg = this.get(`${nerfed}:_authToken`)
791838
if (tokenReg) {
792-
if (tokenReg === defaultToken) {
793-
log.warn('config', deprecatedAuthWarning)
794-
}
795839
creds.token = tokenReg
796840
return creds
797841
}
@@ -816,37 +860,7 @@ class Config {
816860
return creds
817861
}
818862

819-
// at this point, we can only use the values if the URI is the
820-
// default registry.
821-
const defaultNerf = nerfDart(this.get('registry'))
822-
if (nerfed !== defaultNerf) {
823-
return creds
824-
}
825-
826-
const userDef = this.get('username')
827-
const passDef = this.get('_password')
828-
if (userDef && passDef) {
829-
log.warn('config', deprecatedAuthWarning)
830-
creds.username = userDef
831-
creds.password = Buffer.from(passDef, 'base64').toString('utf8')
832-
const auth = `${creds.username}:${creds.password}`
833-
creds.auth = Buffer.from(auth, 'utf8').toString('base64')
834-
return creds
835-
}
836-
837-
// Handle the old-style _auth=<base64> style for the default
838-
// registry, if set.
839-
const auth = this.get('_auth')
840-
if (!auth) {
841-
return creds
842-
}
843-
844-
log.warn('config', deprecatedAuthWarning)
845-
const authDecode = Buffer.from(auth, 'base64').toString('utf8')
846-
const authSplit = authDecode.split(':')
847-
creds.username = authSplit.shift()
848-
creds.password = authSplit.join(':')
849-
creds.auth = auth
863+
// at this point, nothing else is usable so just return what we do have
850864
return creds
851865
}
852866

node_modules/@npmcli/config/package.json

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@npmcli/config",
3-
"version": "4.2.2",
3+
"version": "5.0.0",
44
"files": [
55
"bin/",
66
"lib/"
@@ -16,9 +16,6 @@
1616
"scripts": {
1717
"test": "tap",
1818
"snap": "tap",
19-
"preversion": "npm test",
20-
"postversion": "npm publish",
21-
"prepublishOnly": "git push origin --follow-tags",
2219
"lint": "eslint \"**/*.js\"",
2320
"postlint": "template-oss-check",
2421
"lintfix": "npm run lint -- --fix",
@@ -27,11 +24,15 @@
2724
},
2825
"tap": {
2926
"check-coverage": true,
30-
"coverage-map": "map.js"
27+
"coverage-map": "map.js",
28+
"nyc-arg": [
29+
"--exclude",
30+
"tap-snapshots/**"
31+
]
3132
},
3233
"devDependencies": {
3334
"@npmcli/eslint-config": "^3.0.1",
34-
"@npmcli/template-oss": "3.6.0",
35+
"@npmcli/template-oss": "4.5.0",
3536
"tap": "^16.0.1"
3637
},
3738
"dependencies": {
@@ -45,10 +46,10 @@
4546
"walk-up-path": "^1.0.0"
4647
},
4748
"engines": {
48-
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
49+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
4950
},
5051
"templateOSS": {
5152
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
52-
"version": "3.6.0"
53+
"version": "4.5.0"
5354
}
5455
}

package-lock.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
"@isaacs/string-locale-compare": "^1.1.0",
9393
"@npmcli/arborist": "^6.0.0-pre.4",
9494
"@npmcli/ci-detect": "^3.0.0",
95-
"@npmcli/config": "^4.2.1",
95+
"@npmcli/config": "^5.0.0",
9696
"@npmcli/fs": "^2.1.0",
9797
"@npmcli/map-workspaces": "^2.0.3",
9898
"@npmcli/package-json": "^2.0.0",
@@ -2164,9 +2164,10 @@
21642164
}
21652165
},
21662166
"node_modules/@npmcli/config": {
2167-
"version": "4.2.2",
2167+
"version": "5.0.0",
2168+
"resolved": "https://registry.npmjs.org/@npmcli/config/-/config-5.0.0.tgz",
2169+
"integrity": "sha512-TfJ3IRw5eKtzvzCxWbmy74KfO1ikKoWr2oPzpugo3RqSneAF/PNFZuSAuubvyv5qKjAj0hU4BC7VI2o3eOAT2A==",
21682170
"inBundle": true,
2169-
"license": "ISC",
21702171
"dependencies": {
21712172
"@npmcli/map-workspaces": "^2.0.2",
21722173
"ini": "^3.0.0",
@@ -2178,7 +2179,7 @@
21782179
"walk-up-path": "^1.0.0"
21792180
},
21802181
"engines": {
2181-
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
2182+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
21822183
}
21832184
},
21842185
"node_modules/@npmcli/disparity-colors": {

0 commit comments

Comments
 (0)