diff --git a/constant/constant.go b/constant/constant.go index 53d7a72d99..63f9d13b77 100644 --- a/constant/constant.go +++ b/constant/constant.go @@ -32,6 +32,9 @@ const ( // Oracle is Oracle = "oracle" + // EPEL is + EPEL = "epel" + // FreeBSD is FreeBSD = "freebsd" diff --git a/models/packages.go b/models/packages.go index 2967094134..7055ad08fd 100644 --- a/models/packages.go +++ b/models/packages.go @@ -80,6 +80,7 @@ type Package struct { NewVersion string `json:"newVersion"` NewRelease string `json:"newRelease"` Arch string `json:"arch"` + Vendor string `json:"vendor"` Repository string `json:"repository"` Changelog *Changelog `json:"changelog,omitempty"` AffectedProcs []AffectedProcess `json:",omitempty"` diff --git a/oval/util.go b/oval/util.go index d383a112f3..c03617b3dc 100644 --- a/oval/util.go +++ b/oval/util.go @@ -86,6 +86,8 @@ func (e *ovalResult) Sort() { } type request struct { + family string + release string packName string versionRelease string newVersionRelease string @@ -110,18 +112,54 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova defer close(resChan) defer close(errChan) + ovalFamily, err := GetFamilyInOval(r.Family) + if err != nil { + return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) + } + ovalRelease := r.Release + if r.Family == constant.CentOS { + ovalRelease = strings.TrimPrefix(r.Release, "stream") + } go func() { for _, pack := range r.Packages { - reqChan <- request{ + req := request{ + family: ovalFamily, + release: ovalRelease, packName: pack.Name, versionRelease: pack.FormatVer(), - newVersionRelease: pack.FormatVer(), - isSrcPack: false, + newVersionRelease: pack.FormatNewVer(), arch: pack.Arch, + isSrcPack: false, } + + switch ovalFamily { + case constant.RedHat, constant.Alma, constant.Rocky, constant.Oracle: + if pack.Vendor == "Fedora Project" { + switch util.Major(ovalRelease) { + case "7", "8": + req.family = constant.EPEL + default: + continue + } + } + case constant.Amazon: + if pack.Vendor == "Fedora Project" { + switch strings.Fields(ovalRelease)[0] { + case "2": + req.family = constant.EPEL + req.release = "7" + default: + continue + } + } + } + + reqChan <- req } for _, pack := range r.SrcPackages { reqChan <- request{ + family: ovalFamily, + release: ovalRelease, packName: pack.Name, binaryPackNames: pack.BinaryNames, versionRelease: pack.Version, @@ -131,14 +169,6 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova } }() - ovalFamily, err := GetFamilyInOval(r.Family) - if err != nil { - return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) - } - ovalRelease := r.Release - if r.Family == constant.CentOS { - ovalRelease = strings.TrimPrefix(r.Release, "stream") - } concurrency := 10 tasks := util.GenWorkers(concurrency) for i := 0; i < nReq; i++ { @@ -148,8 +178,8 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova url, err := util.URLPathJoin( url, "packs", - ovalFamily, - ovalRelease, + req.family, + req.release, req.packName, ) if err != nil { @@ -248,18 +278,55 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er } func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relatedDefs ovalResult, err error) { + ovalFamily, err := GetFamilyInOval(r.Family) + if err != nil { + return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) + } + ovalRelease := r.Release + if r.Family == constant.CentOS { + ovalRelease = strings.TrimPrefix(r.Release, "stream") + } + requests := []request{} for _, pack := range r.Packages { - requests = append(requests, request{ + req := request{ + family: ovalFamily, + release: ovalRelease, packName: pack.Name, versionRelease: pack.FormatVer(), newVersionRelease: pack.FormatNewVer(), arch: pack.Arch, isSrcPack: false, - }) + } + + switch ovalFamily { + case constant.RedHat, constant.Alma, constant.Rocky, constant.Oracle: + if pack.Vendor == "Fedora Project" { + switch util.Major(ovalRelease) { + case "7", "8": + req.family = constant.EPEL + default: + continue + } + } + case constant.Amazon: + if pack.Vendor == "Fedora Project" { + switch strings.Fields(ovalRelease)[0] { + case "2": + req.family = constant.EPEL + req.release = "7" + default: + continue + } + } + } + + requests = append(requests, req) } for _, pack := range r.SrcPackages { requests = append(requests, request{ + family: ovalFamily, + release: ovalRelease, packName: pack.Name, binaryPackNames: pack.BinaryNames, versionRelease: pack.Version, @@ -268,16 +335,8 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate }) } - ovalFamily, err := GetFamilyInOval(r.Family) - if err != nil { - return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) - } - ovalRelease := r.Release - if r.Family == constant.CentOS { - ovalRelease = strings.TrimPrefix(r.Release, "stream") - } for _, req := range requests { - definitions, err := driver.GetByPackName(ovalFamily, ovalRelease, req.packName, req.arch) + definitions, err := driver.GetByPackName(req.family, req.release, req.packName, req.arch) if err != nil { return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err) } @@ -321,9 +380,9 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru } switch family { - case constant.Oracle, constant.Amazon, constant.Fedora: + case constant.Oracle, constant.Amazon, constant.Fedora, constant.EPEL: if ovalPack.Arch == "" { - logging.Log.Infof("Arch is needed to detect Vulns for Amazon Linux, Oracle Linux and Fedora, but empty. You need refresh OVAL maybe. oval: %#v, defID: %s", ovalPack, def.DefinitionID) + logging.Log.Infof("Arch is needed to detect Vulns for Amazon Linux, Oracle Linux, Fedora and EPEL, but empty. You need refresh OVAL maybe. oval: %#v, defID: %s", ovalPack, def.DefinitionID) continue } } diff --git a/scanner/redhatbase.go b/scanner/redhatbase.go index 11b95fa2bd..454cf21109 100644 --- a/scanner/redhatbase.go +++ b/scanner/redhatbase.go @@ -500,25 +500,25 @@ func (o *redhatBase) parseInstalledPackages(stdout string) (models.Packages, mod } func (o *redhatBase) parseInstalledPackagesLine(line string) (*models.Package, error) { - fields := strings.Fields(line) - if len(fields) != 5 { - return nil, - xerrors.Errorf("Failed to parse package line: %s", line) + ss := strings.SplitN(line, " ", 6) + if len(ss) != 6 { + return nil, xerrors.Errorf("Failed to parse package line: %s", line) } ver := "" - epoch := fields[1] + epoch := ss[1] if epoch == "0" || epoch == "(none)" { - ver = fields[2] + ver = ss[2] } else { - ver = fmt.Sprintf("%s:%s", epoch, fields[2]) + ver = fmt.Sprintf("%s:%s", epoch, ss[2]) } return &models.Package{ - Name: fields[0], + Name: ss[0], Version: ver, - Release: fields[3], - Arch: fields[4], + Release: ss[3], + Arch: ss[4], + Vendor: strings.TrimSuffix(ss[5], "\r"), }, nil } @@ -783,8 +783,8 @@ func (o *redhatBase) getOwnerPkgs(paths []string) (names []string, _ error) { } func (o *redhatBase) rpmQa() string { - const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"` - const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"` + const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH} %{VENDOR}\n"` + const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH} %{VENDOR}\n"` switch o.Distro.Family { case constant.OpenSUSE: if o.Distro.Release == "tumbleweed" { @@ -807,8 +807,8 @@ func (o *redhatBase) rpmQa() string { } func (o *redhatBase) rpmQf() string { - const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" ` - const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" ` + const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH} %{VENDOR}\n" ` + const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH} %{VENDOR}\n" ` switch o.Distro.Family { case constant.OpenSUSE: if o.Distro.Release == "tumbleweed" {