Skip to content

Commit 6f75b51

Browse files
committed
fix: error when passing signature without keys
Raise an error when the package has a signature, `verifySignatures` is true and there are no registry keys configured. Updated the error message and fixed the undefined name@version in the error message to match the test cases here: npm/cli#4827 Possible anti-pattern: I've attached a bunch of error attributes if the signature is invalid so we can present this in the json output to ease debugging.
1 parent ea656cc commit 6f75b51

File tree

2 files changed

+55
-38
lines changed

2 files changed

+55
-38
lines changed

lib/registry.js

+41-35
Original file line numberDiff line numberDiff line change
@@ -165,43 +165,49 @@ class RegistryFetcher extends Fetcher {
165165
mani._integrity = String(this.integrity)
166166
if (dist.signatures) {
167167
if (this.opts.verifySignatures) {
168-
if (this.registryKeys) {
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.filter(key => (key.keyid === signature.keyid))[0]
173-
if (!publicKey) {
174-
throw Object.assign(new Error(
175-
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
176-
'but no corresponding public key can be found.'
177-
), { code: 'EMISSINGSIGNATUREKEY' })
178-
}
179-
const validPublicKey =
180-
!publicKey.expires || (Date.parse(publicKey.expires) > Date.now())
181-
if (!validPublicKey) {
182-
throw Object.assign(new Error(
183-
`${mani._id} has a signature with keyid: ${signature.keyid} ` +
184-
`but the corresponding public key has expired ${publicKey.expires}`
185-
), { code: 'EEXPIREDSIGNATUREKEY' })
186-
}
187-
const verifier = crypto.createVerify('SHA256')
188-
verifier.write(message)
189-
verifier.end()
190-
const valid = verifier.verify(
191-
publicKey.pemkey,
192-
signature.sig,
193-
'base64'
194-
)
195-
if (!valid) {
196-
throw Object.assign(new Error(
197-
'Integrity checksum signature failed: ' +
198-
`key ${publicKey.keyid} signature ${signature.sig}`
199-
), { code: 'EINTEGRITYSIGNATURE' })
200-
}
168+
// validate and throw on error, then set _signatures
169+
const _id = `${mani.name}@${mani.version}`
170+
const message = `${_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+
`${_id} has a registry signature with keyid: ${signature.keyid} ` +
177+
`but no corresponding public key can be found on ${this.registry}-/npm/v1/keys`
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+
`${_id} has a registry signature with keyid: ${signature.keyid} ` +
185+
`but the corresponding public key on ${this.registry}-/npm/v1/keys ` +
186+
`has expired ${publicKey.expires}`
187+
), { code: 'EEXPIREDSIGNATUREKEY' })
188+
}
189+
const verifier = crypto.createVerify('SHA256')
190+
verifier.write(message)
191+
verifier.end()
192+
const valid = verifier.verify(
193+
publicKey.pemkey,
194+
signature.sig,
195+
'base64'
196+
)
197+
if (!valid) {
198+
throw Object.assign(new Error(
199+
`${_id} has an invalid registry signature with ` +
200+
`keyid: ${publicKey.keyid} and signature: ${signature.sig}`
201+
), {
202+
code: 'EINTEGRITYSIGNATURE',
203+
keyid: publicKey.keyid,
204+
signature: signature.sig,
205+
resolved: mani._resolved,
206+
integrity: mani._integrity,
207+
})
201208
}
202-
mani._signatures = dist.signatures
203209
}
204-
// if no keys, don't set _signatures
210+
mani._signatures = dist.signatures
205211
} else {
206212
mani._signatures = dist.signatures
207213
}

test/registry.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,15 @@ t.test('verifySignatures invalid signature', async t => {
240240
}],
241241
})
242242
return t.rejects(
243+
/abbrev@1\.1\.1 has an invalid registry signature/,
243244
f.manifest(),
244245
{
245246
code: 'EINTEGRITYSIGNATURE',
247+
keyid: 'SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA',
248+
signature: 'nope',
249+
resolved: 'https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz',
250+
// eslint-disable-next-line max-len
251+
integrity: 'sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==',
246252
}
247253
)
248254
})
@@ -258,6 +264,7 @@ t.test('verifySignatures no valid key', async t => {
258264
})
259265
return t.rejects(
260266
f.manifest(),
267+
/@isaacs\/namespace-test@1\.0\.0 has a registry signature/,
261268
{
262269
code: 'EMISSINGSIGNATUREKEY',
263270
}
@@ -271,9 +278,13 @@ t.test('verifySignatures no registry keys at all', async t => {
271278
verifySignatures: true,
272279
[`//localhost:${port}/:_keys`]: null,
273280
})
274-
return f.manifest().then(mani => {
275-
t.notOk(mani._signatures)
276-
})
281+
return t.rejects(
282+
f.manifest(),
283+
/@isaacs\/namespace-test@1\.0\.0 has a registry signature/,
284+
{
285+
code: 'EMISSINGSIGNATUREKEY',
286+
}
287+
)
277288
})
278289

279290
t.test('404 fails with E404', t => {

0 commit comments

Comments
 (0)