Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions detector/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/google/go-containerregistry/pkg/name"
"github.com/samber/lo"
"golang.org/x/xerrors"

"github.com/future-architect/vuls/config"
Expand Down Expand Up @@ -228,7 +227,7 @@ func (d *libraryDetector) improveJARInfo() error {
libs = append(libs, l)
}

d.scanner.Libs = lo.UniqBy(libs, func(lib models.Library) string {
d.scanner.Libs = uniqBy(libs, func(lib models.Library) string {
return fmt.Sprintf("%s::%s::%s", lib.Name, lib.Version, lib.FilePath)
})
return nil
Expand Down
57 changes: 57 additions & 0 deletions detector/uniqby_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//go:build !scanner

package detector

import (
"reflect"
"testing"
)

func TestUniqBy(t *testing.T) {
t.Parallel()

t.Run("empty slice returns empty", func(t *testing.T) {
got := uniqBy([]int{}, func(v int) int { return v })
if len(got) != 0 {
t.Errorf("expected empty slice, got %v", got)
}
})

t.Run("no duplicates returns same slice", func(t *testing.T) {
got := uniqBy([]int{1, 2, 3}, func(v int) int { return v })
want := []int{1, 2, 3}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("duplicates removed, first occurrence kept", func(t *testing.T) {
got := uniqBy([]int{1, 2, 3, 2, 1, 4}, func(v int) int { return v })
want := []int{1, 2, 3, 4}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("works with struct key extraction", func(t *testing.T) {
type item struct {
ID int
Name string
}
input := []item{
{ID: 1, Name: "first"},
{ID: 2, Name: "second"},
{ID: 1, Name: "duplicate"},
{ID: 3, Name: "third"},
}
got := uniqBy(input, func(v item) int { return v.ID })
want := []item{
{ID: 1, Name: "first"},
{ID: 2, Name: "second"},
{ID: 3, Name: "third"},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})
}
14 changes: 14 additions & 0 deletions detector/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,17 @@ func ValidateDBs(cveConf config.GoCveDictConf, gostConf config.GostConf, exploit

return nil
}

func uniqBy[T any, K comparable](collection []T, keyFn func(T) K) []T {
seen := make(map[K]struct{}, len(collection))
result := make([]T, 0, len(collection))
for _, item := range collection {
k := keyFn(item)
if _, ok := seen[k]; ok {
continue
}
seen[k] = struct{}{}
result = append(result, item)
}
return result
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ require (
github.com/pkg/errors v0.9.1
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/samber/lo v1.52.0
github.com/sirupsen/logrus v1.9.4
github.com/spdx/tools-golang v0.5.7
github.com/spf13/cobra v1.10.2
Expand Down Expand Up @@ -298,6 +297,7 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/samber/lo v1.52.0 // indirect
github.com/samber/oops v1.18.1 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/schollz/progressbar/v3 v3.19.0 // indirect
Expand Down
17 changes: 15 additions & 2 deletions scanner/trivy/jar/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
xos "github.com/aquasecurity/trivy/pkg/x/os"
"github.com/samber/lo"
"golang.org/x/xerrors"
)

Expand Down Expand Up @@ -394,7 +393,21 @@ func (m manifest) determineVersion() (string, error) {
}

func removeLibraryDuplicates(libs []jarLibrary) []jarLibrary {
return lo.UniqBy(libs, func(lib jarLibrary) string {
return uniqBy(libs, func(lib jarLibrary) string {
return fmt.Sprintf("%s::%s::%s", lib.name, lib.version, lib.filePath)
})
}

func uniqBy[T any, K comparable](collection []T, keyFn func(T) K) []T {
seen := make(map[K]struct{}, len(collection))
result := make([]T, 0, len(collection))
for _, item := range collection {
k := keyFn(item)
if _, ok := seen[k]; ok {
continue
}
seen[k] = struct{}{}
result = append(result, item)
}
return result
}
55 changes: 55 additions & 0 deletions scanner/trivy/jar/uniqby_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package jar

import (
"reflect"
"testing"
)

func TestUniqBy(t *testing.T) {
t.Parallel()

t.Run("empty slice returns empty", func(t *testing.T) {
got := uniqBy([]int{}, func(v int) int { return v })
if len(got) != 0 {
t.Errorf("expected empty slice, got %v", got)
}
})

t.Run("no duplicates returns same slice", func(t *testing.T) {
got := uniqBy([]int{1, 2, 3}, func(v int) int { return v })
want := []int{1, 2, 3}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("duplicates removed, first occurrence kept", func(t *testing.T) {
got := uniqBy([]int{1, 2, 3, 2, 1, 4}, func(v int) int { return v })
want := []int{1, 2, 3, 4}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("works with struct key extraction", func(t *testing.T) {
type item struct {
ID int
Name string
}
input := []item{
{ID: 1, Name: "first"},
{ID: 2, Name: "second"},
{ID: 1, Name: "duplicate"},
{ID: 3, Name: "third"},
}
got := uniqBy(input, func(v item) int { return v.ID })
want := []item{
{ID: 1, Name: "first"},
{ID: 2, Name: "second"},
{ID: 3, Name: "third"},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})
}
Loading