@@ -12,10 +12,8 @@ import (
12
12
"strings"
13
13
14
14
"github.com/rs/zerolog"
15
- trusty "github.com/stacklok/trusty-sdk-go/pkg/v1/client"
16
- trustytypes "github.com/stacklok/trusty-sdk-go/pkg/v1/types"
17
- "golang.org/x/text/cases"
18
- "golang.org/x/text/language"
15
+ trusty "github.com/stacklok/trusty-sdk-go/pkg/v2/client"
16
+ trustytypes "github.com/stacklok/trusty-sdk-go/pkg/v2/types"
19
17
"google.golang.org/protobuf/reflect/protoreflect"
20
18
21
19
"github.com/mindersec/minder/internal/constants"
@@ -39,7 +37,7 @@ const (
39
37
type Evaluator struct {
40
38
cli provifv1.GitHub
41
39
endpoint string
42
- client * trusty.Trusty
40
+ client trusty.Trusty
43
41
}
44
42
45
43
// NewTrustyEvaluator creates a new trusty evaluator
@@ -296,135 +294,201 @@ type alternative struct {
296
294
297
295
func getDependencyScore (
298
296
ctx context.Context ,
299
- trustyClient * trusty.Trusty ,
297
+ trustyClient trusty.Trusty ,
300
298
dep * pbinternal.PrDependencies_ContextualDependency ,
301
299
) (* trustyReport , error ) {
302
300
// Call the Trusty API
303
- resp , err := trustyClient .Report (ctx , & trustytypes.Dependency {
304
- Name : dep .Dep .Name ,
305
- Version : dep .Dep .Version ,
306
- Ecosystem : trustytypes .Ecosystem (dep .Dep .Ecosystem ),
307
- })
301
+ packageType := dep .Dep .Ecosystem .AsString ()
302
+ input := & trustytypes.Dependency {
303
+ PackageName : dep .Dep .Name ,
304
+ PackageVersion : & dep .Dep .Version ,
305
+ PackageType : & packageType ,
306
+ }
307
+
308
+ respSummary , err := trustyClient .Summary (ctx , input )
309
+ if err != nil {
310
+ return nil , fmt .Errorf ("failed getting summary: %w" , err )
311
+ }
312
+
313
+ respPkg , err := trustyClient .PackageMetadata (ctx , input )
314
+ if err != nil {
315
+ return nil , fmt .Errorf ("failed getting package metadata: %w" , err )
316
+ }
317
+
318
+ respAlternatives , err := trustyClient .Alternatives (ctx , input )
308
319
if err != nil {
309
- return nil , fmt .Errorf ("failed to send request : %w" , err )
320
+ return nil , fmt .Errorf ("failed getting alternatives : %w" , err )
310
321
}
311
322
312
- res := makeTrustyReport (dep , resp )
323
+ respProvenance , err := trustyClient .Provenance (ctx , input )
324
+ if err != nil {
325
+ return nil , fmt .Errorf ("failed getting provenance: %w" , err )
326
+ }
327
+
328
+ res := makeTrustyReport (dep ,
329
+ respSummary ,
330
+ respPkg ,
331
+ respAlternatives ,
332
+ respProvenance ,
333
+ )
313
334
314
335
return res , nil
315
336
}
316
337
317
338
func makeTrustyReport (
318
339
dep * pbinternal.PrDependencies_ContextualDependency ,
319
- resp * trustytypes.Reply ,
340
+ respSummary * trustytypes.PackageSummaryAnnotation ,
341
+ respPkg * trustytypes.TrustyPackageData ,
342
+ respAlternatives * trustytypes.PackageAlternatives ,
343
+ respProvenance * trustytypes.Provenance ,
320
344
) * trustyReport {
321
345
res := & trustyReport {
322
- PackageName : dep .Dep .Name ,
323
- PackageVersion : dep .Dep .Version ,
324
- PackageType : dep .Dep .Ecosystem .AsString (),
325
- TrustyURL : makeTrustyURL (dep .Dep .Name , strings .ToLower (dep .Dep .Ecosystem .AsString ())),
326
- Score : resp .Summary .Score ,
327
- IsDeprecated : resp .PackageData .Deprecated ,
328
- IsArchived : resp .PackageData .Archived ,
329
- ActivityScore : getValueFromMap [float64 ](resp .Summary .Description , "activity" ),
330
- ProvenanceScore : getValueFromMap [float64 ](resp .Summary .Description , "provenance" ),
331
- }
332
-
333
- res .ScoreComponents = makeScoreComponents (resp .Summary .Description )
334
- res .Alternatives = makeAlternatives (dep .Dep .Ecosystem .AsString (), resp .Alternatives .Packages )
335
-
336
- if getValueFromMap [bool ](resp .Summary .Description , "malicious" ) {
337
- res .Malicious = & malicious {
338
- Summary : resp .PackageData .Malicious .Summary ,
339
- Details : preprocessDetails (resp .PackageData .Malicious .Details ),
340
- }
346
+ PackageName : dep .Dep .Name ,
347
+ PackageVersion : dep .Dep .Version ,
348
+ PackageType : dep .Dep .Ecosystem .AsString (),
349
+ TrustyURL : makeTrustyURL (dep .Dep .Name , strings .ToLower (dep .Dep .Ecosystem .AsString ())),
341
350
}
342
351
343
- res .Provenance = makeProvenance (resp .Provenance )
352
+ addSummaryDetails (res , respSummary )
353
+ addMetadataDetails (res , respPkg )
354
+
355
+ res .ScoreComponents = makeScoreComponents (respSummary .Description )
356
+ res .Alternatives = makeAlternatives (dep .Dep .Ecosystem .AsString (), respAlternatives .Packages )
357
+
358
+ if respSummary .Description .Malicious {
359
+ res .Malicious = makeMaliciousDetails (respPkg .Malicious )
360
+ }
361
+
362
+ res .Provenance = makeProvenance (respProvenance )
344
363
345
364
return res
346
365
}
347
366
348
- func makeScoreComponents (descr map [string ]any ) []scoreComponent {
367
+ func addSummaryDetails (res * trustyReport , resp * trustytypes.PackageSummaryAnnotation ) {
368
+ if resp == nil {
369
+ return
370
+ }
371
+
372
+ res .Score = resp .Score
373
+ res .ActivityScore = resp .Description .Activity
374
+ res .ProvenanceScore = resp .Description .Provenance
375
+ }
376
+
377
+ func addMetadataDetails (res * trustyReport , resp * trustytypes.TrustyPackageData ) {
378
+ if resp == nil {
379
+ return
380
+ }
381
+
382
+ res .IsDeprecated = resp .IsDeprecated != nil && * resp .IsDeprecated
383
+ res .IsArchived = resp .Archived != nil && * resp .Archived
384
+ }
385
+
386
+ func makeScoreComponents (resp trustytypes.SummaryDescription ) []scoreComponent {
349
387
scoreComponents := make ([]scoreComponent , 0 )
350
388
351
- if descr == nil {
352
- return scoreComponents
353
- }
354
-
355
- caser := cases .Title (language .Und , cases .NoLower )
356
- for l , v := range descr {
357
- switch l {
358
- case "activity" :
359
- l = "Package activity"
360
- case "activity_repo" :
361
- l = "Repository activity"
362
- case "activity_user" :
363
- l = "User activity"
364
- case "provenance_type" :
365
- l = "Provenance"
366
- case "typosquatting" :
367
- if f , ok := v .(float64 ); ok && f > 5.0 {
368
- // skip typosquatting entry
369
- continue
370
- }
371
- l = "Typosquatting"
372
- v = "⚠️ Dependency may be trying to impersonate a well known package"
373
- }
389
+ // activity scores
390
+ if resp .Activity != 0 {
391
+ scoreComponents = append (scoreComponents , scoreComponent {
392
+ Label : "Package activity" ,
393
+ Value : resp .Activity ,
394
+ })
395
+ }
396
+ if resp .ActivityRepo != 0 {
397
+ scoreComponents = append (scoreComponents , scoreComponent {
398
+ Label : "Repository activity" ,
399
+ Value : resp .ActivityRepo ,
400
+ })
401
+ }
402
+ if resp .ActivityUser != 0 {
403
+ scoreComponents = append (scoreComponents , scoreComponent {
404
+ Label : "User activity" ,
405
+ Value : resp .ActivityUser ,
406
+ })
407
+ }
374
408
375
- // Note: if none of the cases above match, we still
376
- // add the value to the list along with its
377
- // capitalized label.
409
+ // provenance information
410
+ if resp .ProvenanceType != nil {
411
+ scoreComponents = append (scoreComponents , scoreComponent {
412
+ Label : "Provenance" ,
413
+ Value : string (* resp .ProvenanceType ),
414
+ })
415
+ }
378
416
417
+ // typosquatting information
418
+ if resp .TypoSquatting != 0 && resp .TypoSquatting <= 5.0 {
379
419
scoreComponents = append (scoreComponents , scoreComponent {
380
- Label : fmt . Sprintf ( "%s%s" , caser . String ( l [ 0 : 1 ]), l [ 1 :]) ,
381
- Value : v ,
420
+ Label : "Typosquatting" ,
421
+ Value : "⚠️ Dependency may be trying to impersonate a well known package" ,
382
422
})
383
423
}
384
424
425
+ // Note: in the previous implementation based on Trusty v1
426
+ // API, if new fields were added to the `"description"` field
427
+ // of a package they were implicitly added to the table of
428
+ // score components.
429
+ //
430
+ // This was possible because the `Description` field of the go
431
+ // struct was defined as `map[string]any`.
432
+ //
433
+ // This is not the case with v2 API, so we need to keep track
434
+ // of new measures being added to the API.
435
+
385
436
return scoreComponents
386
437
}
387
438
388
439
func makeAlternatives (
389
440
ecosystem string ,
390
- trustyAlternatives []trustytypes.Alternative ,
441
+ trustyAlternatives []* trustytypes.PackageBasicInfo ,
391
442
) []alternative {
392
443
alternatives := []alternative {}
393
444
for _ , alt := range trustyAlternatives {
394
445
alternatives = append (alternatives , alternative {
395
446
PackageName : alt .PackageName ,
396
447
PackageType : ecosystem ,
397
- Score : & alt .Score ,
448
+ Score : alt .Score ,
398
449
TrustyURL : makeTrustyURL (alt .PackageName , ecosystem ),
399
450
})
400
451
}
401
452
402
453
return alternatives
403
454
}
404
455
456
+ func makeMaliciousDetails (
457
+ maliciousInfo * trustytypes.PackageMaliciousPayload ,
458
+ ) * malicious {
459
+ if maliciousInfo == nil {
460
+ return nil
461
+ }
462
+
463
+ return & malicious {
464
+ Summary : maliciousInfo .Summary ,
465
+ Details : preprocessDetails (maliciousInfo .Details ),
466
+ }
467
+ }
468
+
405
469
func makeProvenance (
406
- trustyProvenance * trustytypes.Provenance ,
470
+ resp * trustytypes.Provenance ,
407
471
) * provenance {
408
- if trustyProvenance == nil {
472
+ if resp == nil {
409
473
return nil
410
474
}
411
475
412
476
prov := & provenance {}
413
- if trustyProvenance . Description .Historical .Overlap != 0 {
477
+ if resp .Historical .Overlap != 0 {
414
478
prov .Historical = & historicalProvenance {
415
- Versions : int (trustyProvenance . Description .Historical .Versions ),
416
- Tags : int (trustyProvenance . Description .Historical .Tags ),
417
- Common : int (trustyProvenance . Description .Historical .Common ),
418
- Overlap : trustyProvenance . Description .Historical .Overlap ,
479
+ Versions : int (resp .Historical .Versions ),
480
+ Tags : int (resp .Historical .Tags ),
481
+ Common : int (resp .Historical .Common ),
482
+ Overlap : resp .Historical .Overlap ,
419
483
}
420
484
}
421
485
422
- if trustyProvenance . Description .Sigstore .Issuer != "" {
486
+ if resp .Sigstore .Issuer != "" {
423
487
prov .Sigstore = & sigstoreProvenance {
424
- SourceRepository : trustyProvenance . Description . Sigstore .SourceRepository ,
425
- Workflow : trustyProvenance . Description .Sigstore .Workflow ,
426
- Issuer : trustyProvenance . Description .Sigstore .Issuer ,
427
- RekorURI : trustyProvenance . Description .Sigstore .Transparency ,
488
+ SourceRepository : resp . Sigstore .SourceRepo ,
489
+ Workflow : resp .Sigstore .Workflow ,
490
+ Issuer : resp .Sigstore .Issuer ,
491
+ RekorURI : resp .Sigstore .Transparency ,
428
492
}
429
493
}
430
494
@@ -440,19 +504,6 @@ func makeTrustyURL(packageName string, ecosystem string) string {
440
504
return trustyURL
441
505
}
442
506
443
- func getValueFromMap [T any ](coll map [string ]any , field string ) T {
444
- var t T
445
- v , ok := coll [field ]
446
- if ! ok {
447
- return t
448
- }
449
- res , ok := v .(T )
450
- if ! ok {
451
- return t
452
- }
453
- return res
454
- }
455
-
456
507
// classifyDependency checks the dependencies from the PR for maliciousness or
457
508
// low scores and adds them to the summary if needed
458
509
func classifyDependency (
0 commit comments