Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

npm-registry-fetch

Package Overview
Dependencies
Maintainers
6
Versions
79
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

npm-registry-fetch - npm Package Compare versions

Comparing version 9.0.0 to 10.0.0

129

auth.js
'use strict'
const npa = require('npm-package-arg')
const defaultOpts = require('./default-opts.js')
const url = require('url')
// Find the longest registry key that is used for some kind of auth
// in the options.
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
// We walk up the "path" until we're left with just //<host>[:<port>],
// stopping when we reach '//'.
let regKey = `//${parsed.host}${parsed.pathname}`
while (regKey.length > '//'.length) {
// got some auth for this URI
if (hasAuth(regKey, opts))
return regKey
module.exports = getAuth
function getAuth (registry, opts_ = {}) {
if (!registry)
throw new Error('registry is required')
const opts = opts_.forceAuth ? opts_.forceAuth : { ...defaultOpts, ...opts_ }
const AUTH = {}
const regKey = registry && registryKey(registry)
const doKey = (key, alias) => addKey(opts, AUTH, regKey, key, alias)
doKey('token')
doKey('_authToken', 'token')
doKey('username')
doKey('password')
doKey('_password', 'password')
doKey('email')
doKey('_auth')
doKey('otp')
doKey('always-auth', 'alwaysAuth')
if (AUTH.password)
AUTH.password = Buffer.from(AUTH.password, 'base64').toString('utf8')
if (AUTH._auth && !(AUTH.username && AUTH.password)) {
let auth = Buffer.from(AUTH._auth, 'base64').toString()
auth = auth.split(':')
AUTH.username = auth.shift()
AUTH.password = auth.join(':')
// can be either //host/some/path/:_auth or //host/some/path:_auth
// walk up by removing EITHER what's after the slash OR the slash itself
regKey = regKey.replace(/([^/]+|\/)$/, '')
}
AUTH.alwaysAuth = AUTH.alwaysAuth === 'false' ? false : !!AUTH.alwaysAuth
return AUTH
}
function addKey (opts, obj, scope, key, objKey) {
if (opts[key])
obj[objKey || key] = opts[key]
const hasAuth = (regKey, opts) => (
opts[`${regKey}:_authToken`] ||
opts[`${regKey}:_auth`] ||
opts[`${regKey}:username`] && opts[`${regKey}:_password`]
)
if (scope && opts[`${scope}:${key}`])
obj[objKey || key] = opts[`${scope}:${key}`]
}
const getAuth = (uri, opts = {}) => {
const { forceAuth } = opts
if (!uri)
throw new Error('URI is required')
const regKey = regKeyFromURI(uri, forceAuth || opts)
// Called a nerf dart in the main codebase. Used as a "safe"
// key when fetching registry info from config.
function registryKey (registry) {
const parsed = new url.URL(registry)
const formatted = url.format({
protocol: parsed.protocol,
host: parsed.host,
pathname: parsed.pathname,
slashes: true,
// we are only allowed to use what's in forceAuth if specified
if (forceAuth && !regKey) {
return new Auth({
scopeAuthKey: null,
token: forceAuth._authToken,
username: forceAuth.username,
password: forceAuth._password || forceAuth.password,
auth: forceAuth._auth || forceAuth.auth,
})
}
// no auth for this URI
if (!regKey && opts.spec) {
// If making a tarball request to a different base URI than the
// registry where we logged in, but the same auth SHOULD be sent
// to that artifact host, then we track where it was coming in from,
// and warn the user if we get a 4xx error on it.
const { spec } = opts
const { scope: specScope, subSpec } = npa(spec)
const subSpecScope = subSpec && subSpec.scope
const scope = subSpec ? subSpecScope : specScope
const scopeReg = scope && opts[`${scope}:registry`]
const scopeAuthKey = scopeReg && regKeyFromURI(scopeReg, opts)
return new Auth({ scopeAuthKey })
}
const {
[`${regKey}:_authToken`]: token,
[`${regKey}:username`]: username,
[`${regKey}:_password`]: password,
[`${regKey}:_auth`]: auth,
} = opts
return new Auth({
scopeAuthKey: null,
token,
auth,
username,
password,
})
return url.format(new url.URL('.', formatted)).replace(/^[^:]+:/, '')
}
class Auth {
constructor ({ token, auth, username, password, scopeAuthKey }) {
this.scopeAuthKey = scopeAuthKey
this.token = null
this.auth = null
if (token)
this.token = token
else if (auth)
this.auth = auth
else if (username && password) {
const p = Buffer.from(password, 'base64').toString('utf8')
this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64')
}
}
}
module.exports = getAuth

@@ -8,5 +8,4 @@ 'use strict'

module.exports = checkResponse
function checkResponse (method, res, registry, startTime, opts_ = {}) {
const opts = { ...defaultOpts, ...opts_ }
const checkResponse = async ({ method, uri, res, registry, startTime, auth, opts }) => {
opts = { ...defaultOpts, ...opts }
if (res.headers.has('npm-notice') && !res.headers.has('x-local-cache'))

@@ -18,2 +17,13 @@ opts.log.notice('', res.headers.get('npm-notice'))

logRequest(method, res, startTime, opts)
if (auth && auth.scopeAuthKey && !auth.token && !auth.auth) {
// we didn't have auth for THIS request, but we do have auth for
// requests to the registry indicated by the spec's scope value.
// Warn the user.
opts.log.warn('registry', `No auth for URI, but auth present for scoped registry.
URI: ${uri}
Scoped Registry Key: ${auth.scopeAuthKey}
More info here: https://github.com/npm/cli/wiki/No-auth-for-URI,-but-auth-present-for-scoped-registry`)
}
return checkErrors(method, res, startTime, opts)

@@ -29,2 +39,3 @@ } else {

}
module.exports = checkResponse

@@ -31,0 +42,0 @@ function logRequest (method, res, startTime, opts) {

const pkg = require('./package.json')
const ciDetect = require('@npmcli/ci-detect')
module.exports = {
isFromCI: ciDetect(),
log: require('./silentlog.js'),

@@ -6,0 +4,0 @@ maxSockets: 12,

@@ -25,2 +25,3 @@ 'use strict'

super()
this.name = this.constructor.name
this.headers = res.headers.raw()

@@ -27,0 +28,0 @@ this.statusCode = res.status

'use strict'
const { HttpErrorAuthOTP } = require('./errors.js')
const checkResponse = require('./check-response.js')

@@ -30,10 +31,13 @@ const getAuth = require('./auth.js')

}
const registry = opts.registry = (
(opts.spec && pickRegistry(opts.spec, opts)) ||
opts.registry ||
/* istanbul ignore next */
'https://registry.npmjs.org/'
)
if (!urlIsValid(uri)) {
// if we did not get a fully qualified URI, then we look at the registry
// config or relevant scope to resolve it.
const uriValid = urlIsValid(uri)
let registry = opts.registry || defaultOpts.registry
if (!uriValid) {
registry = opts.registry = (
(opts.spec && pickRegistry(opts.spec, opts)) ||
opts.registry ||
registry
)
uri = `${

@@ -44,2 +48,4 @@ registry.trim().replace(/\/?$/g, '')

}`
// asserts that this is now valid
new url.URL(uri)
}

@@ -51,3 +57,4 @@

const startTime = Date.now()
const headers = getHeaders(registry, uri, opts)
const auth = getAuth(uri, opts)
const headers = getHeaders(uri, auth, opts)
let body = opts.body

@@ -98,31 +105,54 @@ const bodyIsStream = Minipass.isStream(body)

const doFetch = (body) => fetch(uri, {
agent: opts.agent,
algorithms: opts.algorithms,
body,
cache: getCacheMode(opts),
cacheManager: opts.cache,
ca: opts.ca,
cert: opts.cert,
headers,
integrity: opts.integrity,
key: opts.key,
localAddress: opts.localAddress,
maxSockets: opts.maxSockets,
memoize: opts.memoize,
method: method,
noProxy: opts.noProxy,
proxy: opts.httpsProxy || opts.proxy,
retry: opts.retry ? opts.retry : {
retries: opts.fetchRetries,
factor: opts.fetchRetryFactor,
minTimeout: opts.fetchRetryMintimeout,
maxTimeout: opts.fetchRetryMaxtimeout,
},
strictSSL: opts.strictSSL,
timeout: opts.timeout || 30 * 1000,
}).then(res => checkResponse(
method, res, registry, startTime, opts
))
const doFetch = async body => {
const p = fetch(uri, {
agent: opts.agent,
algorithms: opts.algorithms,
body,
cache: getCacheMode(opts),
cacheManager: opts.cache,
ca: opts.ca,
cert: opts.cert,
headers,
integrity: opts.integrity,
key: opts.key,
localAddress: opts.localAddress,
maxSockets: opts.maxSockets,
memoize: opts.memoize,
method: method,
noProxy: opts.noProxy,
proxy: opts.httpsProxy || opts.proxy,
retry: opts.retry ? opts.retry : {
retries: opts.fetchRetries,
factor: opts.fetchRetryFactor,
minTimeout: opts.fetchRetryMintimeout,
maxTimeout: opts.fetchRetryMaxtimeout,
},
strictSSL: opts.strictSSL,
timeout: opts.timeout || 30 * 1000,
}).then(res => checkResponse({
method,
uri,
res,
registry,
startTime,
auth,
opts,
}))
if (typeof opts.otpPrompt === 'function') {
return p.catch(async er => {
if (er instanceof HttpErrorAuthOTP) {
// if otp fails to complete, we fail with that failure
const otp = await opts.otpPrompt()
// if no otp provided, throw the original HTTP error
if (!otp)
throw er
return regFetch(uri, { ...opts, otp })
}
throw er
})
} else
return p
}
return Promise.resolve(body).then(doFetch)

@@ -158,3 +188,3 @@ }

if (!registry)
registry = opts.registry || 'https://registry.npmjs.org/'
registry = opts.registry || defaultOpts.registry

@@ -171,5 +201,4 @@ return registry

function getHeaders (registry, uri, opts) {
function getHeaders (uri, auth, opts) {
const headers = Object.assign({
'npm-in-ci': !!opts.isFromCI,
'user-agent': opts.userAgent,

@@ -187,23 +216,13 @@ }, opts.headers || {})

const auth = getAuth(registry, opts)
// If a tarball is hosted on a different place than the manifest, only send
// credentials on `alwaysAuth`
const shouldAuth = (
auth.alwaysAuth ||
new url.URL(uri).host === new url.URL(registry).host
)
if (shouldAuth && auth.token)
if (auth.token)
headers.authorization = `Bearer ${auth.token}`
else if (shouldAuth && auth.username && auth.password) {
const encoded = Buffer.from(
`${auth.username}:${auth.password}`, 'utf8'
).toString('base64')
headers.authorization = `Basic ${encoded}`
} else if (shouldAuth && auth._auth)
headers.authorization = `Basic ${auth._auth}`
else if (auth.auth)
headers.authorization = `Basic ${auth.auth}`
if (shouldAuth && auth.otp)
headers['npm-otp'] = auth.otp
if (opts.otp)
headers['npm-otp'] = opts.otp
return headers
}
{
"name": "npm-registry-fetch",
"version": "9.0.0",
"version": "10.0.0",
"description": "Fetch-based http client for use with npm registry APIs",

@@ -32,3 +32,2 @@ "main": "index.js",

"dependencies": {
"@npmcli/ci-detect": "^1.0.0",
"lru-cache": "^6.0.0",

@@ -35,0 +34,0 @@ "make-fetch-happen": "^8.0.9",

@@ -312,9 +312,2 @@ # npm-registry-fetch

##### <a name='opts-isFromCI'></a> `opts.isFromCI`
* Type: Boolean
* Default: Based on environment variables
This is used to populate the `npm-in-ci` request header sent to the registry.
##### <a name="opts-key"></a> `opts.key`

@@ -429,2 +422,15 @@

##### <a name="opts-otpPrompt"></a> `opts.otpPrompt`
* Type: Function
* Default: null
This is a method which will be called to provide an OTP if the server
responds with a 401 response indicating that a one-time-password is
required.
It may return a promise, which must resolve to the OTP value to be used.
If the method fails to provide an OTP value, then the fetch will fail with
the auth error that indicated an OTP was needed.
##### <a name="opts-password"></a> `opts.password`

@@ -431,0 +437,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc