package models import ( "path/filepath" "github.com/aquasecurity/trivy/pkg/scanner/library" "github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability" "github.com/future-architect/vuls/util" "golang.org/x/xerrors" "github.com/aquasecurity/go-dep-parser/pkg/types" "github.com/knqyf263/go-version" ) // LibraryScanner has libraries information type LibraryScanner struct { Path string Libs []types.Library } // Scan : scan target library func (s LibraryScanner) Scan() ([]VulnInfo, error) { scanner := library.NewScanner(filepath.Base(string(s.Path))) if scanner == nil { return nil, xerrors.New("unknown file type") } util.Log.Info("Updating library db...") err := scanner.UpdateDB() if err != nil { return nil, xerrors.Errorf("failed to update %s advisories: %w", scanner.Type(), err) } var vulnerabilities []VulnInfo for _, pkg := range s.Libs { v, err := version.NewVersion(pkg.Version) if err != nil { util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version) continue } tvulns, err := scanner.Detect(pkg.Name, v) if err != nil { return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err) } vulns := s.convertFanalToVuln(tvulns) vulnerabilities = append(vulnerabilities, vulns...) } return vulnerabilities, nil } func (s LibraryScanner) convertFanalToVuln(tvulns []vulnerability.DetectedVulnerability) (vulns []VulnInfo) { for _, tvuln := range tvulns { vinfo, _ := s.getVulnDetail(tvuln) vulns = append(vulns, vinfo) } return vulns } func (s LibraryScanner) getVulnDetail(tvuln vulnerability.DetectedVulnerability) (vinfo VulnInfo, err error) { details, err := vulnerability.Get(tvuln.VulnerabilityID) if err != nil { return vinfo, err } else if len(details) == 0 { return vinfo, xerrors.Errorf("Unknown vulnID : %s", tvuln.VulnerabilityID) } vinfo.CveID = tvuln.VulnerabilityID vinfo.CveContents = getCveContents(details) if tvuln.FixedVersion != "" { vinfo.LibraryFixedIns = []LibraryFixedIn{ { Key: s.GetLibraryKey(), Name: tvuln.PkgName, FixedIn: tvuln.FixedVersion, }, } } return vinfo, nil } func getCveContents(details map[string]vulnerability.Vulnerability) (contents map[CveContentType]CveContent) { contents = map[CveContentType]CveContent{} for source, detail := range details { refs := []Reference{} for _, refURL := range detail.References { refs = append(refs, Reference{Source: refURL, Link: refURL}) } content := CveContent{ Type: NewCveContentType(source), CveID: detail.ID, Title: detail.Title, Summary: detail.Description, Cvss3Score: detail.CvssScoreV3, Cvss3Severity: string(detail.SeverityV3), Cvss2Score: detail.CvssScore, Cvss2Severity: string(detail.Severity), References: refs, //SourceLink string `json:"sourceLink"` //Cvss2Vector string `json:"cvss2Vector"` //Cvss3Vector string `json:"cvss3Vector"` //Cvss3Severity string `json:"cvss3Severity"` //Cpes []Cpe `json:"cpes,omitempty"` //CweIDs []string `json:"cweIDs,omitempty"` //Published time.Time `json:"published"` //LastModified time.Time `json:"lastModified"` //Mitigation string `json:"mitigation"` // RedHat API //Optional map[string]string `json:"optional,omitempty"` } contents[NewCveContentType(source)] = content } return contents } // LibraryMap is filename and library type var LibraryMap = map[string]string{ "package-lock.json": "node", "yarn.lock": "node", "Gemfile.lock": "ruby", "Cargo.lock": "rust", "composer.lock": "php", "Pipfile.lock": "python", "poetry.lock": "python", } // GetLibraryKey returns target library key func (s LibraryScanner) GetLibraryKey() string { fileName := filepath.Base(s.Path) return LibraryMap[fileName] } // LibraryFixedIn has library fixed information type LibraryFixedIn struct { Key string `json:"key,omitempty"` Name string `json:"name,omitempty"` FixedIn string `json:"fixedIn,omitempty"` }