pacote 13.5.0 → 13.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/lib/bin.js +8 -3
- package/lib/fetcher.js +30 -28
- package/lib/git.js +8 -9
- package/lib/registry.js +41 -36
- package/lib/remote.js +15 -13
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -237,6 +237,7 @@ In addition to the common `package.json` fields, manifests include:
|
|
|
237
237
|
artifact can be found.
|
|
238
238
|
* `manifest._from` A normalized form of the spec passed in as an argument.
|
|
239
239
|
* `manifest._integrity` The integrity value for the package artifact.
|
|
240
|
+
* `manifest._id` The canonical spec of this package version: name@version.
|
|
240
241
|
* `manifest.dist` Registry manifests (those included in a packument) have a
|
|
241
242
|
`dist` object. Only `tarball` is required, though at least one of
|
|
242
243
|
`shasum` or `integrity` is almost always present.
|
|
@@ -274,3 +275,8 @@ For Pacote's purposes, the following fields are relevant:
|
|
|
274
275
|
`foo@latest` gets turned into `foo@1.2.3`.
|
|
275
276
|
* `time` In the full packument, an object mapping version numbers to
|
|
276
277
|
publication times, for the `opts.before` functionality.
|
|
278
|
+
|
|
279
|
+
Pacote adds the following fields, regardless of the accept header:
|
|
280
|
+
|
|
281
|
+
* `_cached` Whether the packument was fetched from the network or the local cache.
|
|
282
|
+
* `_contentLength` The size of the packument.
|
package/lib/bin.js
CHANGED
|
@@ -18,10 +18,15 @@ const run = conf => {
|
|
|
18
18
|
case 'tarball':
|
|
19
19
|
if (!conf._[2] || conf._[2] === '-') {
|
|
20
20
|
return pacote.tarball.stream(conf._[1], stream => {
|
|
21
|
-
stream.pipe(
|
|
22
|
-
|
|
21
|
+
stream.pipe(
|
|
22
|
+
conf.testStdout ||
|
|
23
|
+
/* istanbul ignore next */
|
|
24
|
+
process.stdout
|
|
25
|
+
)
|
|
23
26
|
// make sure it resolves something falsey
|
|
24
|
-
return stream.promise().then(() => {
|
|
27
|
+
return stream.promise().then(() => {
|
|
28
|
+
return false
|
|
29
|
+
})
|
|
25
30
|
}, conf)
|
|
26
31
|
} else {
|
|
27
32
|
return pacote.tarball.file(conf._[1], conf._[2], conf)
|
package/lib/fetcher.js
CHANGED
|
@@ -18,6 +18,7 @@ const removeTrailingSlashes = require('./util/trailing-slashes.js')
|
|
|
18
18
|
const getContents = require('@npmcli/installed-package-contents')
|
|
19
19
|
const readPackageJsonFast = require('read-package-json-fast')
|
|
20
20
|
const readPackageJson = promisify(require('read-package-json'))
|
|
21
|
+
const Minipass = require('minipass')
|
|
21
22
|
|
|
22
23
|
// we only change ownership on unix platforms, and only if uid is 0
|
|
23
24
|
const selfOwner = process.getuid && process.getuid() === 0 ? {
|
|
@@ -105,15 +106,10 @@ class FetcherBase {
|
|
|
105
106
|
this[_readPackageJson] = readPackageJsonFast
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
) {
|
|
113
|
-
this.replaceRegistryHost = 'npmjs'
|
|
114
|
-
} else {
|
|
115
|
-
this.replaceRegistryHost = opts.replaceRegistryHost
|
|
116
|
-
}
|
|
109
|
+
// rrh is a registry hostname or 'never' or 'always'
|
|
110
|
+
// defaults to registry.npmjs.org
|
|
111
|
+
this.replaceRegistryHost = (!opts.replaceRegistryHost || opts.replaceRegistryHost === 'npmjs') ?
|
|
112
|
+
'registry.npmjs.org' : opts.replaceRegistryHost
|
|
117
113
|
|
|
118
114
|
this.defaultTag = opts.defaultTag || 'latest'
|
|
119
115
|
this.registry = removeTrailingSlashes(opts.registry || 'https://registry.npmjs.org')
|
|
@@ -224,18 +220,18 @@ class FetcherBase {
|
|
|
224
220
|
}
|
|
225
221
|
|
|
226
222
|
[_istream] (stream) {
|
|
227
|
-
//
|
|
228
|
-
// We always set it, because we have might only have a weak legacy hex
|
|
229
|
-
// sha1 in the packument, and this MAY upgrade it to a stronger algo.
|
|
230
|
-
// If we had an integrity, and it doesn't match, then this does not
|
|
231
|
-
// override that error; the istream will raise the error before it
|
|
232
|
-
// gets to the point of re-setting the integrity.
|
|
233
|
-
const istream = ssri.integrityStream(this.opts)
|
|
234
|
-
istream.on('integrity', i => this.integrity = i)
|
|
235
|
-
stream.on('error', er => istream.emit('error', er))
|
|
236
|
-
|
|
237
|
-
// if not caching this, just pipe through to the istream and return it
|
|
223
|
+
// if not caching this, just return it
|
|
238
224
|
if (!this.opts.cache || !this[_cacheFetches]) {
|
|
225
|
+
// instead of creating a new integrity stream, we only piggyback on the
|
|
226
|
+
// provided stream's events
|
|
227
|
+
if (stream.hasIntegrityEmitter) {
|
|
228
|
+
stream.on('integrity', i => this.integrity = i)
|
|
229
|
+
return stream
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const istream = ssri.integrityStream(this.opts)
|
|
233
|
+
istream.on('integrity', i => this.integrity = i)
|
|
234
|
+
stream.on('error', err => istream.emit('error', err))
|
|
239
235
|
return stream.pipe(istream)
|
|
240
236
|
}
|
|
241
237
|
|
|
@@ -243,21 +239,24 @@ class FetcherBase {
|
|
|
243
239
|
// but then pipe from the original tarball stream into the cache as well.
|
|
244
240
|
// To do this without losing any data, and since the cacache put stream
|
|
245
241
|
// is not a passthrough, we have to pipe from the original stream into
|
|
246
|
-
// the cache AFTER we pipe into the
|
|
242
|
+
// the cache AFTER we pipe into the middleStream. Since the cache stream
|
|
247
243
|
// has an asynchronous flush to write its contents to disk, we need to
|
|
248
|
-
// defer the
|
|
249
|
-
|
|
244
|
+
// defer the middleStream end until the cache stream ends.
|
|
245
|
+
const middleStream = new Minipass()
|
|
246
|
+
stream.on('error', err => middleStream.emit('error', err))
|
|
247
|
+
stream.pipe(middleStream, { end: false })
|
|
250
248
|
const cstream = cacache.put.stream(
|
|
251
249
|
this.opts.cache,
|
|
252
250
|
`pacote:tarball:${this.from}`,
|
|
253
251
|
this.opts
|
|
254
252
|
)
|
|
253
|
+
cstream.on('integrity', i => this.integrity = i)
|
|
254
|
+
cstream.on('error', err => stream.emit('error', err))
|
|
255
255
|
stream.pipe(cstream)
|
|
256
|
-
// defer istream end until after cstream
|
|
257
|
-
// cache write errors should not crash the fetch, this is best-effort.
|
|
258
|
-
cstream.promise().catch(() => {}).then(() => istream.end())
|
|
259
256
|
|
|
260
|
-
return
|
|
257
|
+
// eslint-disable-next-line promise/catch-or-return
|
|
258
|
+
cstream.promise().catch(() => {}).then(() => middleStream.end())
|
|
259
|
+
return middleStream
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
pickIntegrityAlgorithm () {
|
|
@@ -271,7 +270,10 @@ class FetcherBase {
|
|
|
271
270
|
}
|
|
272
271
|
|
|
273
272
|
// override the types getter
|
|
274
|
-
get types () {
|
|
273
|
+
get types () {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
|
|
275
277
|
[_assertType] () {
|
|
276
278
|
if (this.types && !this.types.includes(this.spec.type)) {
|
|
277
279
|
throw new TypeError(`Wrong spec type (${
|
package/lib/git.js
CHANGED
|
@@ -239,7 +239,7 @@ class GitFetcher extends Fetcher {
|
|
|
239
239
|
tarballOk = tarballOk &&
|
|
240
240
|
h && resolved === repoUrl(h, { noCommittish: false }) && h.tarball
|
|
241
241
|
|
|
242
|
-
return cacache.tmp.withTmp(this.cache, o, tmp => {
|
|
242
|
+
return cacache.tmp.withTmp(this.cache, o, async tmp => {
|
|
243
243
|
// if we're resolved, and have a tarball url, shell out to RemoteFetcher
|
|
244
244
|
if (tarballOk) {
|
|
245
245
|
const nameat = this.spec.name ? `${this.spec.name}@` : ''
|
|
@@ -259,16 +259,15 @@ class GitFetcher extends Fetcher {
|
|
|
259
259
|
})
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
const sha = await (
|
|
263
263
|
h ? this[_cloneHosted](ref, tmp)
|
|
264
264
|
: this[_cloneRepo](this.spec.fetchSpec, ref, tmp)
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
.then(() => handler(tmp))
|
|
265
|
+
)
|
|
266
|
+
this.resolvedSha = sha
|
|
267
|
+
if (!this.resolved) {
|
|
268
|
+
await this[_addGitSha](sha)
|
|
269
|
+
}
|
|
270
|
+
return handler(tmp)
|
|
272
271
|
})
|
|
273
272
|
}
|
|
274
273
|
|
package/lib/registry.js
CHANGED
|
@@ -122,11 +122,12 @@ class RegistryFetcher extends Fetcher {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const packument = await this.packument()
|
|
125
|
-
|
|
125
|
+
let mani = await pickManifest(packument, this.spec.fetchSpec, {
|
|
126
126
|
...this.opts,
|
|
127
127
|
defaultTag: this.defaultTag,
|
|
128
128
|
before: this.before,
|
|
129
129
|
})
|
|
130
|
+
mani = rpj.normalize(mani)
|
|
130
131
|
/* XXX add ETARGET and E403 revalidation of cached packuments here */
|
|
131
132
|
|
|
132
133
|
// add _resolved and _integrity from dist object
|
|
@@ -165,49 +166,53 @@ class RegistryFetcher extends Fetcher {
|
|
|
165
166
|
mani._integrity = String(this.integrity)
|
|
166
167
|
if (dist.signatures) {
|
|
167
168
|
if (this.opts.verifySignatures) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
|
|
176
|
-
'but no corresponding public key can be found
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
|
|
169
|
+
// validate and throw on error, then set _signatures
|
|
170
|
+
const message = `${mani._id}:${mani._integrity}`
|
|
171
|
+
for (const signature of dist.signatures) {
|
|
172
|
+
const publicKey = this.registryKeys &&
|
|
173
|
+
this.registryKeys.filter(key => (key.keyid === signature.keyid))[0]
|
|
174
|
+
if (!publicKey) {
|
|
175
|
+
throw Object.assign(new Error(
|
|
176
|
+
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
|
|
177
|
+
'but no corresponding public key can be found'
|
|
178
|
+
), { code: 'EMISSINGSIGNATUREKEY' })
|
|
179
|
+
}
|
|
180
|
+
const validPublicKey =
|
|
181
|
+
!publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
|
|
182
|
+
if (!validPublicKey) {
|
|
183
|
+
throw Object.assign(new Error(
|
|
184
|
+
`${mani._id} has a registry signature with keyid: ${signature.keyid} ` +
|
|
184
185
|
`but the corresponding public key has expired ${publicKey.expires}`
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`
|
|
199
|
-
|
|
200
|
-
|
|
186
|
+
), { code: 'EEXPIREDSIGNATUREKEY' })
|
|
187
|
+
}
|
|
188
|
+
const verifier = crypto.createVerify('SHA256')
|
|
189
|
+
verifier.write(message)
|
|
190
|
+
verifier.end()
|
|
191
|
+
const valid = verifier.verify(
|
|
192
|
+
publicKey.pemkey,
|
|
193
|
+
signature.sig,
|
|
194
|
+
'base64'
|
|
195
|
+
)
|
|
196
|
+
if (!valid) {
|
|
197
|
+
throw Object.assign(new Error(
|
|
198
|
+
`${mani._id} has an invalid registry signature with ` +
|
|
199
|
+
`keyid: ${publicKey.keyid} and signature: ${signature.sig}`
|
|
200
|
+
), {
|
|
201
|
+
code: 'EINTEGRITYSIGNATURE',
|
|
202
|
+
keyid: publicKey.keyid,
|
|
203
|
+
signature: signature.sig,
|
|
204
|
+
resolved: mani._resolved,
|
|
205
|
+
integrity: mani._integrity,
|
|
206
|
+
})
|
|
201
207
|
}
|
|
202
|
-
mani._signatures = dist.signatures
|
|
203
208
|
}
|
|
204
|
-
|
|
209
|
+
mani._signatures = dist.signatures
|
|
205
210
|
} else {
|
|
206
211
|
mani._signatures = dist.signatures
|
|
207
212
|
}
|
|
208
213
|
}
|
|
209
214
|
}
|
|
210
|
-
this.package =
|
|
215
|
+
this.package = mani
|
|
211
216
|
return this.package
|
|
212
217
|
}
|
|
213
218
|
|
package/lib/remote.js
CHANGED
|
@@ -4,8 +4,6 @@ const _tarballFromResolved = Symbol.for('pacote.Fetcher._tarballFromResolved')
|
|
|
4
4
|
const pacoteVersion = require('../package.json').version
|
|
5
5
|
const fetch = require('npm-registry-fetch')
|
|
6
6
|
const Minipass = require('minipass')
|
|
7
|
-
// The default registry URL is a string of great magic.
|
|
8
|
-
const magicHost = 'https://registry.npmjs.org'
|
|
9
7
|
|
|
10
8
|
const _cacheFetches = Symbol.for('pacote.Fetcher._cacheFetches')
|
|
11
9
|
const _headers = Symbol('_headers')
|
|
@@ -14,11 +12,9 @@ class RemoteFetcher extends Fetcher {
|
|
|
14
12
|
super(spec, opts)
|
|
15
13
|
this.resolved = this.spec.fetchSpec
|
|
16
14
|
const resolvedURL = new URL(this.resolved)
|
|
17
|
-
if (
|
|
18
|
-
(this.replaceRegistryHost === '
|
|
19
|
-
|
|
20
|
-
|| this.replaceRegistryHost === 'always'
|
|
21
|
-
) {
|
|
15
|
+
if (this.replaceRegistryHost !== 'never'
|
|
16
|
+
&& (this.replaceRegistryHost === 'always'
|
|
17
|
+
|| this.replaceRegistryHost === resolvedURL.host)) {
|
|
22
18
|
this.resolved = new URL(resolvedURL.pathname, this.registry).href
|
|
23
19
|
}
|
|
24
20
|
|
|
@@ -35,6 +31,8 @@ class RemoteFetcher extends Fetcher {
|
|
|
35
31
|
|
|
36
32
|
[_tarballFromResolved] () {
|
|
37
33
|
const stream = new Minipass()
|
|
34
|
+
stream.hasIntegrityEmitter = true
|
|
35
|
+
|
|
38
36
|
const fetchOpts = {
|
|
39
37
|
...this.opts,
|
|
40
38
|
headers: this[_headers](),
|
|
@@ -42,16 +40,20 @@ class RemoteFetcher extends Fetcher {
|
|
|
42
40
|
integrity: this.integrity,
|
|
43
41
|
algorithms: [this.pickIntegrityAlgorithm()],
|
|
44
42
|
}
|
|
45
|
-
fetch(this.resolved, fetchOpts).then(res => {
|
|
46
|
-
const hash = res.headers.get('x-local-cache-hash')
|
|
47
|
-
if (hash) {
|
|
48
|
-
this.integrity = decodeURIComponent(hash)
|
|
49
|
-
}
|
|
50
43
|
|
|
44
|
+
// eslint-disable-next-line promise/always-return
|
|
45
|
+
fetch(this.resolved, fetchOpts).then(res => {
|
|
51
46
|
res.body.on('error',
|
|
52
47
|
/* istanbul ignore next - exceedingly rare and hard to simulate */
|
|
53
48
|
er => stream.emit('error', er)
|
|
54
|
-
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
res.body.on('integrity', i => {
|
|
52
|
+
this.integrity = i
|
|
53
|
+
stream.emit('integrity', i)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
res.body.pipe(stream)
|
|
55
57
|
}).catch(er => stream.emit('error', er))
|
|
56
58
|
|
|
57
59
|
return stream
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pacote",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.6.2",
|
|
4
4
|
"description": "JavaScript package downloader",
|
|
5
5
|
"author": "GitHub Inc.",
|
|
6
6
|
"bin": {
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
"template-oss-apply": "template-oss-apply --force"
|
|
22
22
|
},
|
|
23
23
|
"tap": {
|
|
24
|
-
"timeout": 300
|
|
25
|
-
"coverage-map": "map.js"
|
|
24
|
+
"timeout": 300
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@npmcli/eslint-config": "^3.0.1",
|
|
@@ -46,7 +45,7 @@
|
|
|
46
45
|
"@npmcli/git": "^3.0.0",
|
|
47
46
|
"@npmcli/installed-package-contents": "^1.0.7",
|
|
48
47
|
"@npmcli/promise-spawn": "^3.0.0",
|
|
49
|
-
"@npmcli/run-script": "^
|
|
48
|
+
"@npmcli/run-script": "^4.1.0",
|
|
50
49
|
"cacache": "^16.0.0",
|
|
51
50
|
"chownr": "^2.0.0",
|
|
52
51
|
"fs-minipass": "^2.1.0",
|