diff --git a/detector/library.go b/detector/library.go index e2575e12..80f0c260 100644 --- a/detector/library.go +++ b/detector/library.go @@ -5,12 +5,20 @@ package detector import ( "context" + "errors" + "fmt" + "strings" + "github.com/aquasecurity/go-dep-parser/pkg/java/jar" trivydb "github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/metadata" + trivydbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy/pkg/db" + "github.com/aquasecurity/trivy/pkg/detector/library" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" + "github.com/samber/lo" "golang.org/x/xerrors" "github.com/future-architect/vuls/config" @@ -19,6 +27,11 @@ import ( "github.com/future-architect/vuls/models" ) +type libraryDetector struct { + scanner models.LibraryScanner + javaDBClient *javadb.DBClient +} + // DetectLibsCves fills LibraryScanner information func DetectLibsCves(r *models.ScanResult, trivyOpts config.TrivyOpts, logOpts logging.LogOpts, noProgress bool) (err error) { totalCnt := 0 @@ -44,6 +57,7 @@ func DetectLibsCves(r *models.ScanResult, trivyOpts config.TrivyOpts, logOpts lo var javaDBClient *javadb.DBClient defer javaDBClient.Close() for _, lib := range r.LibraryScanners { + d := libraryDetector{scanner: lib} if lib.Type == ftypes.Jar { if javaDBClient == nil { if err := javadb.UpdateJavaDB(trivyOpts, noProgress); err != nil { @@ -55,10 +69,10 @@ func DetectLibsCves(r *models.ScanResult, trivyOpts config.TrivyOpts, logOpts lo return xerrors.Errorf("Failed to open Trivy Java DB. err: %w", err) } } - lib.JavaDBClient = javaDBClient + d.javaDBClient = javaDBClient } - vinfos, err := lib.Scan() + vinfos, err := d.scan() if err != nil { return xerrors.Errorf("Failed to scan library. err: %w", err) } @@ -113,3 +127,121 @@ func showDBInfo(cacheDir string) error { meta.Version, meta.UpdatedAt, meta.NextUpdate, meta.DownloadedAt) return nil } + +// Scan : scan target library +func (d libraryDetector) scan() ([]models.VulnInfo, error) { + if d.scanner.Type == ftypes.Jar { + if err := d.improveJARInfo(); err != nil { + return nil, xerrors.Errorf("Failed to improve JAR information by trivy Java DB. err: %w", err) + } + } + scanner, ok := library.NewDriver(d.scanner.Type) + if !ok { + return nil, xerrors.Errorf("Failed to new a library driver for %s", d.scanner.Type) + } + var vulnerabilities = []models.VulnInfo{} + for _, pkg := range d.scanner.Libs { + tvulns, err := scanner.DetectVulnerabilities("", pkg.Name, pkg.Version) + if err != nil { + return nil, xerrors.Errorf("Failed to detect %s vulnerabilities. err: %w", scanner.Type(), err) + } + if len(tvulns) == 0 { + continue + } + + vulns := d.convertFanalToVuln(tvulns) + vulnerabilities = append(vulnerabilities, vulns...) + } + + return vulnerabilities, nil +} + +func (d *libraryDetector) improveJARInfo() error { + libs := make([]models.Library, 0, len(d.scanner.Libs)) + for _, l := range d.scanner.Libs { + if l.Digest == "" { + // This is the case from pom.properties, it should be respected as is. + libs = append(libs, l) + continue + } + + algorithm, sha1, found := strings.Cut(l.Digest, ":") + if !found || algorithm != "sha1" { + logging.Log.Debugf("No SHA1 hash found for %s in the digest: %q", l.FilePath, l.Digest) + libs = append(libs, l) + continue + } + + foundProps, err := d.javaDBClient.SearchBySHA1(sha1) + if err != nil { + if !errors.Is(err, jar.ArtifactNotFoundErr) { + return xerrors.Errorf("Failed to search trivy Java DB. err: %w", err) + } + + logging.Log.Debugf("No record in Java DB for %s by SHA1: %s", l.FilePath, sha1) + libs = append(libs, l) + continue + } + + foundLib := foundProps.Library() + l.Name = foundLib.Name + l.Version = foundLib.Version + libs = append(libs, l) + } + + d.scanner.Libs = lo.UniqBy(libs, func(lib models.Library) string { + return fmt.Sprintf("%s::%s::%s", lib.Name, lib.Version, lib.FilePath) + }) + return nil +} + +func (d libraryDetector) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []models.VulnInfo) { + for _, tvuln := range tvulns { + vinfo, err := d.getVulnDetail(tvuln) + if err != nil { + logging.Log.Debugf("failed to getVulnDetail. err: %+v, tvuln: %#v", err, tvuln) + continue + } + vulns = append(vulns, vinfo) + } + return vulns +} + +func (d libraryDetector) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo models.VulnInfo, err error) { + vul, err := trivydb.Config{}.GetVulnerability(tvuln.VulnerabilityID) + if err != nil { + return vinfo, err + } + + vinfo.CveID = tvuln.VulnerabilityID + vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul) + vinfo.LibraryFixedIns = []models.LibraryFixedIn{ + { + Key: d.scanner.GetLibraryKey(), + Name: tvuln.PkgName, + FixedIn: tvuln.FixedVersion, + Path: d.scanner.LockfilePath, + }, + } + return vinfo, nil +} + +func getCveContents(cveID string, vul trivydbTypes.Vulnerability) (contents map[models.CveContentType][]models.CveContent) { + contents = map[models.CveContentType][]models.CveContent{} + refs := []models.Reference{} + for _, refURL := range vul.References { + refs = append(refs, models.Reference{Source: "trivy", Link: refURL}) + } + + contents[models.Trivy] = []models.CveContent{ + { + Type: models.Trivy, + CveID: cveID, + Title: vul.Title, + Summary: vul.Description, + Cvss3Severity: string(vul.Severity), + References: refs, + }, + } + return contents +} diff --git a/models/library.go b/models/library.go index 850dfc81..02e332a2 100644 --- a/models/library.go +++ b/models/library.go @@ -1,21 +1,7 @@ package models import ( - "errors" - "fmt" - "strings" - - "github.com/aquasecurity/go-dep-parser/pkg/java/jar" - "github.com/aquasecurity/trivy-db/pkg/db" - trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types" - "github.com/aquasecurity/trivy/pkg/detector/library" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/types" - "github.com/samber/lo" - "golang.org/x/xerrors" - - "github.com/future-architect/vuls/detector/javadb" - "github.com/future-architect/vuls/logging" ) // LibraryScanners is an array of LibraryScanner @@ -50,8 +36,6 @@ type LibraryScanner struct { // The path to the Lockfile is stored. LockfilePath string `json:"path,omitempty"` - - JavaDBClient *javadb.DBClient `json:"-"` } // Library holds the attribute of a package library @@ -65,124 +49,6 @@ type Library struct { Digest string } -// Scan : scan target library -func (s LibraryScanner) Scan() ([]VulnInfo, error) { - if s.Type == ftypes.Jar { - if err := s.improveJARInfo(); err != nil { - return nil, xerrors.Errorf("Failed to improve JAR information by trivy Java DB. err: %w", err) - } - } - scanner, ok := library.NewDriver(s.Type) - if !ok { - return nil, xerrors.Errorf("Failed to new a library driver for %s", s.Type) - } - var vulnerabilities = []VulnInfo{} - for _, pkg := range s.Libs { - tvulns, err := scanner.DetectVulnerabilities("", pkg.Name, pkg.Version) - if err != nil { - return nil, xerrors.Errorf("Failed to detect %s vulnerabilities. err: %w", scanner.Type(), err) - } - if len(tvulns) == 0 { - continue - } - - vulns := s.convertFanalToVuln(tvulns) - vulnerabilities = append(vulnerabilities, vulns...) - } - - return vulnerabilities, nil -} - -func (s *LibraryScanner) improveJARInfo() error { - libs := make([]Library, 0, len(s.Libs)) - for _, l := range s.Libs { - if l.Digest == "" { - // This is the case from pom.properties, it should be respected as is. - libs = append(libs, l) - continue - } - - algorithm, sha1, found := strings.Cut(l.Digest, ":") - if !found || algorithm != "sha1" { - logging.Log.Debugf("No SHA1 hash found for %s in the digest: %q", l.FilePath, l.Digest) - libs = append(libs, l) - continue - } - - foundProps, err := s.JavaDBClient.SearchBySHA1(sha1) - if err != nil { - if !errors.Is(err, jar.ArtifactNotFoundErr) { - return xerrors.Errorf("Failed to search trivy Java DB. err: %w", err) - } - - logging.Log.Debugf("No record in Java DB for %s by SHA1: %s", l.FilePath, sha1) - libs = append(libs, l) - continue - } - - foundLib := foundProps.Library() - l.Name = foundLib.Name - l.Version = foundLib.Version - libs = append(libs, l) - } - - s.Libs = lo.UniqBy(libs, func(lib Library) string { - return fmt.Sprintf("%s::%s::%s", lib.Name, lib.Version, lib.FilePath) - }) - return nil -} - -func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []VulnInfo) { - for _, tvuln := range tvulns { - vinfo, err := s.getVulnDetail(tvuln) - if err != nil { - logging.Log.Debugf("failed to getVulnDetail. err: %+v, tvuln: %#v", err, tvuln) - continue - } - vulns = append(vulns, vinfo) - } - return vulns -} - -func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo VulnInfo, err error) { - vul, err := db.Config{}.GetVulnerability(tvuln.VulnerabilityID) - if err != nil { - return vinfo, err - } - - vinfo.CveID = tvuln.VulnerabilityID - vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul) - vinfo.LibraryFixedIns = []LibraryFixedIn{ - { - Key: s.GetLibraryKey(), - Name: tvuln.PkgName, - FixedIn: tvuln.FixedVersion, - Path: s.LockfilePath, - }, - } - return vinfo, nil -} - -func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType][]CveContent) { - contents = map[CveContentType][]CveContent{} - refs := []Reference{} - for _, refURL := range vul.References { - refs = append(refs, Reference{Source: "trivy", Link: refURL}) - } - - contents[Trivy] = []CveContent{ - { - Type: Trivy, - CveID: cveID, - Title: vul.Title, - Summary: vul.Description, - Cvss3Severity: string(vul.Severity), - References: refs, - }, - } - return contents -} - // FindLockFiles is a list of filenames that is the target of findLock var FindLockFiles = []string{ // dart/pub