Skip to content

Commit

Permalink
Decouple Trusty engine from Trusty SDK structs.
Browse files Browse the repository at this point in the history
This change aims to limit the impact of changes deriving from Trusty
SDK to fewer lines of code. Specifically, business logic implemented
in Trusty evaluation engine looks into the response as returned by the
SDK itself, making it harder to move to Trusty API v2.

Note: this change is meant to be bug-compatible with the current
Trusty evaluation engine.
  • Loading branch information
blkt committed Nov 20, 2024
1 parent 1fa93ef commit f23a861
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 334 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ require (
golang.org/x/mod v0.22.0
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/text v0.20.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
Expand Down
94 changes: 37 additions & 57 deletions internal/engine/eval/trusty/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ import (
"slices"
"strings"
template "text/template"
"unicode"

"github.com/google/go-github/v63/github"
"github.com/rs/zerolog"
trustytypes "github.com/stacklok/trusty-sdk-go/pkg/v1/types"

"github.com/mindersec/minder/internal/constants"
"github.com/mindersec/minder/internal/engine/eval/pr_actions"
Expand Down Expand Up @@ -194,7 +192,7 @@ type dependencyAlternatives struct {
BlockPR bool

// trustyReply is the complete response from trusty for this package
trustyReply *trustytypes.Reply
trustyReply *trustyReport
}

// summaryPrHandler is a prStatusHandler that adds a summary text to the PR as a comment.
Expand Down Expand Up @@ -276,8 +274,8 @@ func (sph *summaryPrHandler) generateSummary() (string, error) {
for _, alternative := range sph.trackedAlternatives {
if _, ok := lowScorePackages[alternative.Dependency.Name]; !ok {
var score float64
if alternative.trustyReply.Summary.Score != nil {
score = *alternative.trustyReply.Summary.Score
if alternative.trustyReply.Score != nil {
score = *alternative.trustyReply.Score
}

packageUIURL, err := url.JoinPath(
Expand All @@ -300,37 +298,42 @@ func (sph *summaryPrHandler) generateSummary() (string, error) {
if slices.Contains(alternative.Reasons, TRUSTY_MALICIOUS_PKG) {
malicious = append(malicious, maliciousTemplateData{
templatePackageData: packageData,
Summary: alternative.trustyReply.PackageData.Malicious.Summary,
Details: preprocessDetails(alternative.trustyReply.PackageData.Malicious.Details),
Summary: alternative.trustyReply.Malicious.Summary,
Details: preprocessDetails(alternative.trustyReply.Malicious.Details),
})
continue
}

lowScorePackages[alternative.Dependency.Name] = templatePackage{
templatePackageData: packageData,
Deprecated: alternative.trustyReply.PackageData.Deprecated,
Archived: alternative.trustyReply.PackageData.Archived,
ScoreComponents: buildScoreMatrix(alternative),
Deprecated: alternative.trustyReply.IsDeprecated,
Archived: alternative.trustyReply.IsArchived,
ScoreComponents: buildScoreMatrix(alternative.trustyReply.ScoreComponents),
Alternatives: []templateAlternative{},
Provenance: buildProvenanceStruct(alternative.trustyReply),
}
}

for _, altData := range alternative.trustyReply.Alternatives.Packages {
if altData.Score <= lowScorePackages[alternative.Dependency.Name].Score {
for _, altData := range alternative.trustyReply.Alternatives {
// Note: now that the score is deprecated and
// effectively `nil` for all packages, this
// loop will always discard all alternatives,
// rendering the whole block dead code.
//
// Since (1) we don't have score anymore, and
// (2) we don't suggest malicious packages, I
// suggest getting rid of this check
// altogether.
if altData.Score != nil && *altData.Score <= lowScorePackages[alternative.Dependency.Name].Score {
continue
}

altPackageData := templateAlternative{
templatePackageData: templatePackageData{
Ecosystem: alternative.Dependency.Ecosystem.AsString(),
Ecosystem: altData.PackageType,
PackageName: altData.PackageName,
TrustyURL: fmt.Sprintf(
"%s%s/%s", constants.TrustyHttpURL,
strings.ToLower(alternative.Dependency.Ecosystem.AsString()),
url.PathEscape(altData.PackageName),
),
Score: altData.Score,
TrustyURL: altData.TrustyURL,
Score: *altData.Score,
},
}

Expand All @@ -344,27 +347,27 @@ func (sph *summaryPrHandler) generateSummary() (string, error) {
}

// buildProvenanceStruct builds the provenance data structure for the PR template
func buildProvenanceStruct(r *trustytypes.Reply) *templateProvenance {
func buildProvenanceStruct(r *trustyReport) *templateProvenance {
if r == nil || r.Provenance == nil {
return nil
}
var provenance *templateProvenance
if r.Provenance != nil {
provenance = &templateProvenance{}
if r.Provenance.Description.Historical.Overlap != 0 {
if r.Provenance.Historical != nil && r.Provenance.Historical.Overlap != 0 {
provenance.Historical = &templateHistoricalProvenance{
NumVersions: int(r.Provenance.Description.Historical.Versions),
NumTags: int(r.Provenance.Description.Historical.Tags),
MatchedVersions: int(r.Provenance.Description.Historical.Common),
NumVersions: int(r.Provenance.Historical.Versions),
NumTags: int(r.Provenance.Historical.Tags),
MatchedVersions: int(r.Provenance.Historical.Common),
}
}

if r.Provenance.Description.Sigstore.Issuer != "" {
if r.Provenance.Sigstore != nil && r.Provenance.Sigstore.Issuer != "" {
provenance.Sigstore = &templateSigstoreProvenance{
SourceRepository: r.Provenance.Description.Sigstore.SourceRepository,
Workflow: r.Provenance.Description.Sigstore.Workflow,
Issuer: r.Provenance.Description.Sigstore.Issuer,
RekorURI: r.Provenance.Description.Sigstore.Transparency,
SourceRepository: r.Provenance.Sigstore.SourceRepository,
Workflow: r.Provenance.Sigstore.Workflow,
Issuer: r.Provenance.Sigstore.Issuer,
RekorURI: r.Provenance.Sigstore.RekorURI,
}
}

Expand All @@ -377,35 +380,12 @@ func buildProvenanceStruct(r *trustytypes.Reply) *templateProvenance {

// buildScoreMatrix builds the score components matrix that populates
// the score table in the PR comment template
func buildScoreMatrix(alternative dependencyAlternatives) []templateScoreComponent {
//
//nolint:gosimple // This code is legacy and should be removed
func buildScoreMatrix(components []scoreComponent) []templateScoreComponent {
scoreComp := []templateScoreComponent{}
if alternative.trustyReply.Summary.Description != nil {
for l, v := range alternative.trustyReply.Summary.Description {
switch l {
case "activity":
l = "Package activity"
case "activity_user":
l = "User activity"
case "provenance":
l = "Provenance"
case "typosquatting":
if v.(float64) > 5.00 {
continue
}
v = "⚠️ Dependency may be trying to impersonate a well known package"
l = "Typosquatting"
case "activity_repo":
l = "Repository activity"
default:
if len(l) > 1 {
l = string(unicode.ToUpper([]rune(l)[0])) + l[1:]
}
}
scoreComp = append(scoreComp, templateScoreComponent{
Label: l,
Value: v,
})
}
for _, component := range components {
scoreComp = append(scoreComp, templateScoreComponent(component))
}
return scoreComp
}
Expand Down
68 changes: 29 additions & 39 deletions internal/engine/eval/trusty/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package trusty
import (
"testing"

trustytypes "github.com/stacklok/trusty-sdk-go/pkg/v1/types"
"github.com/stretchr/testify/require"

v1 "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
Expand All @@ -26,28 +25,25 @@ func TestBuildProvenanceStruct(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
sut *trustytypes.Reply
sut *trustyReport
mustNil bool
expected *templateProvenance
}{
{
name: "full-response",
sut: &trustytypes.Reply{
Provenance: &trustytypes.Provenance{
Score: 8.0,
Description: trustytypes.ProvenanceDescription{
Historical: trustytypes.HistoricalProvenance{
Tags: 10,
Common: 8,
Overlap: 80,
Versions: 10,
},
Sigstore: trustytypes.SigstoreProvenance{
Issuer: "CN=sigstore-intermediate,O=sigstore.dev",
Workflow: ".github/workflows/build_and_deploy.yml",
SourceRepository: "https://github.com/vercel/next.js",
Transparency: "https://search.sigstore.dev/?logIndex=88381843",
},
sut: &trustyReport{
Provenance: &provenance{
Historical: &historicalProvenance{
Tags: 10,
Common: 8,
Overlap: 80,
Versions: 10,
},
Sigstore: &sigstoreProvenance{
Issuer: "CN=sigstore-intermediate,O=sigstore.dev",
Workflow: ".github/workflows/build_and_deploy.yml",
SourceRepository: "https://github.com/vercel/next.js",
RekorURI: "https://search.sigstore.dev/?logIndex=88381843",
},
},
},
Expand All @@ -68,16 +64,13 @@ func TestBuildProvenanceStruct(t *testing.T) {
},
{
name: "only-historical",
sut: &trustytypes.Reply{
Provenance: &trustytypes.Provenance{
Score: 8.0,
Description: trustytypes.ProvenanceDescription{
Historical: trustytypes.HistoricalProvenance{
Tags: 10,
Common: 8,
Overlap: 80,
Versions: 10,
},
sut: &trustyReport{
Provenance: &provenance{
Historical: &historicalProvenance{
Tags: 10,
Common: 8,
Overlap: 80,
Versions: 10,
},
},
},
Expand All @@ -92,16 +85,13 @@ func TestBuildProvenanceStruct(t *testing.T) {
},
{
name: "only-sigstore",
sut: &trustytypes.Reply{
Provenance: &trustytypes.Provenance{
Score: 8.0,
Description: trustytypes.ProvenanceDescription{
Sigstore: trustytypes.SigstoreProvenance{
Issuer: "CN=sigstore-intermediate,O=sigstore.dev",
Workflow: ".github/workflows/build_and_deploy.yml",
SourceRepository: "https://github.com/vercel/next.js",
Transparency: "https://search.sigstore.dev/?logIndex=88381843",
},
sut: &trustyReport{
Provenance: &provenance{
Sigstore: &sigstoreProvenance{
Issuer: "CN=sigstore-intermediate,O=sigstore.dev",
Workflow: ".github/workflows/build_and_deploy.yml",
SourceRepository: "https://github.com/vercel/next.js",
RekorURI: "https://search.sigstore.dev/?logIndex=88381843",
},
},
},
Expand All @@ -122,7 +112,7 @@ func TestBuildProvenanceStruct(t *testing.T) {
},
{
name: "no-provenance",
sut: &trustytypes.Reply{},
sut: &trustyReport{},
mustNil: true,
},
} {
Expand Down
Loading

0 comments on commit f23a861

Please sign in to comment.