Skip to content
Merged
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
2 changes: 1 addition & 1 deletion contrib/trivy/parser/v2/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error)
return nil, err
}

scanResult, err := pkg.Convert(report.Results)
scanResult, err := pkg.Convert(report.Results, report.ArtifactType, report.ArtifactName)
if err != nil {
return nil, err
}
Expand Down
26 changes: 12 additions & 14 deletions contrib/trivy/parser/v2/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,7 @@ var strutsSR = &models.ScanResult{
Name: "commons-beanutils:commons-beanutils",
Version: "1.7.0",
FixedIn: "1.9.2",
//TODO use Artifactname?
Path: "Java",
Path: "/data/struts-1.2.7/lib/Java",
},
},
AffectedPackages: models.PackageFixStatuses{},
Expand Down Expand Up @@ -638,8 +637,7 @@ var strutsSR = &models.ScanResult{
Name: "struts:struts",
Version: "1.2.7",
FixedIn: "",
//TODO use Artifactname?
Path: "Java",
Path: "/data/struts-1.2.7/lib/Java",
},
},
AffectedPackages: models.PackageFixStatuses{},
Expand All @@ -648,7 +646,7 @@ var strutsSR = &models.ScanResult{
LibraryScanners: models.LibraryScanners{
models.LibraryScanner{
Type: "jar",
LockfilePath: "Java",
LockfilePath: "/data/struts-1.2.7/lib/Java",
Libs: []models.Library{
{
Name: "commons-beanutils:commons-beanutils",
Expand Down Expand Up @@ -1099,15 +1097,15 @@ var osAndLibSR = &models.ScanResult{
Name: "activesupport",
Version: "6.0.2.1",
FixedIn: "6.0.3.1, 5.2.4.3",
Path: "Ruby",
Path: "/var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
},
},
},
},
LibraryScanners: models.LibraryScanners{
models.LibraryScanner{
Type: "gemspec",
LockfilePath: "Ruby",
LockfilePath: "/var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
Libs: []models.Library{
{
Name: "activesupport",
Expand Down Expand Up @@ -1589,15 +1587,15 @@ var osAndLib2SR = &models.ScanResult{
Name: "activesupport",
Version: "6.0.2.1",
FixedIn: "6.0.3.1, 5.2.4.3",
Path: "Ruby",
Path: "/var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
},
},
},
},
LibraryScanners: models.LibraryScanners{
models.LibraryScanner{
Type: "gemspec",
LockfilePath: "Ruby",
LockfilePath: "/var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
Libs: []models.Library{
{
Name: "activesupport",
Expand Down Expand Up @@ -2884,14 +2882,14 @@ var oneCVEtoNVulnerabilitySR = &models.ScanResult{
Name: "pubnub",
Version: "0.3.0",
FixedIn: "0.4.0",
Path: "Cargo.lock",
Path: "/Cargo.lock",
},
{
Key: "composer",
Name: "pubnub/pubnub",
Version: "6.0.0",
FixedIn: "6.1.0",
Path: "composer.lock",
Path: "/composer.lock",
},
},
},
Expand All @@ -2903,15 +2901,15 @@ var oneCVEtoNVulnerabilitySR = &models.ScanResult{
Name: "pubnub",
Version: "0.3.0",
}},
LockfilePath: "Cargo.lock",
LockfilePath: "/Cargo.lock",
},
{
Type: "composer",
Libs: []models.Library{{
Name: "pubnub/pubnub",
Version: "6.0.0",
}},
LockfilePath: "composer.lock",
LockfilePath: "/composer.lock",
},
},
Packages: models.Packages{
Expand Down Expand Up @@ -3001,7 +2999,7 @@ var includeDevDependenciesSR = &models.ScanResult{
Dev: true,
},
},
LockfilePath: "pnpm-lock.yaml",
LockfilePath: "integration/data/lockfile/pnpm-v9/pnpm-lock.yaml",
},
},
Optional: nil,
Expand Down
108 changes: 84 additions & 24 deletions contrib/trivy/pkg/converter.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package pkg

import (
"cmp"
"fmt"
"os"
"path/filepath"
"slices"
"sort"
"strings"
"time"

Expand All @@ -15,16 +17,30 @@ import (
)

// Convert :
func Convert(results types.Results) (result *models.ScanResult, err error) {
func Convert(results types.Results, artifactType ftypes.ArtifactType, artifactName string) (result *models.ScanResult, err error) {
scanResult := &models.ScanResult{
JSONVersion: models.JSONVersion,
ScannedCves: models.VulnInfos{},
}

scanmode := func() ftypes.ArtifactType {
switch artifactType {
case ftypes.TypeFilesystem:
// It is not possible to distinguish between fs and rootfs from the artifact type,
// so we have no choice but to determine whether or not the results contain os-pkg.
if slices.ContainsFunc(results, func(e types.Result) bool { return e.Class == types.ClassOSPkg }) {
return "rootfs"
}
return ftypes.TypeFilesystem
default:
return artifactType
}
}()

pkgs := models.Packages{}
srcPkgs := models.SrcPackages{}
vulnInfos := models.VulnInfos{}
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
libraryScannerPaths := map[string]models.LibraryScanner{}
for _, trivyResult := range results {
for _, vuln := range trivyResult.Vulnerabilities {
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
Expand Down Expand Up @@ -57,8 +73,8 @@ func Convert(results types.Results) (result *models.ScanResult, err error) {
})
}

sort.Slice(references, func(i, j int) bool {
return references[i].Link < references[j].Link
slices.SortFunc(references, func(a, b models.Reference) int {
return cmp.Compare(a.Link, b.Link)
})

var published time.Time
Expand Down Expand Up @@ -134,21 +150,23 @@ func Convert(results types.Results) (result *models.ScanResult, err error) {
FixedIn: vuln.FixedVersion,
})
} else {
lockfilePath := getLockfilePath(scanmode, artifactName, trivyResult.Type, trivyResult.Target, vuln.PkgPath)

vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
Key: string(trivyResult.Type),
Name: vuln.PkgName,
Version: vuln.InstalledVersion,
Path: trivyResult.Target,
FixedIn: vuln.FixedVersion,
Path: lockfilePath,
})
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
libScanner := libraryScannerPaths[lockfilePath]
libScanner.Type = trivyResult.Type
libScanner.Libs = append(libScanner.Libs, models.Library{
Name: vuln.PkgName,
Version: vuln.InstalledVersion,
FilePath: vuln.PkgPath,
})
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
libraryScannerPaths[lockfilePath] = libScanner
}
vulnInfos[vuln.VulnerabilityID] = vulnInfo
}
Expand Down Expand Up @@ -188,25 +206,29 @@ func Convert(results types.Results) (result *models.ScanResult, err error) {
srcPkgs[p.SrcName] = v
}
case types.ClassLangPkg:
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
libScanner.Type = trivyResult.Type
for _, p := range trivyResult.Packages {
libScanner.Libs = append(libScanner.Libs, models.Library{
Name: p.Name,
Version: p.Version,
PURL: getPURL(p),
FilePath: p.FilePath,
Dev: p.Dev,
})
lockfilePath := getLockfilePath(scanmode, artifactName, trivyResult.Type, trivyResult.Target, p.FilePath)

libScanner := libraryScannerPaths[lockfilePath]
libScanner.Type = trivyResult.Type
for _, p := range trivyResult.Packages {
libScanner.Libs = append(libScanner.Libs, models.Library{
Name: p.Name,
Version: p.Version,
PURL: getPURL(p),
FilePath: p.FilePath,
Dev: p.Dev,
})
}
libraryScannerPaths[lockfilePath] = libScanner
}
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
default:
}
}

// flatten and unique libraries
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
for path, v := range uniqueLibraryScannerPaths {
libraryScanners := make([]models.LibraryScanner, 0, len(libraryScannerPaths))
for path, v := range libraryScannerPaths {
uniqueLibrary := map[string]models.Library{}
for _, lib := range v.Libs {
uniqueLibrary[lib.Name+lib.Version] = lib
Expand All @@ -217,8 +239,8 @@ func Convert(results types.Results) (result *models.ScanResult, err error) {
libraries = append(libraries, library)
}

sort.Slice(libraries, func(i, j int) bool {
return libraries[i].Name < libraries[j].Name
slices.SortFunc(libraries, func(a, b models.Library) int {
return cmp.Compare(a.Name, b.Name)
})

libscanner := models.LibraryScanner{
Expand All @@ -228,8 +250,8 @@ func Convert(results types.Results) (result *models.ScanResult, err error) {
}
libraryScanners = append(libraryScanners, libscanner)
}
sort.Slice(libraryScanners, func(i, j int) bool {
return libraryScanners[i].LockfilePath < libraryScanners[j].LockfilePath
slices.SortFunc(libraryScanners, func(a, b models.LibraryScanner) int {
return cmp.Compare(a.LockfilePath, b.LockfilePath)
})
scanResult.ScannedCves = vulnInfos
scanResult.Packages = pkgs
Expand Down Expand Up @@ -275,3 +297,41 @@ func getPURL(p ftypes.Package) string {
}
return p.Identifier.PURL.String()
}

func getLockfilePath(scanmode ftypes.ArtifactType, artifactName string, libType ftypes.LangType, target string, libFilepath string) string {
p := func() string {
switch libType {
case ftypes.NodePkg, ftypes.GemSpec, ftypes.PythonPkg:
if libFilepath == "" {
return target
}
return libFilepath
case ftypes.Jar:
if libFilepath == "" {
return target
}
for _, sep := range []string{".jar", ".war", ".par", ".ear"} {
if lhs, _, ok := strings.Cut(libFilepath, fmt.Sprintf("%s%s", sep, string(os.PathSeparator))); ok {
return fmt.Sprintf("%s%s", lhs, sep)
}
}
return libFilepath
default:
return target
}
}()

switch scanmode {
case ftypes.TypeContainerImage:
return filepath.Join(string(os.PathSeparator), p)
case "rootfs": // rootfs does not have the path passed to the command in artifactName
return p
case ftypes.TypeFilesystem, ftypes.TypeRepository:
if strings.HasSuffix(artifactName, p) {
return artifactName
}
return filepath.Join(artifactName, p)
default:
return p
}
}
Loading
Loading