@@ -24,7 +24,8 @@ class VerifySignatures {
24
24
this . missing = [ ]
25
25
this . checkedPackages = new Set ( )
26
26
this . auditedWithKeysCount = 0
27
- this . verifiedCount = 0
27
+ this . verifiedSignatureCount = 0
28
+ this . verifiedAttestationCount = 0
28
29
this . exitCode = 0
29
30
}
30
31
@@ -78,12 +79,25 @@ class VerifySignatures {
78
79
this . npm . output ( timing )
79
80
this . npm . output ( '' )
80
81
81
- if ( this . verifiedCount ) {
82
- const verifiedBold = this . npm . chalk . bold ( 'verified' )
83
- if ( this . verifiedCount === 1 ) {
84
- this . npm . output ( `${ this . verifiedCount } package has a ${ verifiedBold } registry signature` )
82
+ const verifiedBold = this . npm . chalk . bold ( 'verified' )
83
+ if ( this . verifiedSignatureCount ) {
84
+ if ( this . verifiedSignatureCount === 1 ) {
85
+ /* eslint-disable-next-line max-len */
86
+ this . npm . output ( `${ this . verifiedSignatureCount } package has a ${ verifiedBold } registry signature` )
87
+ } else {
88
+ /* eslint-disable-next-line max-len */
89
+ this . npm . output ( `${ this . verifiedSignatureCount } packages have ${ verifiedBold } registry signatures` )
90
+ }
91
+ this . npm . output ( '' )
92
+ }
93
+
94
+ if ( this . verifiedAttestationCount ) {
95
+ if ( this . verifiedAttestationCount === 1 ) {
96
+ /* eslint-disable-next-line max-len */
97
+ this . npm . output ( `${ this . verifiedAttestationCount } package has a ${ verifiedBold } attestation` )
85
98
} else {
86
- this . npm . output ( `${ this . verifiedCount } packages have ${ verifiedBold } registry signatures` )
99
+ /* eslint-disable-next-line max-len */
100
+ this . npm . output ( `${ this . verifiedAttestationCount } packages have ${ verifiedBold } attestations` )
87
101
}
88
102
this . npm . output ( '' )
89
103
}
@@ -110,19 +124,35 @@ class VerifySignatures {
110
124
const invalidClr = this . npm . chalk . bold ( this . npm . chalk . red ( 'invalid' ) )
111
125
// We can have either invalid signatures or invalid provenance
112
126
const invalidSignatures = this . invalid . filter ( i => i . code === 'EINTEGRITYSIGNATURE' )
113
- if ( invalidSignatures . length === 1 ) {
114
- this . npm . output ( `1 package has an ${ invalidClr } registry signature:` )
115
- // } else if (invalidSignatures.length > 1) {
116
- } else {
117
- // TODO move this back to an else if once provenance attestation audit is added
118
- /* eslint-disable-next-line max-len */
119
- this . npm . output ( `${ invalidSignatures . length } packages have ${ invalidClr } registry signatures:` )
127
+ if ( invalidSignatures . length ) {
128
+ if ( invalidSignatures . length === 1 ) {
129
+ this . npm . output ( `1 package has an ${ invalidClr } registry signature:` )
130
+ } else {
131
+ /* eslint-disable-next-line max-len */
132
+ this . npm . output ( `${ invalidSignatures . length } packages have ${ invalidClr } registry signatures:` )
133
+ }
134
+ this . npm . output ( '' )
135
+ invalidSignatures . map ( i =>
136
+ this . npm . output ( `${ this . npm . chalk . red ( `${ i . name } @${ i . version } ` ) } (${ i . registry } )` )
137
+ )
138
+ this . npm . output ( '' )
120
139
}
121
- this . npm . output ( '' )
122
- invalidSignatures . map ( i =>
123
- this . npm . output ( `${ this . npm . chalk . red ( `${ i . name } @${ i . version } ` ) } (${ i . registry } )` )
124
- )
125
- this . npm . output ( '' )
140
+
141
+ const invalidAttestations = this . invalid . filter ( i => i . code === 'EATTESTATIONVERIFY' )
142
+ if ( invalidAttestations . length ) {
143
+ if ( invalidAttestations . length === 1 ) {
144
+ this . npm . output ( `1 package has an ${ invalidClr } attestation:` )
145
+ } else {
146
+ /* eslint-disable-next-line max-len */
147
+ this . npm . output ( `${ invalidAttestations . length } packages have ${ invalidClr } attestations:` )
148
+ }
149
+ this . npm . output ( '' )
150
+ invalidAttestations . map ( i =>
151
+ this . npm . output ( `${ this . npm . chalk . red ( `${ i . name } @${ i . version } ` ) } (${ i . registry } )` )
152
+ )
153
+ this . npm . output ( '' )
154
+ }
155
+
126
156
if ( invalid . length === 1 ) {
127
157
/* eslint-disable-next-line max-len */
128
158
this . npm . output ( `Someone might have tampered with this package since it was published on the registry!` )
@@ -252,16 +282,19 @@ class VerifySignatures {
252
282
const {
253
283
_integrity : integrity ,
254
284
_signatures,
285
+ _attestations,
255
286
_resolved : resolved ,
256
287
} = await pacote . manifest ( `${ name } @${ version } ` , {
257
288
verifySignatures : true ,
289
+ verifyAttestations : true ,
258
290
...this . buildRegistryConfig ( registry ) ,
259
291
...this . npm . flatOptions ,
260
292
} )
261
293
const signatures = _signatures || [ ]
262
294
const result = {
263
295
integrity,
264
296
signatures,
297
+ attestations : _attestations ,
265
298
resolved,
266
299
}
267
300
return result
@@ -287,14 +320,14 @@ class VerifySignatures {
287
320
}
288
321
289
322
try {
290
- const { integrity, signatures, resolved } = await this . verifySignatures (
323
+ const { integrity, signatures, attestations , resolved } = await this . verifySignatures (
291
324
name , version , registry
292
325
)
293
326
294
327
// Currently we only care about missing signatures on registries that provide a public key
295
328
// We could make this configurable in the future with a strict/paranoid mode
296
329
if ( signatures . length ) {
297
- this . verifiedCount += 1
330
+ this . verifiedSignatureCount += 1
298
331
} else if ( keys . length ) {
299
332
this . missing . push ( {
300
333
integrity,
@@ -305,17 +338,26 @@ class VerifySignatures {
305
338
version,
306
339
} )
307
340
}
341
+
342
+ // Track verified attestations separately to registry signatures, as all
343
+ // packages on registries with signing keys are expected to have registry
344
+ // signatures, but not all packages have provenance and publish attestations.
345
+ if ( attestations ) {
346
+ this . verifiedAttestationCount += 1
347
+ }
308
348
} catch ( e ) {
309
- if ( e . code === 'EINTEGRITYSIGNATURE' ) {
349
+ if ( e . code === 'EINTEGRITYSIGNATURE' || e . code === 'EATTESTATIONVERIFY' ) {
310
350
this . invalid . push ( {
311
351
code : e . code ,
352
+ message : e . message ,
312
353
integrity : e . integrity ,
313
354
keyid : e . keyid ,
314
355
location,
315
356
name,
316
357
registry,
317
358
resolved : e . resolved ,
318
359
signature : e . signature ,
360
+ predicateType : e . predicateType ,
319
361
type,
320
362
version,
321
363
} )
0 commit comments