Skip to content

Commit

Permalink
deps: pacote@13.6.0 (#4969)
Browse files Browse the repository at this point in the history
* allow reuse of external integrity stream
 * replaceRegistryHost can now be a hostname
 * error when passing signature without keys
  • Loading branch information
wraithgar committed Jun 1, 2022
1 parent a6b62b2 commit fb4cc24
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 87 deletions.
52 changes: 25 additions & 27 deletions node_modules/pacote/lib/fetcher.js
Expand Up @@ -18,6 +18,7 @@ const removeTrailingSlashes = require('./util/trailing-slashes.js')
const getContents = require('@npmcli/installed-package-contents')
const readPackageJsonFast = require('read-package-json-fast')
const readPackageJson = promisify(require('read-package-json'))
const Minipass = require('minipass')

// we only change ownership on unix platforms, and only if uid is 0
const selfOwner = process.getuid && process.getuid() === 0 ? {
Expand Down Expand Up @@ -105,15 +106,10 @@ class FetcherBase {
this[_readPackageJson] = readPackageJsonFast
}

// config values: npmjs (default), never, always
// we don't want to mutate the original value
if (opts.replaceRegistryHost !== 'never'
&& opts.replaceRegistryHost !== 'always'
) {
this.replaceRegistryHost = 'npmjs'
} else {
this.replaceRegistryHost = opts.replaceRegistryHost
}
// rrh is a registry hostname or 'never' or 'always'
// defaults to registry.npmjs.org
this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ?
'registry.npmjs.org' : opts.replaceRegistryHost

this.defaultTag = opts.defaultTag || 'latest'
this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org')
Expand Down Expand Up @@ -224,40 +220,42 @@ class FetcherBase {
}

[_istream] (stream) {
// everyone will need one of these, either for verifying or calculating
// We always set it, because we have might only have a weak legacy hex
// sha1 in the packument, and this MAY upgrade it to a stronger algo.
// If we had an integrity, and it doesn't match, then this does not
// override that error; the istream will raise the error before it
// gets to the point of re-setting the integrity.
const istream = ssri.integrityStream(this.opts)
istream.on('integrity', i => this.integrity = i)
stream.on('error', er => istream.emit('error', er))

// if not caching this, just pipe through to the istream and return it
// if not caching this, just return it
if (!this.opts.cache || !this[_cacheFetches]) {
// instead of creating a new integrity stream, we only piggyback on the
// provided stream's events
if (stream.hasIntegrityEmitter) {
stream.on('integrity', i => this.integrity = i)
return stream
}

const istream = ssri.integrityStream(this.opts)
istream.on('integrity', i => this.integrity = i)
stream.on('error', err => istream.emit('error', err))
return stream.pipe(istream)
}

// we have to return a stream that gets ALL the data, and proxies errors,
// but then pipe from the original tarball stream into the cache as well.
// To do this without losing any data, and since the cacache put stream
// is not a passthrough, we have to pipe from the original stream into
// the cache AFTER we pipe into the istream. Since the cache stream
// the cache AFTER we pipe into the middleStream. Since the cache stream
// has an asynchronous flush to write its contents to disk, we need to
// defer the istream end until the cache stream ends.
stream.pipe(istream, { end: false })
// defer the middleStream end until the cache stream ends.
const middleStream = new Minipass()
stream.on('error', err => middleStream.emit('error', err))
stream.pipe(middleStream, { end: false })
const cstream = cacache.put.stream(
this.opts.cache,
`pacote:tarball:${this.from}`,
this.opts
)
cstream.on('integrity', i => this.integrity = i)
cstream.on('error', err => stream.emit('error', err))
stream.pipe(cstream)
// defer istream end until after cstream
// cache write errors should not crash the fetch, this is best-effort.
cstream.promise().catch(() => {}).then(() => istream.end())

return istream
cstream.promise().catch(() => {}).then(() => middleStream.end())
return middleStream
}

pickIntegrityAlgorithm () {
Expand Down
77 changes: 41 additions & 36 deletions node_modules/pacote/lib/registry.js
Expand Up @@ -122,11 +122,12 @@ class RegistryFetcher extends Fetcher {
}

const packument = await this.packument()
const mani = await pickManifest(packument, this.spec.fetchSpec, {
let mani = await pickManifest(packument, this.spec.fetchSpec, {
...this.opts,
defaultTag: this.defaultTag,
before: this.before,
})
mani = rpj.normalize(mani)
/* XXX add ETARGET and E403 revalidation of cached packuments here */

// add _resolved and _integrity from dist object
Expand Down Expand Up @@ -165,49 +166,53 @@ class RegistryFetcher extends Fetcher {
mani._integrity = String(this.integrity)
if (dist.signatures) {
if (this.opts.verifySignatures) {
if (this.registryKeys) {
// validate and throw on error, then set _signatures
const message = `${mani._id}:${mani._integrity}`
for (const signature of dist.signatures) {
const publicKey = this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
if (!publicKey) {
throw Object.assign(new Error(
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
'but no corresponding public key can be found.'
), { code: 'EMISSINGSIGNATUREKEY' })
}
const validPublicKey =
!publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
if (!validPublicKey) {
throw Object.assign(new Error(
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
// validate and throw on error, then set _signatures
const message = `${mani._id}:${mani._integrity}`
for (const signature of dist.signatures) {
const publicKey = this.registryKeys &&
this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
if (!publicKey) {
throw Object.assign(new Error(
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
'but no corresponding public key can be found'
), { code: 'EMISSINGSIGNATUREKEY' })
}
const validPublicKey =
!publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
if (!validPublicKey) {
throw Object.assign(new Error(
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
`but the corresponding public key has expired ${publicKey.expires}`
), { code: 'EEXPIREDSIGNATUREKEY' })
}
const verifier = crypto.createVerify('SHA256')
verifier.write(message)
verifier.end()
const valid = verifier.verify(
publicKey.pemkey,
signature.sig,
'base64'
)
if (!valid) {
throw Object.assign(new Error(
'Integrity checksum signature failed: ' +
`key ${publicKey.keyid} signature ${signature.sig}`
), { code: 'EINTEGRITYSIGNATURE' })
}
), { code: 'EEXPIREDSIGNATUREKEY' })
}
const verifier = crypto.createVerify('SHA256')
verifier.write(message)
verifier.end()
const valid = verifier.verify(
publicKey.pemkey,
signature.sig,
'base64'
)
if (!valid) {
throw Object.assign(new Error(
`${mani._id} has an invalid registry signature with ` +
`keyid: ${publicKey.keyid} and signature: ${signature.sig}`
), {
code: 'EINTEGRITYSIGNATURE',
keyid: publicKey.keyid,
signature: signature.sig,
resolved: mani._resolved,
integrity: mani._integrity,
})
}
mani._signatures = dist.signatures
}
// if no keys, don't set _signatures
mani._signatures = dist.signatures
} else {
mani._signatures = dist.signatures
}
}
}
this.package = rpj.normalize(mani)
this.package = mani
return this.package
}

Expand Down
27 changes: 14 additions & 13 deletions node_modules/pacote/lib/remote.js
Expand Up @@ -4,8 +4,6 @@ const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
const pacoteVersion = require('../package.json').version
const fetch = require('npm-registry-fetch')
const Minipass = require('minipass')
// The default registry URL is a string of great magic.
const magicHost = 'https://registry.npmjs.org'

const _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
const _headers = Symbol('_headers')
Expand All @@ -14,11 +12,9 @@ class RemoteFetcher extends Fetcher {
super(spec, opts)
this.resolved = this.spec.fetchSpec
const resolvedURL = new URL(this.resolved)
if (
(this.replaceRegistryHost === 'npmjs'
&& resolvedURL.origin === magicHost)
|| this.replaceRegistryHost === 'always'
) {
if (this.replaceRegistryHost !== 'never'
&& (this.replaceRegistryHost === 'always'
|| this.replaceRegistryHost === resolvedURL.host)) {
this.resolved = new URL(resolvedURL.pathname, this.registry).href
}

Expand All @@ -35,23 +31,28 @@ class RemoteFetcher extends Fetcher {

[_tarballFromResolved] () {
const stream = new Minipass()
stream.hasIntegrityEmitter = true

const fetchOpts = {
...this.opts,
headers: this[_headers](),
spec: this.spec,
integrity: this.integrity,
algorithms: [this.pickIntegrityAlgorithm()],
}
fetch(this.resolved, fetchOpts).then(res => {
const hash = res.headers.get('x-local-cache-hash')
if (hash) {
this.integrity = decodeURIComponent(hash)
}

fetch(this.resolved, fetchOpts).then(res => {
res.body.on('error',
/* istanbul ignore next - exceedingly rare and hard to simulate */
er => stream.emit('error', er)
).pipe(stream)
)

res.body.on('integrity', i => {
this.integrity = i
stream.emit('integrity', i)
})

res.body.pipe(stream)
}).catch(er => stream.emit('error', er))

return stream
Expand Down
5 changes: 2 additions & 3 deletions node_modules/pacote/package.json
@@ -1,6 +1,6 @@
{
"name": "pacote",
"version": "13.5.0",
"version": "13.6.0",
"description": "JavaScript package downloader",
"author": "GitHub Inc.",
"bin": {
Expand All @@ -21,8 +21,7 @@
"template-oss-apply": "template-oss-apply --force"
},
"tap": {
"timeout": 300,
"coverage-map": "map.js"
"timeout": 300
},
"devDependencies": {
"@npmcli/eslint-config": "^3.0.1",
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json
Expand Up @@ -138,7 +138,7 @@
"npm-user-validate": "^1.0.1",
"npmlog": "^6.0.2",
"opener": "^1.5.2",
"pacote": "^13.4.1",
"pacote": "^13.6.0",
"parse-conflict-json": "^2.0.2",
"proc-log": "^2.0.1",
"qrcode-terminal": "^0.12.0",
Expand Down Expand Up @@ -5547,9 +5547,9 @@
}
},
"node_modules/pacote": {
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/pacote/-/pacote-13.5.0.tgz",
"integrity": "sha512-yekp0ykEsaBH0t0bYA/89R+ywdYV5ZnEdg4YMIfqakSlpIhoF6b8+aEUm8NZpfWRgmy6lxgywcW05URhLRogVQ==",
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.0.tgz",
"integrity": "sha512-zHmuCwG4+QKnj47LFlW3LmArwKoglx2k5xtADiMCivVWPgNRP5QyLDGOIjGjwOe61lhl1rO63m/VxT16pEHLWg==",
"inBundle": true,
"dependencies": {
"@npmcli/git": "^3.0.0",
Expand Down Expand Up @@ -13877,9 +13877,9 @@
}
},
"pacote": {
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/pacote/-/pacote-13.5.0.tgz",
"integrity": "sha512-yekp0ykEsaBH0t0bYA/89R+ywdYV5ZnEdg4YMIfqakSlpIhoF6b8+aEUm8NZpfWRgmy6lxgywcW05URhLRogVQ==",
"version": "13.6.0",
"resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.0.tgz",
"integrity": "sha512-zHmuCwG4+QKnj47LFlW3LmArwKoglx2k5xtADiMCivVWPgNRP5QyLDGOIjGjwOe61lhl1rO63m/VxT16pEHLWg==",
"requires": {
"@npmcli/git": "^3.0.0",
"@npmcli/installed-package-contents": "^1.0.7",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -107,7 +107,7 @@
"npm-user-validate": "^1.0.1",
"npmlog": "^6.0.2",
"opener": "^1.5.2",
"pacote": "^13.4.1",
"pacote": "^13.6.0",
"parse-conflict-json": "^2.0.2",
"proc-log": "^2.0.1",
"qrcode-terminal": "^0.12.0",
Expand Down

0 comments on commit fb4cc24

Please sign in to comment.