From d845878cdf6e06d0eb9dcdf2bad702f99e4b9cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dugovi=C4=8D?= Date: Sun, 29 Sep 2024 13:34:06 +0200 Subject: [PATCH 1/5] feat: rewrite basic cves endpoint in go RHINENG-13545 --- vmaas/cves.go | 130 +++++++++++++++++++++++++++++++++++++++++++++ vmaas/load.go | 22 +++++--- vmaas/types.go | 46 ++++++++++------ vmaas/utils/rpm.go | 15 +++--- vmaas/vmaas.go | 4 ++ 5 files changed, 188 insertions(+), 29 deletions(-) create mode 100644 vmaas/cves.go diff --git a/vmaas/cves.go b/vmaas/cves.go new file mode 100644 index 0000000..99c546c --- /dev/null +++ b/vmaas/cves.go @@ -0,0 +1,130 @@ +package vmaas + +import ( + "slices" + + "github.com/pkg/errors" + "github.com/redhatinsights/vmaas-lib/vmaas/utils" +) + +type CveDetails map[string]CveDetail + +type Cves struct { + Cves CveDetails `json:"cve_list"` + LastChange string `json:"last_change"` +} + +func (c *Cache) pkgDetail2Nevra(pkgDetail PackageDetail) string { + evr := c.ID2Evr[pkgDetail.EvrID] + nevra := utils.Nevra{ + Name: c.ID2Packagename[pkgDetail.NameID], + Epoch: evr.Epoch, + Version: evr.Version, + Release: evr.Release, + Arch: c.ID2Arch[pkgDetail.ArchID], + } + return nevra.String() +} + +func (c *Cache) errataIDs2Names(eids []int) []string { + names := make([]string, 0, len(eids)) + for _, eid := range eids { + names = append(names, c.ErratumID2Name[ErratumID(eid)]) + } + return names +} + +func (c *Cache) packageIDs2Nevras(pkgIDs []int) ([]string, []string) { + binPackages := make([]string, 0, len(pkgIDs)) + sourcePackages := make([]string, 0, len(pkgIDs)) + sourceArchID := c.Arch2ID["src"] + for _, pkgID := range pkgIDs { + pkgDetail := c.PackageDetails[PkgID(pkgID)] + nevra := c.pkgDetail2Nevra(pkgDetail) + if nevra == "" { + continue + } + if pkgDetail.ArchID == sourceArchID { + sourcePackages = append(sourcePackages, nevra) + } else { + binPackages = append(binPackages, nevra) + } + } + return binPackages, sourcePackages +} + +func (req *CvesRequest) getSortedCves(c *Cache) ([]string, error) { + cves := req.Cves + if len(cves) == 0 { + return nil, errors.New("cve_list must contain at least one item") + } + // TODO: implement expanding by regex + slices.Sort(cves) + return cves, nil +} + +func filterInputCves(c *Cache, cves []string, req *CvesRequest) []string { + filteredIDs := make([]string, 0, len(cves)) + for _, cve := range cves { + if cve == "" { + continue + } + cveDetail, found := c.CveDetail[cve] + if !found { + continue + } + if req.RHOnly && cveDetail.Source != "Red Hat" { + continue + } + if req.AreErrataAssociated && len(cveDetail.ErrataIDs) == 0 { + // FIXME: also check CSAF + continue + } + + if req.ModifiedSince != nil && cveDetail.ModifiedDate != nil { + if cveDetail.ModifiedDate.Before(*req.ModifiedSince) { + continue + } + } + if req.PublishedSince != nil && cveDetail.PublishedDate != nil { + if cveDetail.PublishedDate.Before(*req.PublishedSince) { + continue + } + } + + filteredIDs = append(filteredIDs, cve) + } + return filteredIDs +} + +func (c *Cache) loadCveDetails(cves []string) CveDetails { + cveDetails := make(CveDetails, len(cves)) + for _, cve := range cves { + cveDetail := c.CveDetail[cve] + cveDetail.Name = cve + cveDetail.Errata = c.errataIDs2Names(cveDetail.ErrataIDs) + binPackages, sourcePackages := c.packageIDs2Nevras(cveDetail.PkgIDs) + cveDetail.Packages = binPackages + cveDetail.SourcePackages = sourcePackages + cveDetails[cve] = cveDetail + } + return cveDetails +} + +func (req *CvesRequest) cves(c *Cache) (*Cves, error) { // TODO: implement opts + cves, err := req.getSortedCves(c) + if err != nil { + return nil, err + } + + cves = filterInputCves(c, cves, req) + // TODO: add pagination + + // TODO: write tests for everything + + res := Cves{ + Cves: c.loadCveDetails(cves), + LastChange: c.DBChange.LastChange, + } + return &res, nil +} diff --git a/vmaas/load.go b/vmaas/load.go index be97dcd..3d60833 100644 --- a/vmaas/load.go +++ b/vmaas/load.go @@ -505,6 +505,7 @@ func loadErrata(c *Cache) { c.ErratumID2Name = erratumID2Name } +//nolint:lll func loadCves(c *Cache) { defer utils.TimeTrack(time.Now(), "CveDetail") @@ -512,21 +513,28 @@ func loadCves(c *Cache) { cveID2pkg := loadInt2Ints("cve_pkg", "cve_id,pkg_id", "cveID2pkg") cve2eid := loadString2Ints("errata_cve", "cve,errata_id", "cve2eid") - rows := getAllRows("cve_detail", "*") + rows := getAllRows("cve_detail", "id, name, COALESCE(redhat_url, ''), COALESCE(secondary_url, ''), COALESCE(cvss3_score, ''), COALESCE(cvss3_metrics, ''), impact, COALESCE(published_date, ''), COALESCE(modified_date, ''), COALESCE(iava, ''), description, COALESCE(cvss2_score, ''), COALESCE(cvss2_metrics, ''), source") cnt := getCount("cve_detail", "*") cveDetails := make(map[string]CveDetail, cnt) cveNames := make(map[int]string, cnt) var cveID int - var cveName string + var publishedDateStr, modifiedDateStr string for rows.Next() { var det CveDetail - err := rows.Scan(&cveID, &cveName, &det.RedHatURL, &det.SecondaryURL, &det.Cvss3Score, &det.Cvss3Metrics, - &det.Impact, &det.PublishedDate, &det.ModifiedDate, &det.Iava, &det.Description, &det.Cvss2Score, + err := rows.Scan(&cveID, &det.Name, &det.RedHatURL, &det.SecondaryURL, &det.Cvss3Score, &det.Cvss3Metrics, + &det.Impact, &publishedDateStr, &modifiedDateStr, &det.Iava, &det.Description, &det.Cvss2Score, &det.Cvss2Metrics, &det.Source) if err != nil { panic(err) } + if publishedDate, err := time.Parse(time.RFC3339, publishedDateStr); err == nil { + det.PublishedDate = &publishedDate + } + if modifiedDate, err := time.Parse(time.RFC3339, modifiedDateStr); err == nil { + det.ModifiedDate = &modifiedDate + } + cwes, ok := cveID2cwes[cveID] sort.Strings(cwes) if ok { @@ -538,12 +546,12 @@ func loadCves(c *Cache) { det.PkgIDs = pkgs } - eids, ok := cve2eid[cveName] + eids, ok := cve2eid[det.Name] if ok { det.ErrataIDs = eids } - cveDetails[cveName] = det - cveNames[cveID] = cveName + cveDetails[det.Name] = det + cveNames[cveID] = det.Name } c.CveDetail = cveDetails c.CveNames = cveNames diff --git a/vmaas/types.go b/vmaas/types.go index 917969a..6f7de40 100644 --- a/vmaas/types.go +++ b/vmaas/types.go @@ -44,6 +44,15 @@ type Request struct { EpochRequired bool `json:"epoch_required"` } +type CvesRequest struct { + Cves []string `json:"cve_list"` + PublishedSince *time.Time `json:"published_since"` + ModifiedSince *time.Time `json:"modified_since"` + RHOnly bool `json:"rh_only"` + AreErrataAssociated bool `json:"errata_associated"` + ThirdParty bool `json:"third_party"` +} + type Update struct { Package string `json:"package"` PackageName string `json:"package_name"` @@ -167,22 +176,27 @@ type RepoDetail struct { } type CveDetail struct { - RedHatURL *string - SecondaryURL *string - Cvss3Score *string - Cvss3Metrics *string - Impact string - PublishedDate *string - ModifiedDate *string - Iava *string - Description string - Cvss2Score *string - Cvss2Metrics *string - Source string - - CWEs []string - PkgIDs []int - ErrataIDs []int + Name string `json:"synopsis"` + RedHatURL string `json:"redhat_url"` + SecondaryURL string `json:"secondary_url"` + Cvss3Score string `json:"cvss3_score"` + Cvss3Metrics string `json:"cvss3_metrics"` + Impact string `json:"impact"` + PublishedDate *time.Time `json:"public_date"` + ModifiedDate *time.Time `json:"modified_date"` + Iava string `json:"-"` + Description string `json:"description"` + Cvss2Score string `json:"cvss2_score"` + Cvss2Metrics string `json:"cvss2_metrics"` + Source string `json:"-"` + + CWEs []string `json:"cwe_list"` + PkgIDs []int `json:"-"` + ErrataIDs []int `json:"-"` + + Errata []string `json:"errata_list"` + Packages []string `json:"package_list"` + SourcePackages []string `json:"source_package_list"` } type PkgErratum struct { diff --git a/vmaas/utils/rpm.go b/vmaas/utils/rpm.go index 2077841..1b45bd5 100644 --- a/vmaas/utils/rpm.go +++ b/vmaas/utils/rpm.go @@ -78,10 +78,10 @@ func ParseNameEVRA(name, evra string, epochRequired bool) (Nevra, error) { } func (n *Nevra) StringE(showEpoch bool) string { - if n.Epoch != 0 || showEpoch { - return fmt.Sprintf("%s-%d:%s-%s.%s", n.Name, n.Epoch, n.Version, n.Release, n.Arch) + if evra := n.EVRAStringE(showEpoch); n.Name != "" && evra != "" { + return fmt.Sprintf("%s-%s", n.Name, evra) } - return fmt.Sprintf("%s-%s-%s.%s", n.Name, n.Version, n.Release, n.Arch) + return "" } func (n *Nevra) String() string { @@ -89,6 +89,9 @@ func (n *Nevra) String() string { } func (n *Nevra) EVRStringE(showEpoch bool) string { + if n.Epoch == 0 && n.Version == "" && n.Release == "" { + return "" + } if n.Epoch != 0 || showEpoch { return fmt.Sprintf("%d:%s-%s", n.Epoch, n.Version, n.Release) } @@ -100,10 +103,10 @@ func (n *Nevra) EVRString() string { } func (n *Nevra) EVRAStringE(showEpoch bool) string { - if n.Epoch != 0 || showEpoch { - return fmt.Sprintf("%d:%s-%s.%s", n.Epoch, n.Version, n.Release, n.Arch) + if evr := n.EVRStringE(showEpoch); evr != "" { + return fmt.Sprintf("%s.%s", evr, n.Arch) } - return fmt.Sprintf("%s-%s.%s", n.Version, n.Release, n.Arch) + return "" } func (n *Nevra) EVRAString() string { diff --git a/vmaas/vmaas.go b/vmaas/vmaas.go index 2c740ea..91e864e 100644 --- a/vmaas/vmaas.go +++ b/vmaas/vmaas.go @@ -54,6 +54,10 @@ func (api *API) VulnerabilitiesExtended(request *Request) (*VulnerabilitiesExten return request.vulnerabilitiesExtended(api.Cache, api.options) } +func (api *API) Cves(request *CvesRequest) (*Cves, error) { + return request.cves(api.Cache) +} + func (api *API) LoadCacheFromFile(cachePath string) error { var err error api.Cache, err = loadCache(cachePath, api.options) From b3316c40e187aaecb2d1c400b11a9c6ca617bb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dugovi=C4=8D?= Date: Thu, 17 Oct 2024 14:32:14 +0200 Subject: [PATCH 2/5] feat: implement expanding cves by regex RHINENG-13545 --- vmaas/cves.go | 2 +- vmaas/utils/expand_regex.go | 36 ++++++++++++++++++++++++++++++++ vmaas/utils/expand_regex_test.go | 33 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 vmaas/utils/expand_regex.go create mode 100644 vmaas/utils/expand_regex_test.go diff --git a/vmaas/cves.go b/vmaas/cves.go index 99c546c..0e25771 100644 --- a/vmaas/cves.go +++ b/vmaas/cves.go @@ -58,7 +58,7 @@ func (req *CvesRequest) getSortedCves(c *Cache) ([]string, error) { if len(cves) == 0 { return nil, errors.New("cve_list must contain at least one item") } - // TODO: implement expanding by regex + cves = utils.TryExpandRegexPattern(cves, c.CveDetail) slices.Sort(cves) return cves, nil } diff --git a/vmaas/utils/expand_regex.go b/vmaas/utils/expand_regex.go new file mode 100644 index 0000000..3e30e22 --- /dev/null +++ b/vmaas/utils/expand_regex.go @@ -0,0 +1,36 @@ +package utils + +import ( + "regexp" + "strings" +) + +// TryExpandRegexPattern treats the item in a single-label slice like a regex pattern +// and returns all matching labels from dataByLabels, otherwise it returns inLabels. +func TryExpandRegexPattern[T any](inLabels []string, dataByLabels map[string]T) []string { + if len(inLabels) != 1 { + return inLabels + } + + pattern := inLabels[0] + if !strings.HasPrefix(pattern, "^") { + pattern = "^" + pattern + } + if !strings.HasSuffix(pattern, "$") { + pattern += "$" + } + + re, err := regexp.Compile(pattern) + if err != nil { + return inLabels + } + + outLabels := make([]string, 0, len(dataByLabels)) + for label := range dataByLabels { + matched := re.Match([]byte(label)) + if matched { + outLabels = append(outLabels, label) + } + } + return outLabels +} diff --git a/vmaas/utils/expand_regex_test.go b/vmaas/utils/expand_regex_test.go new file mode 100644 index 0000000..1c79f66 --- /dev/null +++ b/vmaas/utils/expand_regex_test.go @@ -0,0 +1,33 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTryExpandRegexPattern(t *testing.T) { + regexLabel := []string{`CVE-2024-1\d+`} + inLabels := []string{"CVE-2024-1234", "CVE-2024-21345"} + labelDetails := map[string]int{ + "CVE-2024-1234": 0, + "CVE-2024-12345": 0, + "CVE-2024-21345": 0, + } + + // empty slice + outLabels := TryExpandRegexPattern([]string{}, labelDetails) + assert.Equal(t, 0, len(outLabels)) + + // with a single lable that is not a regex pattern + outLabels = TryExpandRegexPattern(inLabels[0:1], labelDetails) + assert.Equal(t, inLabels[0], outLabels[0]) + + // more labels in inLabels + outLabels = TryExpandRegexPattern(inLabels, labelDetails) + assert.Equal(t, len(inLabels), len(outLabels)) + + // with regex + outLabels = TryExpandRegexPattern(regexLabel, labelDetails) + assert.Equal(t, 2, len(outLabels)) +} From 21fcc64e6d5838ef76d6ab7fe83ff6db0fd641b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dugovi=C4=8D?= Date: Thu, 17 Oct 2024 14:37:14 +0200 Subject: [PATCH 3/5] feat: implement cves pagination RHINENG-13545 --- vmaas/cves.go | 8 ++++--- vmaas/types.go | 2 ++ vmaas/utils/paginate.go | 46 ++++++++++++++++++++++++++++++++++++ vmaas/utils/paginate_test.go | 37 +++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 vmaas/utils/paginate.go create mode 100644 vmaas/utils/paginate_test.go diff --git a/vmaas/cves.go b/vmaas/cves.go index 0e25771..950d526 100644 --- a/vmaas/cves.go +++ b/vmaas/cves.go @@ -12,6 +12,7 @@ type CveDetails map[string]CveDetail type Cves struct { Cves CveDetails `json:"cve_list"` LastChange string `json:"last_change"` + utils.PaginationDetails } func (c *Cache) pkgDetail2Nevra(pkgDetail PackageDetail) string { @@ -118,13 +119,14 @@ func (req *CvesRequest) cves(c *Cache) (*Cves, error) { // TODO: implement opts } cves = filterInputCves(c, cves, req) - // TODO: add pagination + cves, paginationDetails := utils.Paginate(cves, req.PageNumber, req.PageSize) // TODO: write tests for everything res := Cves{ - Cves: c.loadCveDetails(cves), - LastChange: c.DBChange.LastChange, + Cves: c.loadCveDetails(cves), + LastChange: c.DBChange.LastChange, + PaginationDetails: paginationDetails, } return &res, nil } diff --git a/vmaas/types.go b/vmaas/types.go index 6f7de40..37ba09f 100644 --- a/vmaas/types.go +++ b/vmaas/types.go @@ -51,6 +51,8 @@ type CvesRequest struct { RHOnly bool `json:"rh_only"` AreErrataAssociated bool `json:"errata_associated"` ThirdParty bool `json:"third_party"` + PageNumber int `json:"page"` + PageSize int `json:"page_size"` } type Update struct { diff --git a/vmaas/utils/paginate.go b/vmaas/utils/paginate.go new file mode 100644 index 0000000..39f8e70 --- /dev/null +++ b/vmaas/utils/paginate.go @@ -0,0 +1,46 @@ +package utils + +import ( + "math" +) + +const ( + DefaultPageNumber = 1 + DefaultPageSize = 5000 +) + +type PaginationDetails struct { + PageNumber int `json:"page"` + PageSize int `json:"page_size"` + TotalPages int `json:"pages"` +} + +// Paginate returns pageSize-long sub-slice of items corresponding to the pageNumber. +// For the last page, there may be fewer than pageSize items. +func Paginate[T any](slice []T, pageNumber, pageSize int) ([]T, PaginationDetails) { + if pageNumber <= 0 { + pageNumber = DefaultPageNumber + } + if pageSize <= 0 { + pageSize = DefaultPageSize + } + + start := (pageNumber - 1) * pageSize + if start > len(slice) { + start = len(slice) + } + end := pageNumber * pageSize + if end > len(slice) { + end = len(slice) + } + subslice := slice[start:end] + + totalPages := int(math.Ceil(float64(len(slice))/float64(pageSize) + 1e-6)) + + paginationDetails := PaginationDetails{ + PageNumber: pageNumber, + PageSize: len(subslice), + TotalPages: totalPages, + } + return subslice, paginationDetails +} diff --git a/vmaas/utils/paginate_test.go b/vmaas/utils/paginate_test.go new file mode 100644 index 0000000..81015a9 --- /dev/null +++ b/vmaas/utils/paginate_test.go @@ -0,0 +1,37 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPaginate(t *testing.T) { + slice := []int{42, 43, 44, 45, 46} + + // empty slice + subslice, paginationDetails := Paginate([]int{}, 1, 2) + assert.Equal(t, 0, len(subslice)) + assert.Equal(t, 1, paginationDetails.PageNumber) + assert.Equal(t, 0, paginationDetails.PageSize) + assert.Equal(t, 1, paginationDetails.TotalPages) + + // use default values of pageNumber and pageSize + subslice, paginationDetails = Paginate(slice, 0, -1) + assert.Equal(t, len(slice), len(subslice)) + assert.Equal(t, 1, paginationDetails.PageNumber) + assert.LessOrEqual(t, paginationDetails.PageSize, 5000) + + // usual case + subslice, paginationDetails = Paginate(slice, 2, 2) + assert.Equal(t, 2, len(subslice)) + assert.Equal(t, 44, subslice[0]) + assert.Equal(t, 45, subslice[1]) + assert.Equal(t, 2, paginationDetails.PageNumber) + assert.Equal(t, 2, paginationDetails.PageSize) + assert.Equal(t, 3, paginationDetails.TotalPages) + + // the last page + subslice, paginationDetails = Paginate(slice, 2, 3) + assert.LessOrEqual(t, len(subslice), 3) +} From 93fe1ead8ac867f900d44a6833f6ad220532742f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dugovi=C4=8D?= Date: Thu, 17 Oct 2024 14:38:16 +0200 Subject: [PATCH 4/5] test: add tests for cves endpoint RHINENG-13545 --- vmaas/cves.go | 2 - vmaas/cves_test.go | 158 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 vmaas/cves_test.go diff --git a/vmaas/cves.go b/vmaas/cves.go index 950d526..e55dc5b 100644 --- a/vmaas/cves.go +++ b/vmaas/cves.go @@ -121,8 +121,6 @@ func (req *CvesRequest) cves(c *Cache) (*Cves, error) { // TODO: implement opts cves = filterInputCves(c, cves, req) cves, paginationDetails := utils.Paginate(cves, req.PageNumber, req.PageSize) - // TODO: write tests for everything - res := Cves{ Cves: c.loadCveDetails(cves), LastChange: c.DBChange.LastChange, diff --git a/vmaas/cves_test.go b/vmaas/cves_test.go new file mode 100644 index 0000000..992baf2 --- /dev/null +++ b/vmaas/cves_test.go @@ -0,0 +1,158 @@ +package vmaas + +import ( + "testing" + "time" + + "github.com/redhatinsights/vmaas-lib/vmaas/utils" + "github.com/stretchr/testify/assert" +) + +func TestPkgDetail2Nevra(t *testing.T) { + c := mockCache() + pkgDetail := c.PackageDetails[PkgID(1)] + nevra := c.pkgDetail2Nevra(pkgDetail) + assert.Equal(t, "kernel-1:1-1.x86_64", nevra) +} + +func TestErrataIDs2Names(t *testing.T) { + c := mockCache() + errataNames := c.errataIDs2Names([]int{1, 2}) + assert.Equal(t, 2, len(errataNames)) +} + +func TestPackageIDs2Nevras(t *testing.T) { + c := mockCache() + binPackages, sourcePackages := c.packageIDs2Nevras([]int{1, 3}) + assert.Equal(t, 1, len(binPackages)) + assert.Equal(t, 1, len(sourcePackages)) + assert.Equal(t, "kernel-1:1-1.x86_64", binPackages[0]) + assert.Equal(t, "kernel-devel-1:1-1.src", sourcePackages[0]) +} + +func TestGetSortedCves(t *testing.T) { + req := mockCvesRequest() + reqWithoutReq := &CvesRequest{} + c := mockCache() + + cves, err := req.getSortedCves(c) + assert.NoError(t, err) + assert.Equal(t, "CVE-2024-1111111", cves[0]) + assert.Equal(t, "CVE-2024-1234", cves[1]) + assert.Equal(t, "CVE-2024-21345", cves[2]) + + _, err = reqWithoutReq.getSortedCves(c) + assert.Error(t, err) +} + +func TestFilterInputCves(t *testing.T) { + cves := []string{"CVE-2024-1234", "CVE-2024-21345", ""} + c := mockCache() + + // usual case + filteredIDs := filterInputCves(c, cves, &CvesRequest{}) + assert.Equal(t, 2, len(filteredIDs)) + + // RHOnly + req := &CvesRequest{RHOnly: true} + filteredIDs = filterInputCves(c, cves, req) + assert.Equal(t, 1, len(filteredIDs)) + assert.Equal(t, "CVE-2024-21345", filteredIDs[0]) + + // With some errata associated only + req = &CvesRequest{AreErrataAssociated: true} + filteredIDs = filterInputCves(c, cves, req) + assert.Equal(t, 1, len(filteredIDs)) + assert.Equal(t, "CVE-2024-1234", filteredIDs[0]) + + // With modified date before req.ModifiedSince + testTime, _ := time.Parse(time.RFC3339, "2024-10-03T15:01:01Z") + req = &CvesRequest{ModifiedSince: &testTime} + filteredIDs = filterInputCves(c, cves, req) + assert.Equal(t, 1, len(filteredIDs)) + assert.Equal(t, "CVE-2024-1234", filteredIDs[0]) + + // With published date before req.PublishedSince + req = &CvesRequest{PublishedSince: &testTime} + filteredIDs = filterInputCves(c, cves, req) + assert.Equal(t, 1, len(filteredIDs)) + assert.Equal(t, "CVE-2024-1234", filteredIDs[0]) +} + +func TestLoadCveDetails(t *testing.T) { + c := mockCache() + cve := "CVE-2024-1111111" + cvePropertiesMap := c.loadCveDetails([]string{cve}) + assert.Equal(t, 1, len(cvePropertiesMap)) + assert.Equal(t, cve, cvePropertiesMap[cve].Name) +} + +func TestCves(t *testing.T) { + req := &CvesRequest{} + c := mockCache() + + // empty cve list + _, err := req.cves(c) + assert.Error(t, err) +} + +func mockCache() *Cache { + modifiedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") + publishedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") + return &Cache{ + ID2Packagename: map[NameID]string{1: "kernel", 2: "kernel-devel"}, + + ID2Evr: map[EvrID]utils.Evr{ + 1: {Epoch: 1, Version: "1", Release: "1"}, + 2: {Epoch: 0, Version: "2", Release: "2"}, + }, + + Arch2ID: map[string]ArchID{ + "x86_64": 1, + "src": 2, + }, + ID2Arch: map[ArchID]string{ + 1: "x86_64", + 2: "src", + }, + + PackageDetails: map[PkgID]PackageDetail{ + 1: {NameID: 1, EvrID: 1, ArchID: 1}, // kernel-1:1-1 + 2: {NameID: 1, EvrID: 2, ArchID: 1}, // kernel-0:2-2 + 3: {NameID: 2, EvrID: 1, ArchID: 2}, // kernel-devel-1:1-1 + }, + + ErratumID2Name: map[ErratumID]string{ + 1: "RHSA-2024:0042", + 2: "RHSA-2024:1111", + }, + + CveDetail: map[string]CveDetail{ + "CVE-2024-21345": { + Source: "Red Hat", + ModifiedDate: &modifiedDate, + PublishedDate: &publishedDate, + }, + "CVE-2024-1234": { + ErrataIDs: []int{1, 2}, + }, + "CVE-2024-1111111": {}, + }, + + DBChange: DBChange{LastChange: "2024-10-02T16:08:00+02:00"}, + } +} + +func mockCvesRequest() *CvesRequest { + modifiedSince, _ := time.Parse(time.RFC3339, "2024-10-02T16:08:00+02:00") + publishedSince, _ := time.Parse(time.RFC3339, "2024-10-02T16:08:00+02:00") + return &CvesRequest{ + Cves: []string{"CVE-2024-21345", "CVE-2024-1234", "CVE-2024-1111111"}, + ModifiedSince: &modifiedSince, + PublishedSince: &publishedSince, + RHOnly: false, + AreErrataAssociated: false, + PageNumber: 1, + PageSize: 5000, + } +} From 4bab9d747f09a0b0f7c7f851dfddea7574bca42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Dugovi=C4=8D?= Date: Fri, 25 Oct 2024 19:44:00 +0200 Subject: [PATCH 5/5] fix: move cache utils and their tests --- vmaas/cache.go | 39 +++++++++++++++++++++++ vmaas/cache_test.go | 78 +++++++++++++++++++++++++++++++++++++++++++++ vmaas/cves.go | 39 ----------------------- vmaas/cves_test.go | 70 ---------------------------------------- 4 files changed, 117 insertions(+), 109 deletions(-) create mode 100644 vmaas/cache_test.go diff --git a/vmaas/cache.go b/vmaas/cache.go index f53caf3..9e061e1 100644 --- a/vmaas/cache.go +++ b/vmaas/cache.go @@ -106,3 +106,42 @@ func ShouldReload(c *Cache, latestDumpEndpoint string) bool { utils.LogDebug("latest", latest, "exported", exported, "Cache reload not needed") return false } + +func (c *Cache) errataIDs2Names(eids []int) []string { + names := make([]string, 0, len(eids)) + for _, eid := range eids { + names = append(names, c.ErratumID2Name[ErratumID(eid)]) + } + return names +} + +func (c *Cache) pkgDetail2Nevra(pkgDetail PackageDetail) string { + evr := c.ID2Evr[pkgDetail.EvrID] + nevra := utils.Nevra{ + Name: c.ID2Packagename[pkgDetail.NameID], + Epoch: evr.Epoch, + Version: evr.Version, + Release: evr.Release, + Arch: c.ID2Arch[pkgDetail.ArchID], + } + return nevra.String() +} + +func (c *Cache) packageIDs2Nevras(pkgIDs []int) ([]string, []string) { + binPackages := make([]string, 0, len(pkgIDs)) + sourcePackages := make([]string, 0, len(pkgIDs)) + sourceArchID := c.Arch2ID["src"] + for _, pkgID := range pkgIDs { + pkgDetail := c.PackageDetails[PkgID(pkgID)] + nevra := c.pkgDetail2Nevra(pkgDetail) + if nevra == "" { + continue + } + if pkgDetail.ArchID == sourceArchID { + sourcePackages = append(sourcePackages, nevra) + } else { + binPackages = append(binPackages, nevra) + } + } + return binPackages, sourcePackages +} diff --git a/vmaas/cache_test.go b/vmaas/cache_test.go new file mode 100644 index 0000000..abd5f07 --- /dev/null +++ b/vmaas/cache_test.go @@ -0,0 +1,78 @@ +package vmaas + +import ( + "testing" + "time" + + "github.com/redhatinsights/vmaas-lib/vmaas/utils" + "github.com/stretchr/testify/assert" +) + +func TestErrataIDs2Names(t *testing.T) { + c := mockCache() + errataNames := c.errataIDs2Names([]int{1, 2}) + assert.Equal(t, 2, len(errataNames)) +} + +func TestPkgDetail2Nevra(t *testing.T) { + c := mockCache() + pkgDetail := c.PackageDetails[PkgID(1)] + nevra := c.pkgDetail2Nevra(pkgDetail) + assert.Equal(t, "kernel-1:1-1.x86_64", nevra) +} + +func TestPackageIDs2Nevras(t *testing.T) { + c := mockCache() + binPackages, sourcePackages := c.packageIDs2Nevras([]int{1, 3}) + assert.Equal(t, 1, len(binPackages)) + assert.Equal(t, 1, len(sourcePackages)) + assert.Equal(t, "kernel-1:1-1.x86_64", binPackages[0]) + assert.Equal(t, "kernel-devel-1:1-1.src", sourcePackages[0]) +} + +func mockCache() *Cache { + modifiedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") + publishedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") + return &Cache{ + ID2Packagename: map[NameID]string{1: "kernel", 2: "kernel-devel"}, + + ID2Evr: map[EvrID]utils.Evr{ + 1: {Epoch: 1, Version: "1", Release: "1"}, + 2: {Epoch: 0, Version: "2", Release: "2"}, + }, + + Arch2ID: map[string]ArchID{ + "x86_64": 1, + "src": 2, + }, + ID2Arch: map[ArchID]string{ + 1: "x86_64", + 2: "src", + }, + + PackageDetails: map[PkgID]PackageDetail{ + 1: {NameID: 1, EvrID: 1, ArchID: 1}, // kernel-1:1-1 + 2: {NameID: 1, EvrID: 2, ArchID: 1}, // kernel-0:2-2 + 3: {NameID: 2, EvrID: 1, ArchID: 2}, // kernel-devel-1:1-1 + }, + + ErratumID2Name: map[ErratumID]string{ + 1: "RHSA-2024:0042", + 2: "RHSA-2024:1111", + }, + + CveDetail: map[string]CveDetail{ + "CVE-2024-21345": { + Source: "Red Hat", + ModifiedDate: &modifiedDate, + PublishedDate: &publishedDate, + }, + "CVE-2024-1234": { + ErrataIDs: []int{1, 2}, + }, + "CVE-2024-1111111": {}, + }, + + DBChange: DBChange{LastChange: "2024-10-02T16:08:00+02:00"}, + } +} diff --git a/vmaas/cves.go b/vmaas/cves.go index e55dc5b..3fdff05 100644 --- a/vmaas/cves.go +++ b/vmaas/cves.go @@ -15,45 +15,6 @@ type Cves struct { utils.PaginationDetails } -func (c *Cache) pkgDetail2Nevra(pkgDetail PackageDetail) string { - evr := c.ID2Evr[pkgDetail.EvrID] - nevra := utils.Nevra{ - Name: c.ID2Packagename[pkgDetail.NameID], - Epoch: evr.Epoch, - Version: evr.Version, - Release: evr.Release, - Arch: c.ID2Arch[pkgDetail.ArchID], - } - return nevra.String() -} - -func (c *Cache) errataIDs2Names(eids []int) []string { - names := make([]string, 0, len(eids)) - for _, eid := range eids { - names = append(names, c.ErratumID2Name[ErratumID(eid)]) - } - return names -} - -func (c *Cache) packageIDs2Nevras(pkgIDs []int) ([]string, []string) { - binPackages := make([]string, 0, len(pkgIDs)) - sourcePackages := make([]string, 0, len(pkgIDs)) - sourceArchID := c.Arch2ID["src"] - for _, pkgID := range pkgIDs { - pkgDetail := c.PackageDetails[PkgID(pkgID)] - nevra := c.pkgDetail2Nevra(pkgDetail) - if nevra == "" { - continue - } - if pkgDetail.ArchID == sourceArchID { - sourcePackages = append(sourcePackages, nevra) - } else { - binPackages = append(binPackages, nevra) - } - } - return binPackages, sourcePackages -} - func (req *CvesRequest) getSortedCves(c *Cache) ([]string, error) { cves := req.Cves if len(cves) == 0 { diff --git a/vmaas/cves_test.go b/vmaas/cves_test.go index 992baf2..34373e7 100644 --- a/vmaas/cves_test.go +++ b/vmaas/cves_test.go @@ -4,32 +4,9 @@ import ( "testing" "time" - "github.com/redhatinsights/vmaas-lib/vmaas/utils" "github.com/stretchr/testify/assert" ) -func TestPkgDetail2Nevra(t *testing.T) { - c := mockCache() - pkgDetail := c.PackageDetails[PkgID(1)] - nevra := c.pkgDetail2Nevra(pkgDetail) - assert.Equal(t, "kernel-1:1-1.x86_64", nevra) -} - -func TestErrataIDs2Names(t *testing.T) { - c := mockCache() - errataNames := c.errataIDs2Names([]int{1, 2}) - assert.Equal(t, 2, len(errataNames)) -} - -func TestPackageIDs2Nevras(t *testing.T) { - c := mockCache() - binPackages, sourcePackages := c.packageIDs2Nevras([]int{1, 3}) - assert.Equal(t, 1, len(binPackages)) - assert.Equal(t, 1, len(sourcePackages)) - assert.Equal(t, "kernel-1:1-1.x86_64", binPackages[0]) - assert.Equal(t, "kernel-devel-1:1-1.src", sourcePackages[0]) -} - func TestGetSortedCves(t *testing.T) { req := mockCvesRequest() reqWithoutReq := &CvesRequest{} @@ -96,53 +73,6 @@ func TestCves(t *testing.T) { assert.Error(t, err) } -func mockCache() *Cache { - modifiedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") - publishedDate, _ := time.Parse(time.RFC3339, "2024-10-03T11:44:00+02:00") - return &Cache{ - ID2Packagename: map[NameID]string{1: "kernel", 2: "kernel-devel"}, - - ID2Evr: map[EvrID]utils.Evr{ - 1: {Epoch: 1, Version: "1", Release: "1"}, - 2: {Epoch: 0, Version: "2", Release: "2"}, - }, - - Arch2ID: map[string]ArchID{ - "x86_64": 1, - "src": 2, - }, - ID2Arch: map[ArchID]string{ - 1: "x86_64", - 2: "src", - }, - - PackageDetails: map[PkgID]PackageDetail{ - 1: {NameID: 1, EvrID: 1, ArchID: 1}, // kernel-1:1-1 - 2: {NameID: 1, EvrID: 2, ArchID: 1}, // kernel-0:2-2 - 3: {NameID: 2, EvrID: 1, ArchID: 2}, // kernel-devel-1:1-1 - }, - - ErratumID2Name: map[ErratumID]string{ - 1: "RHSA-2024:0042", - 2: "RHSA-2024:1111", - }, - - CveDetail: map[string]CveDetail{ - "CVE-2024-21345": { - Source: "Red Hat", - ModifiedDate: &modifiedDate, - PublishedDate: &publishedDate, - }, - "CVE-2024-1234": { - ErrataIDs: []int{1, 2}, - }, - "CVE-2024-1111111": {}, - }, - - DBChange: DBChange{LastChange: "2024-10-02T16:08:00+02:00"}, - } -} - func mockCvesRequest() *CvesRequest { modifiedSince, _ := time.Parse(time.RFC3339, "2024-10-02T16:08:00+02:00") publishedSince, _ := time.Parse(time.RFC3339, "2024-10-02T16:08:00+02:00")