From 08a4e007ab18c4d5496a4aeaebea3525b9c0067c Mon Sep 17 00:00:00 2001
From: "npm-cli+bot@github.com" Description
will cause the command to fail. This option does not filter the report
output, it simply changes the command's failure threshold.
This command can also audit the integrity values of the packages in your
-tree against any signatures present in the registry they were downloaded
-from. npm will attempt to download the keys from /-/npm/v1/keys
on
-each the registry used to download any given package. It will then
-check the dist.signatures
object in the package itself, and verify the
-sig
present there using the keyid
there, matching it with a key
-returned from the registry. The command for this is npm audit signatures
To ensure the integrity of packages you download from the public npm registry, or any registry that supports signatures, you can verify the registry signatures of downloaded packages using the npm CLI.
+Registry signatures can be verified using the following audit
command:
$ npm audit signatures
+
+The npm CLI supports registry signatures and signing keys provided by any registry if the following conventions are followed:
+packument
in each published version within the dist
object:"dist":{
+ "..omitted..": "..omitted..",
+ "signatures": [{
+ "keyid": "SHA256:{{SHA256_PUBLIC_KEY}}",
+ "sig": "a312b9c3cb4a1b693e8ebac5ee1ca9cc01f2661c14391917dcb111517f72370809..."
+ }]
+}
+
+See this example of a signed package from the public npm registry.
+The sig
is generated using the following template: ${package.name}@${package.version}:${package.dist.integrity}
and the keyid
has to match one of the public signing keys below.
registry-host.tld/-/npm/v1/keys
in the following format:{
+ "keys": [{
+ "expires": null,
+ "keyid": "SHA256:{{SHA256_PUBLIC_KEY}}",
+ "keytype": "ecdsa-sha2-nistp256",
+ "scheme": "ecdsa-sha2-nistp256",
+ "key": "{{B64_PUBLIC_KEY}}"
+ }]
+}
+
+Keys response:
+expires
: null or a simplified extended ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ
keydid
: sha256 fingerprint of the public keykeytype
: only ecdsa-sha2-nistp256
is currently supported by the npm CLIscheme
: only ecdsa-sha2-nistp256
is currently supported by the npm CLIkey
: base64 encoded public keySee this example key's response from the public npm registry.
There are two audit endpoints that npm may use to fetch vulnerability
information: the Bulk Advisory
endpoint and the Quick Audit
endpoint.
npm ls promzard
in npm's source tree will show:
-npm@8.14.0 /path/to/npm
+npm@8.15.0 /path/to/npm
└─┬ init-package-json@0.0.4
└── promzard@0.1.5
diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html
index cbe5fbee3e1cbb..514017fd875943 100644
--- a/deps/npm/docs/output/commands/npm.html
+++ b/deps/npm/docs/output/commands/npm.html
@@ -149,7 +149,7 @@ Table of contents
Version
-8.14.0
+8.15.0
Description
npm is the package manager for the Node JavaScript platform. It puts
modules in place so that node can find them, and manages dependency
diff --git a/deps/npm/docs/output/configuring-npm/folders.html b/deps/npm/docs/output/configuring-npm/folders.html
index 6d722466c52d83..2968dacd5ee78b 100644
--- a/deps/npm/docs/output/configuring-npm/folders.html
+++ b/deps/npm/docs/output/configuring-npm/folders.html
@@ -291,7 +291,7 @@
Example
For a graphical breakdown of what is installed where, use npm ls
.
Publishing
Upon publishing, npm will look in the node_modules
folder. If any of
-the items there are not in the bundledDependencies
array, then they will
+the items there are not in the bundleDependencies
array, then they will
not be included in the package tarball.
This allows a package maintainer to install all of their dependencies
(and dev dependencies) locally, but only re-publish those items that
diff --git a/deps/npm/docs/output/configuring-npm/package-json.html b/deps/npm/docs/output/configuring-npm/package-json.html
index 665dad43671cb9..354069b1a2c738 100644
--- a/deps/npm/docs/output/configuring-npm/package-json.html
+++ b/deps/npm/docs/output/configuring-npm/package-json.html
@@ -142,7 +142,7 @@
package.json
Table of contents
-- Description
- name
- version
- description
- keywords
- homepage
- bugs
- license
- people fields: author, contributors
- funding
- files
- main
- browser
- bin
- man
- directories
- repository
- scripts
- config
- dependencies
- devDependencies
- peerDependencies
- peerDependenciesMeta
- bundledDependencies
- optionalDependencies
- overrides
- engines
- os
- cpu
- private
- publishConfig
- workspaces
- DEFAULT VALUES
- SEE ALSO
+- Description
- name
- version
- description
- keywords
- homepage
- bugs
- license
- people fields: author, contributors
- funding
- files
- main
- browser
- bin
- man
- directories
- repository
- scripts
- config
- dependencies
- devDependencies
- peerDependencies
- peerDependenciesMeta
- bundleDependencies
- optionalDependencies
- overrides
- engines
- os
- cpu
- private
- publishConfig
- workspaces
- DEFAULT VALUES
- SEE ALSO
Description
@@ -772,19 +772,19 @@ peerDependenciesMeta
if the soy-milk
package is not installed on the host. This allows you to
integrate and interact with a variety of host packages without requiring
all of them to be installed.
-bundledDependencies
+bundleDependencies
This defines an array of package names that will be bundled when publishing
the package.
In cases where you need to preserve npm packages locally or have them
available through a single file download, you can bundle the packages in a
-tarball file by specifying the package names in the bundledDependencies
+tarball file by specifying the package names in the bundleDependencies
array and executing npm pack
.
For example:
If we define a package.json like this:
{
"name": "awesome-web-framework",
"version": "1.0.0",
- "bundledDependencies": [
+ "bundleDependencies": [
"renderized",
"super-streams"
]
@@ -794,8 +794,8 @@ bundledDependencies
This file contains the dependencies renderized
and super-streams
which
can be installed in a new project by executing npm install awesome-web-framework-1.0.0.tgz
. Note that the package names do not
include any versions, as that information is specified in dependencies
.
-If this is spelled "bundleDependencies"
, then that is also honored.
-Alternatively, "bundledDependencies"
can be defined as a boolean value. A
+
If this is spelled "bundledDependencies"
, then that is also honored.
+Alternatively, "bundleDependencies"
can be defined as a boolean value. A
value of true
will bundle all dependencies, a value of false
will bundle
none.
optionalDependencies
diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html
index 900c08b3623d73..79bf005d61e15e 100644
--- a/deps/npm/docs/output/using-npm/config.html
+++ b/deps/npm/docs/output/using-npm/config.html
@@ -428,8 +428,9 @@ cert
newlines replaced by the string "\n". For example:
cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----"
-It is not the path to a certificate file (and there is no "certfile"
-option).
+It is not the path to a certificate file, though you can set a
+registry-scoped "certfile" path like
+"//other-registry.tld/:certfile=/path/to/cert.pem".
ci-name
@@ -907,7 +908,8 @@ key
format with newlines replaced by the string "\n". For example:
key="-----BEGIN PRIVATE KEY-----\nXXXX\nXXXX\n-----END PRIVATE KEY-----"
-It is not the path to a key file (and there is no "keyfile" option).
+It is not the path to a key file, though you can set a registry-scoped
+"keyfile" path like "//other-registry.tld/:keyfile=/path/to/key.pem".
legacy-bundling
diff --git a/deps/npm/lib/auth/sso.js b/deps/npm/lib/auth/sso.js
index 9812a18cb99ca6..621ead5d21b658 100644
--- a/deps/npm/lib/auth/sso.js
+++ b/deps/npm/lib/auth/sso.js
@@ -52,7 +52,7 @@ const login = async (npm, { creds, registry, scope }) => {
authType: ssoType,
}
- const { token, sso } = await otplease(opts,
+ const { token, sso } = await otplease(npm, opts,
opts => profile.loginCouch(auth.username, auth.password, opts)
)
diff --git a/deps/npm/lib/commands/access.js b/deps/npm/lib/commands/access.js
index bc8ce48bacdad5..0a80da8ddd0066 100644
--- a/deps/npm/lib/commands/access.js
+++ b/deps/npm/lib/commands/access.js
@@ -180,7 +180,7 @@ class Access extends BaseCommand {
modifyPackage (pkg, opts, fn, requireScope = true) {
return this.getPackage(pkg, requireScope)
- .then(pkgName => otplease(opts, opts => fn(pkgName, opts)))
+ .then(pkgName => otplease(this.npm, opts, opts => fn(pkgName, opts)))
}
async getPackage (name, requireScope) {
diff --git a/deps/npm/lib/commands/adduser.js b/deps/npm/lib/commands/adduser.js
index 5e23f40dc59680..2853269ef3deee 100644
--- a/deps/npm/lib/commands/adduser.js
+++ b/deps/npm/lib/commands/adduser.js
@@ -30,7 +30,7 @@ class AddUser extends BaseCommand {
log.disableProgress()
log.warn('adduser',
- '`adduser` will be split into `login` and `register in a future version.'
+ '`adduser` will be split into `login` and `register` in a future version.'
+ ' `adduser` will become an alias of `register`.'
+ ' `login` (currently an alias) will become its own command.')
log.notice('', `Log in on ${replaceInfo(registry)}`)
diff --git a/deps/npm/lib/commands/deprecate.js b/deps/npm/lib/commands/deprecate.js
index 862c214dbe592f..068bfdbcec717f 100644
--- a/deps/npm/lib/commands/deprecate.js
+++ b/deps/npm/lib/commands/deprecate.js
@@ -60,7 +60,7 @@ class Deprecate extends BaseCommand {
packument.versions[v].deprecated = msg
})
- return otplease(this.npm.flatOptions, opts => fetch(uri, {
+ return otplease(this.npm, this.npm.flatOptions, opts => fetch(uri, {
...opts,
spec: p,
method: 'PUT',
diff --git a/deps/npm/lib/commands/dist-tag.js b/deps/npm/lib/commands/dist-tag.js
index e74a3f1d435c9b..8052e4f7e4e38c 100644
--- a/deps/npm/lib/commands/dist-tag.js
+++ b/deps/npm/lib/commands/dist-tag.js
@@ -116,7 +116,7 @@ class DistTag extends BaseCommand {
},
spec,
}
- await otplease(reqOpts, reqOpts => regFetch(url, reqOpts))
+ await otplease(this.npm, reqOpts, reqOpts => regFetch(url, reqOpts))
this.npm.output(`+${t}: ${spec.name}@${version}`)
}
@@ -142,7 +142,7 @@ class DistTag extends BaseCommand {
method: 'DELETE',
spec,
}
- await otplease(reqOpts, reqOpts => regFetch(url, reqOpts))
+ await otplease(this.npm, reqOpts, reqOpts => regFetch(url, reqOpts))
this.npm.output(`-${tag}: ${spec.name}@${version}`)
}
diff --git a/deps/npm/lib/commands/hook.js b/deps/npm/lib/commands/hook.js
index a4619802d84298..bb3a34b8d2d1b0 100644
--- a/deps/npm/lib/commands/hook.js
+++ b/deps/npm/lib/commands/hook.js
@@ -22,7 +22,7 @@ class Hook extends BaseCommand {
static ignoreImplicitWorkspace = true
async exec (args) {
- return otplease({
+ return otplease(this.npm, {
...this.npm.flatOptions,
}, (opts) => {
switch (args[0]) {
diff --git a/deps/npm/lib/commands/org.js b/deps/npm/lib/commands/org.js
index e2202a9e9cf3b4..599b4b9c8758a3 100644
--- a/deps/npm/lib/commands/org.js
+++ b/deps/npm/lib/commands/org.js
@@ -33,7 +33,7 @@ class Org extends BaseCommand {
}
async exec ([cmd, orgname, username, role], cb) {
- return otplease({
+ return otplease(this.npm, {
...this.npm.flatOptions,
}, opts => {
switch (cmd) {
diff --git a/deps/npm/lib/commands/owner.js b/deps/npm/lib/commands/owner.js
index 732bb40a300502..824b64e044ecf2 100644
--- a/deps/npm/lib/commands/owner.js
+++ b/deps/npm/lib/commands/owner.js
@@ -202,7 +202,7 @@ class Owner extends BaseCommand {
const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}`
try {
- const res = await otplease(this.npm.flatOptions, opts => {
+ const res = await otplease(this.npm, this.npm.flatOptions, opts => {
return npmFetch.json(dataPath, {
...opts,
method: 'PUT',
diff --git a/deps/npm/lib/commands/profile.js b/deps/npm/lib/commands/profile.js
index fcf0eb7d53fa69..27060cf73a6502 100644
--- a/deps/npm/lib/commands/profile.js
+++ b/deps/npm/lib/commands/profile.js
@@ -221,7 +221,7 @@ class Profile extends BaseCommand {
newUser[prop] = value
- const result = await otplease(conf, conf => npmProfile.set(newUser, conf))
+ const result = await otplease(this.npm, conf, conf => npmProfile.set(newUser, conf))
if (this.npm.config.get('json')) {
this.npm.output(JSON.stringify({ [prop]: result[prop] }, null, 2))
diff --git a/deps/npm/lib/commands/publish.js b/deps/npm/lib/commands/publish.js
index 579f5d6e74e67c..3d17866a684a45 100644
--- a/deps/npm/lib/commands/publish.js
+++ b/deps/npm/lib/commands/publish.js
@@ -61,7 +61,8 @@ class Publish extends BaseCommand {
throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
}
- const opts = { ...this.npm.flatOptions }
+ const opts = { ...this.npm.flatOptions, progress: false }
+ log.disableProgress()
// you can publish name@version, ./foo.tgz, etc.
// even though the default is the 'file:.' cwd.
@@ -101,7 +102,7 @@ class Publish extends BaseCommand {
const resolved = npa.resolve(manifest.name, manifest.version)
const registry = npmFetch.pickRegistry(resolved, opts)
const creds = this.npm.config.getCredentialsByURI(registry)
- const noCreds = !creds.token && !creds.username
+ const noCreds = !(creds.token || creds.username || creds.certfile && creds.keyfile)
const outputRegistry = replaceInfo(registry)
if (noCreds) {
@@ -116,7 +117,7 @@ class Publish extends BaseCommand {
log.notice('', `Publishing to ${outputRegistry}${dryRun ? ' (dry-run)' : ''}`)
if (!dryRun) {
- await otplease(opts, opts => libpub(manifest, tarballData, opts))
+ await otplease(this.npm, opts, opts => libpub(manifest, tarballData, opts))
}
if (spec.type === 'directory' && !ignoreScripts) {
diff --git a/deps/npm/lib/commands/team.js b/deps/npm/lib/commands/team.js
index 640002456acad0..2d4fc663715e4e 100644
--- a/deps/npm/lib/commands/team.js
+++ b/deps/npm/lib/commands/team.js
@@ -44,7 +44,7 @@ class Team extends BaseCommand {
// XXX: "description" option to libnpmteam is used as a description of the
// team, but in npm's options, this is a boolean meaning "show the
// description in npm search output". Hence its being set to null here.
- await otplease({ ...this.npm.flatOptions }, opts => {
+ await otplease(this.npm, { ...this.npm.flatOptions }, opts => {
entity = entity.replace(/^@/, '')
switch (cmd) {
case 'create': return this.create(entity, opts)
diff --git a/deps/npm/lib/commands/token.js b/deps/npm/lib/commands/token.js
index edfb07b9d3a9a4..cf3b8cbee53a4c 100644
--- a/deps/npm/lib/commands/token.js
+++ b/deps/npm/lib/commands/token.js
@@ -121,7 +121,7 @@ class Token extends BaseCommand {
})
await Promise.all(
toRemove.map(key => {
- return otplease(conf, conf => {
+ return otplease(this.npm, conf, conf => {
return profile.removeToken(key, conf)
})
})
@@ -146,7 +146,7 @@ class Token extends BaseCommand {
const validCIDR = this.validateCIDRList(cidr)
log.info('token', 'creating')
return pulseTillDone.withPromise(
- otplease(conf, conf => {
+ otplease(this.npm, conf, conf => {
return profile.createToken(password, readonly, validCIDR, conf)
})
)
diff --git a/deps/npm/lib/commands/unpublish.js b/deps/npm/lib/commands/unpublish.js
index ab929d98cadfa3..0e5ef3dc5e91d3 100644
--- a/deps/npm/lib/commands/unpublish.js
+++ b/deps/npm/lib/commands/unpublish.js
@@ -130,7 +130,7 @@ class Unpublish extends BaseCommand {
}
if (!dryRun) {
- await otplease(opts, opts => libunpub(spec, opts))
+ await otplease(this.npm, opts, opts => libunpub(spec, opts))
}
if (!silent) {
this.npm.output(`- ${pkgName}${pkgVersion}`)
diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js
index 665ed1efe5e61c..7d6af2473f2bd6 100644
--- a/deps/npm/lib/utils/config/definitions.js
+++ b/deps/npm/lib/utils/config/definitions.js
@@ -436,8 +436,8 @@ define('cert', {
cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
\`\`\`
- It is _not_ the path to a certificate file (and there is no "certfile"
- option).
+ It is _not_ the path to a certificate file, though you can set a registry-scoped
+ "certfile" path like "//other-registry.tld/:certfile=/path/to/cert.pem".
`,
flatten,
})
@@ -1118,7 +1118,8 @@ define('key', {
key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----"
\`\`\`
- It is _not_ the path to a key file (and there is no "keyfile" option).
+ It is _not_ the path to a key file, though you can set a registry-scoped
+ "keyfile" path like "//other-registry.tld/:keyfile=/path/to/key.pem".
`,
flatten,
})
diff --git a/deps/npm/lib/utils/get-identity.js b/deps/npm/lib/utils/get-identity.js
index f4aedb89b39573..41d882473ab7b1 100644
--- a/deps/npm/lib/utils/get-identity.js
+++ b/deps/npm/lib/utils/get-identity.js
@@ -9,8 +9,8 @@ module.exports = async (npm, opts) => {
return creds.username
}
- // No username, but we have a token; fetch the username from registry
- if (creds.token) {
+ // No username, but we have other credentials; fetch the username from registry
+ if (creds.token || creds.certfile && creds.keyfile) {
const registryData = await npmFetch.json('/-/whoami', { ...opts })
return registryData.username
}
diff --git a/deps/npm/lib/utils/otplease.js b/deps/npm/lib/utils/otplease.js
index 83985b6bc170d0..0e20e7a797ae04 100644
--- a/deps/npm/lib/utils/otplease.js
+++ b/deps/npm/lib/utils/otplease.js
@@ -1,17 +1,46 @@
-async function otplease (opts, fn) {
+async function otplease (npm, opts, fn) {
try {
return await fn(opts)
} catch (err) {
- const readUserInfo = require('./read-user-info.js')
- if (err.code !== 'EOTP' && (err.code !== 'E401' || !/one-time pass/.test(err.body))) {
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
throw err
- } else if (!process.stdin.isTTY || !process.stdout.isTTY) {
- throw err
- } else {
+ }
+
+ if (isWebOTP(err)) {
+ const webAuth = require('./web-auth')
+ const openUrlPrompt = require('./open-url-prompt')
+
+ const openerPromise = (url, emitter) =>
+ openUrlPrompt(
+ npm,
+ url,
+ 'Authenticate your account at',
+ 'Press ENTER to open in the browser...',
+ emitter
+ )
+ const otp = await webAuth(openerPromise, err.body.authUrl, err.body.doneUrl, opts)
+ return await fn({ ...opts, otp })
+ }
+
+ if (isClassicOTP(err)) {
+ const readUserInfo = require('./read-user-info.js')
const otp = await readUserInfo.otp('This operation requires a one-time password.\nEnter OTP:')
return await fn({ ...opts, otp })
}
+
+ throw err
+ }
+}
+
+function isWebOTP (err) {
+ if (!err.code === 'EOTP' || !err.body) {
+ return false
}
+ return err.body.authUrl && err.body.doneUrl
+}
+
+function isClassicOTP (err) {
+ return err.code === 'EOTP' || (err.code === 'E401' && /one-time pass/.test(err.body))
}
module.exports = otplease
diff --git a/deps/npm/lib/utils/web-auth.js b/deps/npm/lib/utils/web-auth.js
new file mode 100644
index 00000000000000..ce551687098fc8
--- /dev/null
+++ b/deps/npm/lib/utils/web-auth.js
@@ -0,0 +1,20 @@
+const EventEmitter = require('events')
+const { webAuthCheckLogin } = require('npm-profile')
+
+async function webAuth (opener, initialUrl, doneUrl, opts) {
+ const doneEmitter = new EventEmitter()
+
+ const openPromise = opener(initialUrl, doneEmitter)
+ const webAuthCheckPromise = webAuthCheckLogin(doneUrl, { ...opts, cache: false })
+ .then(authResult => {
+ // cancel open prompt if it's present
+ doneEmitter.emit('abort')
+
+ return authResult.token
+ })
+
+ await openPromise
+ return await webAuthCheckPromise
+}
+
+module.exports = webAuth
diff --git a/deps/npm/man/man1/npm-audit.1 b/deps/npm/man/man1/npm-audit.1
index 63ef87d0a7126a..ac628b8a80c517 100644
--- a/deps/npm/man/man1/npm-audit.1
+++ b/deps/npm/man/man1/npm-audit.1
@@ -31,14 +31,74 @@ will cause the command to fail\. This option does not filter the report
output, it simply changes the command's failure threshold\.
.SS Audit Signatures
.P
-This command can also audit the integrity values of the packages in your
-tree against any signatures present in the registry they were downloaded
-from\. npm will attempt to download the keys from \fB/\-/npm/v1/keys\fP on
-each the registry used to download any given package\. It will then
-check the \fBdist\.signatures\fP object in the package itself, and verify the
-\fBsig\fP present there using the \fBkeyid\fP there, matching it with a key
-returned from the registry\. The command for this is \fBnpm audit
-signatures\fP
+To ensure the integrity of packages you download from the public npm registry, or any registry that supports signatures, you can verify the registry signatures of downloaded packages using the npm CLI\.
+.P
+Registry signatures can be verified using the following \fBaudit\fP command:
+.P
+.RS 2
+.nf
+$ npm audit signatures
+.fi
+.RE
+.P
+The npm CLI supports registry signatures and signing keys provided by any registry if the following conventions are followed:
+.RS 0
+.IP 1. 3
+Signatures are provided in the package's \fBpackument\fP in each published version within the \fBdist\fP object:
+
+.RE
+.P
+.RS 2
+.nf
+"dist":{
+ "\.\.omitted\.\.": "\.\.omitted\.\.",
+ "signatures": [{
+ "keyid": "SHA256:{{SHA256_PUBLIC_KEY}}",
+ "sig": "a312b9c3cb4a1b693e8ebac5ee1ca9cc01f2661c14391917dcb111517f72370809\.\.\."
+ }]
+}
+.fi
+.RE
+.P
+See this example \fIhttps://registry\.npmjs\.org/light\-cycle/1\.4\.3\fR of a signed package from the public npm registry\.
+.P
+The \fBsig\fP is generated using the following template: \fB${package\.name}@${package\.version}:${package\.dist\.integrity}\fP and the \fBkeyid\fP has to match one of the public signing keys below\.
+.RS 0
+.IP 1. 3
+Public signing keys are provided at \fBregistry\-host\.tld/\-/npm/v1/keys\fP in the following format:
+
+.RE
+.P
+.RS 2
+.nf
+{
+ "keys": [{
+ "expires": null,
+ "keyid": "SHA256:{{SHA256_PUBLIC_KEY}}",
+ "keytype": "ecdsa\-sha2\-nistp256",
+ "scheme": "ecdsa\-sha2\-nistp256",
+ "key": "{{B64_PUBLIC_KEY}}"
+ }]
+}
+.fi
+.RE
+.P
+Keys response:
+.RS 0
+.IP \(bu 2
+\fBexpires\fP: null or a simplified extended ISO 8601 format: \fBYYYY\-MM\-DDTHH:mm:ss\.sssZ\fP
+.IP \(bu 2
+\fBkeydid\fP: sha256 fingerprint of the public key
+.IP \(bu 2
+\fBkeytype\fP: only \fBecdsa\-sha2\-nistp256\fP is currently supported by the npm CLI
+.IP \(bu 2
+\fBscheme\fP: only \fBecdsa\-sha2\-nistp256\fP is currently supported by the npm CLI
+.IP \(bu 2
+\fBkey\fP: base64 encoded public key
+
+.RE
+.P
+See this example key's response from the public npm registry\|\.
.SS Audit Endpoints
.P
There are two audit endpoints that npm may use to fetch vulnerability
diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1
index ad4473f2d9660a..511f481a6ea9fd 100644
--- a/deps/npm/man/man1/npm-ls.1
+++ b/deps/npm/man/man1/npm-ls.1
@@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show:
.P
.RS 2
.nf
-npm@8\.14\.0 /path/to/npm
+npm@8\.15\.0 /path/to/npm
└─┬ init\-package\-json@0\.0\.4
└── promzard@0\.1\.5
.fi
diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1
index b9f54eec1ba16d..984dbc49192dd7 100644
--- a/deps/npm/man/man1/npm.1
+++ b/deps/npm/man/man1/npm.1
@@ -4,7 +4,7 @@
.SS Synopsis
.SS Version
.P
-8\.14\.0
+8\.15\.0
.SS Description
.P
npm is the package manager for the Node JavaScript platform\. It puts
diff --git a/deps/npm/man/man5/folders.5 b/deps/npm/man/man5/folders.5
index 3dda65825fa13c..924ee0fa243d1c 100644
--- a/deps/npm/man/man5/folders.5
+++ b/deps/npm/man/man5/folders.5
@@ -198,7 +198,7 @@ For a graphical breakdown of what is installed where, use \fBnpm ls\fP\|\.
.SS Publishing
.P
Upon publishing, npm will look in the \fBnode_modules\fP folder\. If any of
-the items there are not in the \fBbundledDependencies\fP array, then they will
+the items there are not in the \fBbundleDependencies\fP array, then they will
not be included in the package tarball\.
.P
This allows a package maintainer to install all of their dependencies
diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5
index 78bfc39af2e95d..0fd5174f6aa7b6 100644
--- a/deps/npm/man/man5/package-json.5
+++ b/deps/npm/man/man5/package-json.5
@@ -924,14 +924,14 @@ Marking a peer dependency as optional ensures npm will not emit a warning
if the \fBsoy\-milk\fP package is not installed on the host\. This allows you to
integrate and interact with a variety of host packages without requiring
all of them to be installed\.
-.SS bundledDependencies
+.SS bundleDependencies
.P
This defines an array of package names that will be bundled when publishing
the package\.
.P
In cases where you need to preserve npm packages locally or have them
available through a single file download, you can bundle the packages in a
-tarball file by specifying the package names in the \fBbundledDependencies\fP
+tarball file by specifying the package names in the \fBbundleDependencies\fP
array and executing \fBnpm pack\fP\|\.
.P
For example:
@@ -943,7 +943,7 @@ If we define a package\.json like this:
{
"name": "awesome\-web\-framework",
"version": "1\.0\.0",
- "bundledDependencies": [
+ "bundleDependencies": [
"renderized",
"super\-streams"
]
@@ -957,9 +957,9 @@ can be installed in a new project by executing \fBnpm install
awesome\-web\-framework\-1\.0\.0\.tgz\fP\|\. Note that the package names do not
include any versions, as that information is specified in \fBdependencies\fP\|\.
.P
-If this is spelled \fB"bundleDependencies"\fP, then that is also honored\.
+If this is spelled \fB"bundledDependencies"\fP, then that is also honored\.
.P
-Alternatively, \fB"bundledDependencies"\fP can be defined as a boolean value\. A
+Alternatively, \fB"bundleDependencies"\fP can be defined as a boolean value\. A
value of \fBtrue\fP will bundle all dependencies, a value of \fBfalse\fP will bundle
none\.
.SS optionalDependencies
diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7
index 1674743956ff6b..bb0c65e9851255 100644
--- a/deps/npm/man/man7/config.7
+++ b/deps/npm/man/man7/config.7
@@ -400,8 +400,9 @@ cert="\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END CERTIF
.fi
.RE
.P
-It is \fInot\fR the path to a certificate file (and there is no "certfile"
-option)\.
+It is \fInot\fR the path to a certificate file, though you can set a
+registry\-scoped "certfile" path like
+"//other\-registry\.tld/:certfile=/path/to/cert\.pem"\.
.SS \fBci\-name\fP
.RS 0
.IP \(bu 2
@@ -1012,7 +1013,8 @@ key="\-\-\-\-\-BEGIN PRIVATE KEY\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END PRIVATE
.fi
.RE
.P
-It is \fInot\fR the path to a key file (and there is no "keyfile" option)\.
+It is \fInot\fR the path to a key file, though you can set a registry\-scoped
+"keyfile" path like "//other\-registry\.tld/:keyfile=/path/to/key\.pem"\.
.SS \fBlegacy\-bundling\fP
.RS 0
.IP \(bu 2
diff --git a/deps/npm/node_modules/@npmcli/config/lib/index.js b/deps/npm/node_modules/@npmcli/config/lib/index.js
index 5b7ea68e91e36e..8c2b181ca9c618 100644
--- a/deps/npm/node_modules/@npmcli/config/lib/index.js
+++ b/deps/npm/node_modules/@npmcli/config/lib/index.js
@@ -698,9 +698,11 @@ class Config {
this.delete(`${nerfed}:_password`, 'user')
this.delete(`${nerfed}:username`, 'user')
this.delete(`${nerfed}:email`, 'user')
+ this.delete(`${nerfed}:certfile`, 'user')
+ this.delete(`${nerfed}:keyfile`, 'user')
}
- setCredentialsByURI (uri, { token, username, password, email }) {
+ setCredentialsByURI (uri, { token, username, password, email, certfile, keyfile }) {
const nerfed = nerfDart(uri)
const def = nerfDart(this.get('registry'))
@@ -733,6 +735,11 @@ class Config {
this.delete(`${nerfed}:-authtoken`, 'user')
this.delete(`${nerfed}:_authtoken`, 'user')
this.delete(`${nerfed}:email`, 'user')
+ if (certfile && keyfile) {
+ this.set(`${nerfed}:certfile`, certfile, 'user')
+ this.set(`${nerfed}:keyfile`, keyfile, 'user')
+ // cert/key may be used in conjunction with other credentials, thus no `else`
+ }
if (token) {
this.set(`${nerfed}:_authToken`, token, 'user')
this.delete(`${nerfed}:_password`, 'user')
@@ -750,7 +757,7 @@ class Config {
// protects against shoulder-hacks if password is memorable, I guess?
const encoded = Buffer.from(password, 'utf8').toString('base64')
this.set(`${nerfed}:_password`, encoded, 'user')
- } else {
+ } else if (!certfile || !keyfile) {
throw new Error('No credentials to set.')
}
}
@@ -765,6 +772,14 @@ class Config {
creds.email = email
}
+ const certfileReg = this.get(`${nerfed}:certfile`)
+ const keyfileReg = this.get(`${nerfed}:keyfile`)
+ if (certfileReg && keyfileReg) {
+ creds.certfile = certfileReg
+ creds.keyfile = keyfileReg
+ // cert/key may be used in conjunction with other credentials, thus no `return`
+ }
+
const tokenReg = this.get(`${nerfed}:_authToken`) ||
this.get(`${nerfed}:_authtoken`) ||
this.get(`${nerfed}:-authtoken`) ||
diff --git a/deps/npm/node_modules/@npmcli/config/package.json b/deps/npm/node_modules/@npmcli/config/package.json
index 2cc04e05be8c97..2f561c12233dc3 100644
--- a/deps/npm/node_modules/@npmcli/config/package.json
+++ b/deps/npm/node_modules/@npmcli/config/package.json
@@ -1,6 +1,6 @@
{
"name": "@npmcli/config",
- "version": "4.1.0",
+ "version": "4.2.0",
"files": [
"bin/",
"lib/"
@@ -31,7 +31,7 @@
},
"devDependencies": {
"@npmcli/eslint-config": "^3.0.1",
- "@npmcli/template-oss": "3.3.2",
+ "@npmcli/template-oss": "3.5.0",
"tap": "^16.0.1"
},
"dependencies": {
@@ -49,6 +49,6 @@
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
- "version": "3.3.2"
+ "version": "3.5.0"
}
}
diff --git a/deps/npm/node_modules/make-fetch-happen/lib/cache/entry.js b/deps/npm/node_modules/make-fetch-happen/lib/cache/entry.js
index 7a7572ba030c9d..4307962b889d0e 100644
--- a/deps/npm/node_modules/make-fetch-happen/lib/cache/entry.js
+++ b/deps/npm/node_modules/make-fetch-happen/lib/cache/entry.js
@@ -35,6 +35,7 @@ const KEEP_RESPONSE_HEADERS = [
'etag',
'expires',
'last-modified',
+ 'link',
'location',
'pragma',
'vary',
diff --git a/deps/npm/node_modules/make-fetch-happen/package.json b/deps/npm/node_modules/make-fetch-happen/package.json
index e04c7645c4f2a1..8b21901f34fc12 100644
--- a/deps/npm/node_modules/make-fetch-happen/package.json
+++ b/deps/npm/node_modules/make-fetch-happen/package.json
@@ -1,6 +1,6 @@
{
"name": "make-fetch-happen",
- "version": "10.1.8",
+ "version": "10.2.0",
"description": "Opinionated, caching, retrying fetch client",
"main": "lib/index.js",
"files": [
diff --git a/deps/npm/node_modules/npm-registry-fetch/lib/auth.js b/deps/npm/node_modules/npm-registry-fetch/lib/auth.js
index 17da6a17d7193b..870ce0d923cd0f 100644
--- a/deps/npm/node_modules/npm-registry-fetch/lib/auth.js
+++ b/deps/npm/node_modules/npm-registry-fetch/lib/auth.js
@@ -1,4 +1,5 @@
'use strict'
+const fs = require('fs')
const npa = require('npm-package-arg')
const { URL } = require('url')
@@ -7,7 +8,8 @@ const { URL } = require('url')
const regKeyFromURI = (uri, opts) => {
const parsed = new URL(uri)
// try to find a config key indicating we have auth for this registry
- // can be one of :_authToken, :_auth, or :_password and :username
+ // can be one of :_authToken, :_auth, :_password and :username, or
+ // :certfile and :keyfile
// We walk up the "path" until we're left with just //[:],
// stopping when we reach '//'.
let regKey = `//${parsed.host}${parsed.pathname}`
@@ -26,7 +28,8 @@ const regKeyFromURI = (uri, opts) => {
const hasAuth = (regKey, opts) => (
opts[`${regKey}:_authToken`] ||
opts[`${regKey}:_auth`] ||
- opts[`${regKey}:username`] && opts[`${regKey}:_password`]
+ opts[`${regKey}:username`] && opts[`${regKey}:_password`] ||
+ opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]
)
const sameHost = (a, b) => {
@@ -44,6 +47,17 @@ const getRegistry = opts => {
return scopeReg || opts.registry
}
+const maybeReadFile = file => {
+ try {
+ return fs.readFileSync(file, 'utf8')
+ } catch (er) {
+ if (er.code !== 'ENOENT') {
+ throw er
+ }
+ return null
+ }
+}
+
const getAuth = (uri, opts = {}) => {
const { forceAuth } = opts
if (!uri) {
@@ -59,6 +73,8 @@ const getAuth = (uri, opts = {}) => {
username: forceAuth.username,
password: forceAuth._password || forceAuth.password,
auth: forceAuth._auth || forceAuth.auth,
+ certfile: forceAuth.certfile,
+ keyfile: forceAuth.keyfile,
})
}
@@ -82,6 +98,8 @@ const getAuth = (uri, opts = {}) => {
[`${regKey}:username`]: username,
[`${regKey}:_password`]: password,
[`${regKey}:_auth`]: auth,
+ [`${regKey}:certfile`]: certfile,
+ [`${regKey}:keyfile`]: keyfile,
} = opts
return new Auth({
@@ -90,15 +108,19 @@ const getAuth = (uri, opts = {}) => {
auth,
username,
password,
+ certfile,
+ keyfile,
})
}
class Auth {
- constructor ({ token, auth, username, password, scopeAuthKey }) {
+ constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) {
this.scopeAuthKey = scopeAuthKey
this.token = null
this.auth = null
this.isBasicAuth = false
+ this.cert = null
+ this.key = null
if (token) {
this.token = token
} else if (auth) {
@@ -108,6 +130,15 @@ class Auth {
this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64')
this.isBasicAuth = true
}
+ // mTLS may be used in conjunction with another auth method above
+ if (certfile && keyfile) {
+ const cert = maybeReadFile(certfile, 'utf-8')
+ const key = maybeReadFile(keyfile, 'utf-8')
+ if (cert && key) {
+ this.cert = cert
+ this.key = key
+ }
+ }
}
}
diff --git a/deps/npm/node_modules/npm-registry-fetch/lib/index.js b/deps/npm/node_modules/npm-registry-fetch/lib/index.js
index c788febc33afb4..cc331a50c09635 100644
--- a/deps/npm/node_modules/npm-registry-fetch/lib/index.js
+++ b/deps/npm/node_modules/npm-registry-fetch/lib/index.js
@@ -112,10 +112,10 @@ function regFetch (uri, /* istanbul ignore next */ opts_ = {}) {
cache: getCacheMode(opts),
cachePath: opts.cache,
ca: opts.ca,
- cert: opts.cert,
+ cert: auth.cert || opts.cert,
headers,
integrity: opts.integrity,
- key: opts.key,
+ key: auth.key || opts.key,
localAddress: opts.localAddress,
maxSockets: opts.maxSockets,
memoize: opts.memoize,
diff --git a/deps/npm/node_modules/npm-registry-fetch/package.json b/deps/npm/node_modules/npm-registry-fetch/package.json
index 5f19697c3b19d7..8a0189a9ef74d5 100644
--- a/deps/npm/node_modules/npm-registry-fetch/package.json
+++ b/deps/npm/node_modules/npm-registry-fetch/package.json
@@ -1,6 +1,6 @@
{
"name": "npm-registry-fetch",
- "version": "13.2.0",
+ "version": "13.3.0",
"description": "Fetch-based http client for use with npm registry APIs",
"main": "lib",
"files": [
diff --git a/deps/npm/package.json b/deps/npm/package.json
index 80e7a4fb0f6795..969e8e160c28c5 100644
--- a/deps/npm/package.json
+++ b/deps/npm/package.json
@@ -1,5 +1,5 @@
{
- "version": "8.14.0",
+ "version": "8.15.0",
"name": "npm",
"description": "a package manager for JavaScript",
"workspaces": [
@@ -58,7 +58,7 @@
"@isaacs/string-locale-compare": "^1.1.0",
"@npmcli/arborist": "^5.0.4",
"@npmcli/ci-detect": "^2.0.0",
- "@npmcli/config": "^4.1.0",
+ "@npmcli/config": "^4.2.0",
"@npmcli/fs": "^2.1.0",
"@npmcli/map-workspaces": "^2.0.3",
"@npmcli/package-json": "^2.0.0",
@@ -90,7 +90,7 @@
"libnpmsearch": "^5.0.2",
"libnpmteam": "^4.0.2",
"libnpmversion": "^3.0.1",
- "make-fetch-happen": "^10.1.8",
+ "make-fetch-happen": "^10.2.0",
"minipass": "^3.1.6",
"minipass-pipeline": "^1.2.4",
"mkdirp": "^1.0.4",
@@ -103,7 +103,7 @@
"npm-package-arg": "^9.1.0",
"npm-pick-manifest": "^7.0.1",
"npm-profile": "^6.2.0",
- "npm-registry-fetch": "^13.2.0",
+ "npm-registry-fetch": "^13.3.0",
"npm-user-validate": "^1.0.1",
"npmlog": "^6.0.2",
"opener": "^1.5.2",
@@ -209,7 +209,7 @@
"tap": "^16.0.1"
},
"scripts": {
- "dependencies": "node scripts/bundle-and-gitignore-deps.js",
+ "dependencies": "node scripts/bundle-and-gitignore-deps.js && node scripts/dependency-graph.js",
"dumpconf": "env | grep npm | sort | uniq",
"preversion": "bash scripts/update-authors.sh && git add AUTHORS && git commit -m \"chore: update AUTHORS\" || true",
"licenses": "licensee --production --errors-only",
diff --git a/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs b/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs
index e2d248edf5b6c6..65a9ee02eb9524 100644
--- a/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs
+++ b/deps/npm/tap-snapshots/test/lib/commands/publish.js.test.cjs
@@ -56,7 +56,11 @@ Array [
]
`
-exports[`test/lib/commands/publish.js TAP has auth for scope configured registry > new package version 1`] = `
+exports[`test/lib/commands/publish.js TAP has mTLS auth for scope configured registry > new package version 1`] = `
++ @npm/test-package@1.0.0
+`
+
+exports[`test/lib/commands/publish.js TAP has token auth for scope configured registry > new package version 1`] = `
+ @npm/test-package@1.0.0
`
diff --git a/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs b/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
index 04d304a2254d99..89c9969d69424d 100644
--- a/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
+++ b/deps/npm/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
@@ -404,8 +404,9 @@ newlines replaced by the string "\\n". For example:
cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
\`\`\`
-It is _not_ the path to a certificate file (and there is no "certfile"
-option).
+It is _not_ the path to a certificate file, though you can set a
+registry-scoped "certfile" path like
+"//other-registry.tld/:certfile=/path/to/cert.pem".
`
exports[`test/lib/utils/config/definitions.js TAP > config description for ci-name 1`] = `
@@ -1016,7 +1017,8 @@ format with newlines replaced by the string "\\n". For example:
key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----"
\`\`\`
-It is _not_ the path to a key file (and there is no "keyfile" option).
+It is _not_ the path to a key file, though you can set a registry-scoped
+"keyfile" path like "//other-registry.tld/:keyfile=/path/to/key.pem".
`
exports[`test/lib/utils/config/definitions.js TAP > config description for legacy-bundling 1`] = `
diff --git a/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs b/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs
index a291af6dedfae8..a9247f49c04187 100644
--- a/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs
+++ b/deps/npm/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs
@@ -230,8 +230,9 @@ newlines replaced by the string "\\n". For example:
cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----"
\`\`\`
-It is _not_ the path to a certificate file (and there is no "certfile"
-option).
+It is _not_ the path to a certificate file, though you can set a
+registry-scoped "certfile" path like
+"//other-registry.tld/:certfile=/path/to/cert.pem".
@@ -819,7 +820,8 @@ format with newlines replaced by the string "\\n". For example:
key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----"
\`\`\`
-It is _not_ the path to a key file (and there is no "keyfile" option).
+It is _not_ the path to a key file, though you can set a registry-scoped
+"keyfile" path like "//other-registry.tld/:keyfile=/path/to/key.pem".
diff --git a/deps/npm/test/lib/commands/publish.js b/deps/npm/test/lib/commands/publish.js
index 3cbe962382e21b..16b79df532d823 100644
--- a/deps/npm/test/lib/commands/publish.js
+++ b/deps/npm/test/lib/commands/publish.js
@@ -327,7 +327,7 @@ t.test('no auth for scope configured registry', async t => {
)
})
-t.test('has auth for scope configured registry', async t => {
+t.test('has token auth for scope configured registry', async t => {
const spec = npa('@npm/test-package')
const { npm, joinedOutput } = await loadMockNpm(t, {
config: {
@@ -356,6 +356,35 @@ t.test('has auth for scope configured registry', async t => {
t.matchSnapshot(joinedOutput(), 'new package version')
})
+t.test('has mTLS auth for scope configured registry', async t => {
+ const spec = npa('@npm/test-package')
+ const { npm, joinedOutput } = await loadMockNpm(t, {
+ config: {
+ '@npm:registry': alternateRegistry,
+ [`${alternateRegistry.slice(6)}/:certfile`]: '/some.cert',
+ [`${alternateRegistry.slice(6)}/:keyfile`]: '/some.key',
+ },
+ prefixDir: {
+ 'package.json': JSON.stringify({
+ name: '@npm/test-package',
+ version: '1.0.0',
+ }, null, 2),
+ },
+ globals: ({ prefix }) => ({
+ 'process.cwd': () => prefix,
+ }),
+ })
+ const registry = new MockRegistry({
+ tap: t,
+ registry: alternateRegistry,
+ })
+ registry.nock.put(`/${spec.escapedName}`, body => {
+ return t.match(body, { name: '@npm/test-package' })
+ }).reply(200, {})
+ await npm.exec('publish', [])
+ t.matchSnapshot(joinedOutput(), 'new package version')
+})
+
t.test('workspaces', t => {
const dir = {
'package.json': JSON.stringify(
diff --git a/deps/npm/test/lib/commands/whoami.js b/deps/npm/test/lib/commands/whoami.js
index ad7c223888df41..d63b49015f0d07 100644
--- a/deps/npm/test/lib/commands/whoami.js
+++ b/deps/npm/test/lib/commands/whoami.js
@@ -34,6 +34,20 @@ t.test('npm whoami --json', async t => {
t.equal(JSON.parse(joinedOutput()), username, 'should print username')
})
+t.test('npm whoami using mTLS', async t => {
+ const { npm, joinedOutput } = await loadMockNpm(t, { config: {
+ '//registry.npmjs.org/:certfile': '/some.cert',
+ '//registry.npmjs.org/:keyfile': '/some.key',
+ } })
+ const registry = new MockRegistry({
+ tap: t,
+ registry: npm.config.get('registry'),
+ })
+ registry.whoami({ username })
+ await npm.exec('whoami', [])
+ t.equal(joinedOutput(), username, 'should print username')
+})
+
t.test('credentials from token', async t => {
const { npm, joinedOutput } = await loadMockNpm(t, {
config: {
diff --git a/deps/npm/test/lib/utils/otplease.js b/deps/npm/test/lib/utils/otplease.js
index 025084ab4f2c5b..79eaa798e60539 100644
--- a/deps/npm/test/lib/utils/otplease.js
+++ b/deps/npm/test/lib/utils/otplease.js
@@ -1,17 +1,25 @@
const t = require('tap')
+
+const { fake: mockNpm } = require('../../fixtures/mock-npm')
const mockGlobals = require('../../fixtures/mock-globals')
const readUserInfo = {
otp: async () => '1234',
}
+const webAuth = async (opener) => {
+ opener()
+ return '1234'
+}
const otplease = t.mock('../../../lib/utils/otplease.js', {
'../../../lib/utils/read-user-info.js': readUserInfo,
+ '../../../lib/utils/open-url-prompt.js': () => {},
+ '../../../lib/utils/web-auth': webAuth,
})
t.test('returns function results on success', async (t) => {
const fn = () => 'test string'
- const result = await otplease({}, fn)
+ const result = await otplease(null, {}, fn)
t.equal('test string', result)
})
@@ -26,7 +34,7 @@ t.test('returns function results on otp success', async (t) => {
}
throw Object.assign(new Error('nope'), { code: 'EOTP' })
}
- const result = await otplease({}, fn)
+ const result = await otplease(null, {}, fn)
t.equal('success', result)
})
@@ -51,7 +59,31 @@ t.test('prompts for otp for EOTP', async (t) => {
t.end()
}
- await otplease({ some: 'prop' }, fn)
+ await otplease(null, { some: 'prop' }, fn)
+})
+
+t.test('returns function results on webauth success', async (t) => {
+ mockGlobals(t, {
+ 'process.stdin': { isTTY: true },
+ 'process.stdout': { isTTY: true },
+ })
+
+ const npm = mockNpm({ config: { browser: 'firefox' } })
+ const fn = ({ otp }) => {
+ if (otp) {
+ return 'success'
+ }
+ throw Object.assign(new Error('nope'), {
+ code: 'EOTP',
+ body: {
+ authUrl: 'https://www.example.com/auth',
+ doneUrl: 'https://www.example.com/done',
+ },
+ })
+ }
+
+ const result = await otplease(npm, {}, fn)
+ t.equal('success', result)
})
t.test('prompts for otp for 401', async (t) => {
@@ -78,7 +110,7 @@ t.test('prompts for otp for 401', async (t) => {
t.end()
}
- await otplease({ some: 'prop' }, fn)
+ await otplease(null, { some: 'prop' }, fn)
})
t.test('does not prompt for non-otp errors', async (t) => {
@@ -95,7 +127,11 @@ t.test('does not prompt for non-otp errors', async (t) => {
throw new Error('nope')
}
- t.rejects(otplease({ some: 'prop' }, fn), { message: 'nope' }, 'rejects with the original error')
+ t.rejects(
+ otplease(null, { some: 'prop' }, fn),
+ { message: 'nope' },
+ 'rejects with the original error'
+ )
})
t.test('does not prompt if stdin or stdout is not a tty', async (t) => {
@@ -112,5 +148,9 @@ t.test('does not prompt if stdin or stdout is not a tty', async (t) => {
throw Object.assign(new Error('nope'), { code: 'EOTP' })
}
- t.rejects(otplease({ some: 'prop' }, fn), { message: 'nope' }, 'rejects with the original error')
+ t.rejects(
+ otplease(null, { some: 'prop' }, fn),
+ { message: 'nope' },
+ 'rejects with the original error'
+ )
})
diff --git a/deps/npm/test/lib/utils/web-auth.js b/deps/npm/test/lib/utils/web-auth.js
new file mode 100644
index 00000000000000..ee8a17ecbc09d4
--- /dev/null
+++ b/deps/npm/test/lib/utils/web-auth.js
@@ -0,0 +1,32 @@
+const t = require('tap')
+
+const webAuthCheckLogin = async () => {
+ return { token: 'otp-token' }
+}
+
+const webauth = t.mock('../../../lib/utils/web-auth.js', {
+ 'npm-profile': { webAuthCheckLogin },
+})
+
+const initialUrl = 'https://example.com/auth'
+const doneUrl = 'https://example.com/done'
+const opts = {}
+
+t.test('returns token on success', async (t) => {
+ const opener = async () => {}
+ const result = await webauth(opener, initialUrl, doneUrl, opts)
+ t.equal(result, 'otp-token')
+})
+
+t.test('closes opener when auth check finishes', async (t) => {
+ const opener = (_url, emitter) => {
+ return new Promise((resolve, reject) => {
+ // the only way to finish this promise is to emit aboter on the emitter
+ emitter.addListener('abort', () => {
+ resolve()
+ })
+ })
+ }
+ const result = await webauth(opener, initialUrl, doneUrl, opts)
+ t.equal(result, 'otp-token')
+})