feat(detect/redhat): detect unpatched vulnerabilities with oval, stop using gost (#1907)
* feat(oval/redhat): detect not fixed package * feat(gost/redhat): stop using to detect unpatched vulnerabilities
This commit is contained in:
		
							
								
								
									
										100
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -18,6 +18,7 @@ import (
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	rpmver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
@@ -43,6 +44,7 @@ type defPacks struct {
 | 
			
		||||
 | 
			
		||||
type fixStat struct {
 | 
			
		||||
	notFixedYet bool
 | 
			
		||||
	fixState    string
 | 
			
		||||
	fixedIn     string
 | 
			
		||||
	isSrcPack   bool
 | 
			
		||||
	srcPackName string
 | 
			
		||||
@@ -53,6 +55,7 @@ func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
 | 
			
		||||
		ps = append(ps, models.PackageFixStatus{
 | 
			
		||||
			Name:        name,
 | 
			
		||||
			NotFixedYet: stat.notFixedYet,
 | 
			
		||||
			FixState:    stat.fixState,
 | 
			
		||||
			FixedIn:     stat.fixedIn,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
@@ -197,7 +200,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			for _, def := range res.defs {
 | 
			
		||||
				affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, res.request, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
				affected, notFixedYet, fixState, fixedIn, err := isOvalDefAffected(def, res.request, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errs = append(errs, err)
 | 
			
		||||
					continue
 | 
			
		||||
@@ -209,16 +212,18 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
				if res.request.isSrcPack {
 | 
			
		||||
					for _, n := range res.request.binaryPackNames {
 | 
			
		||||
						fs := fixStat{
 | 
			
		||||
							srcPackName: res.request.packName,
 | 
			
		||||
							isSrcPack:   true,
 | 
			
		||||
							notFixedYet: notFixedYet,
 | 
			
		||||
							fixState:    fixState,
 | 
			
		||||
							fixedIn:     fixedIn,
 | 
			
		||||
							isSrcPack:   true,
 | 
			
		||||
							srcPackName: res.request.packName,
 | 
			
		||||
						}
 | 
			
		||||
						relatedDefs.upsert(def, n, fs)
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					fs := fixStat{
 | 
			
		||||
						notFixedYet: notFixedYet,
 | 
			
		||||
						fixState:    fixState,
 | 
			
		||||
						fixedIn:     fixedIn,
 | 
			
		||||
					}
 | 
			
		||||
					relatedDefs.upsert(def, res.request.packName, fs)
 | 
			
		||||
@@ -338,7 +343,7 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate
 | 
			
		||||
			return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, def := range definitions {
 | 
			
		||||
			affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, req, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
			affected, notFixedYet, fixState, fixedIn, err := isOvalDefAffected(def, req, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return relatedDefs, xerrors.Errorf("Failed to exec isOvalAffected. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
@@ -349,9 +354,10 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate
 | 
			
		||||
			if req.isSrcPack {
 | 
			
		||||
				for _, binName := range req.binaryPackNames {
 | 
			
		||||
					fs := fixStat{
 | 
			
		||||
						notFixedYet: false,
 | 
			
		||||
						isSrcPack:   true,
 | 
			
		||||
						notFixedYet: notFixedYet,
 | 
			
		||||
						fixState:    fixState,
 | 
			
		||||
						fixedIn:     fixedIn,
 | 
			
		||||
						isSrcPack:   true,
 | 
			
		||||
						srcPackName: req.packName,
 | 
			
		||||
					}
 | 
			
		||||
					relatedDefs.upsert(def, binName, fs)
 | 
			
		||||
@@ -359,6 +365,7 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate
 | 
			
		||||
			} else {
 | 
			
		||||
				fs := fixStat{
 | 
			
		||||
					notFixedYet: notFixedYet,
 | 
			
		||||
					fixState:    fixState,
 | 
			
		||||
					fixedIn:     fixedIn,
 | 
			
		||||
				}
 | 
			
		||||
				relatedDefs.upsert(def, req.packName, fs)
 | 
			
		||||
@@ -370,13 +377,13 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate
 | 
			
		||||
 | 
			
		||||
var modularVersionPattern = regexp.MustCompile(`.+\.module(?:\+el|_f)\d{1,2}.*`)
 | 
			
		||||
 | 
			
		||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family, release string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string, err error) {
 | 
			
		||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family, release string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixState, fixedIn string, err error) {
 | 
			
		||||
	if family == constant.Amazon && release == "2" {
 | 
			
		||||
		if def.Advisory.AffectedRepository == "" {
 | 
			
		||||
			def.Advisory.AffectedRepository = "amzn2-core"
 | 
			
		||||
		}
 | 
			
		||||
		if req.repository != def.Advisory.AffectedRepository {
 | 
			
		||||
			return false, false, "", nil
 | 
			
		||||
			return false, false, "", "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -403,32 +410,49 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family, release s
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// There is a modular package and a non-modular package with the same name. (e.g. fedora 35 community-mysql)
 | 
			
		||||
		if ovalPack.ModularityLabel == "" && modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
			continue
 | 
			
		||||
		} else if ovalPack.ModularityLabel != "" && !modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isModularityLabelEmptyOrSame := false
 | 
			
		||||
		if ovalPack.ModularityLabel != "" {
 | 
			
		||||
		var modularityNameStreamLabel string
 | 
			
		||||
		if ovalPack.ModularityLabel == "" {
 | 
			
		||||
			if modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// expect ovalPack.ModularityLabel e.g. RedHat: nginx:1.16, Fedora: mysql:8.0:3520211031142409:f27b74a8
 | 
			
		||||
			if !modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ss := strings.Split(ovalPack.ModularityLabel, ":")
 | 
			
		||||
			if len(ss) < 2 {
 | 
			
		||||
				logging.Log.Warnf("Invalid modularitylabel format in oval package. Maybe it is necessary to fix modularitylabel of goval-dictionary. expected: ${name}:${stream}(:${version}:${context}:${arch}), actual: %s", ovalPack.ModularityLabel)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			modularityNameStreamLabel := fmt.Sprintf("%s:%s", ss[0], ss[1])
 | 
			
		||||
			for _, mod := range enabledMods {
 | 
			
		||||
				if mod == modularityNameStreamLabel {
 | 
			
		||||
					isModularityLabelEmptyOrSame = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			modularityNameStreamLabel = fmt.Sprintf("%s:%s", ss[0], ss[1])
 | 
			
		||||
			if !slices.Contains(enabledMods, modularityNameStreamLabel) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			isModularityLabelEmptyOrSame = true
 | 
			
		||||
		}
 | 
			
		||||
		if !isModularityLabelEmptyOrSame {
 | 
			
		||||
			continue
 | 
			
		||||
 | 
			
		||||
		if ovalPack.NotFixedYet {
 | 
			
		||||
			switch family {
 | 
			
		||||
			case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
 | 
			
		||||
				n := req.packName
 | 
			
		||||
				if modularityNameStreamLabel != "" {
 | 
			
		||||
					n = fmt.Sprintf("%s/%s", modularityNameStreamLabel, req.packName)
 | 
			
		||||
				}
 | 
			
		||||
				for _, r := range def.Advisory.AffectedResolution {
 | 
			
		||||
					if slices.ContainsFunc(r.Components, func(c ovalmodels.Component) bool { return c.Component == n }) {
 | 
			
		||||
						switch r.State {
 | 
			
		||||
						case "Will not fix", "Under investigation":
 | 
			
		||||
							return false, true, r.State, ovalPack.Version, nil
 | 
			
		||||
						default:
 | 
			
		||||
							return true, true, r.State, ovalPack.Version, nil
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				return true, true, "", ovalPack.Version, nil
 | 
			
		||||
			default:
 | 
			
		||||
				return true, true, "", ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if running.Release != "" {
 | 
			
		||||
@@ -443,21 +467,16 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family, release s
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ovalPack.NotFixedYet {
 | 
			
		||||
			return true, true, ovalPack.Version, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Compare between the installed version vs the version in OVAL
 | 
			
		||||
		less, err := lessThan(family, req.versionRelease, ovalPack)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logging.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
				err, req.versionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
			return false, false, ovalPack.Version, nil
 | 
			
		||||
			logging.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s", err, req.versionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
			return false, false, "", ovalPack.Version, nil
 | 
			
		||||
		}
 | 
			
		||||
		if less {
 | 
			
		||||
			if req.isSrcPack {
 | 
			
		||||
				// Unable to judge whether fixed or not-fixed of src package(Ubuntu, Debian)
 | 
			
		||||
				return true, false, ovalPack.Version, nil
 | 
			
		||||
				return true, false, "", ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// If the version of installed is less than in OVAL
 | 
			
		||||
@@ -474,7 +493,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family, release s
 | 
			
		||||
				constant.Raspbian,
 | 
			
		||||
				constant.Ubuntu:
 | 
			
		||||
				// Use fixed state in OVAL for these distros.
 | 
			
		||||
				return true, false, ovalPack.Version, nil
 | 
			
		||||
				return true, false, "", ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// But CentOS/Alma/Rocky can't judge whether fixed or unfixed.
 | 
			
		||||
@@ -485,20 +504,19 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family, release s
 | 
			
		||||
			// In these mode, the blow field was set empty.
 | 
			
		||||
			// Vuls can not judge fixed or unfixed.
 | 
			
		||||
			if req.newVersionRelease == "" {
 | 
			
		||||
				return true, false, ovalPack.Version, nil
 | 
			
		||||
				return true, false, "", ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// compare version: newVer vs oval
 | 
			
		||||
			less, err := lessThan(family, req.newVersionRelease, ovalPack)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logging.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
 | 
			
		||||
					err, req.newVersionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
				return false, false, ovalPack.Version, nil
 | 
			
		||||
				logging.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s", err, req.newVersionRelease, ovalPack, def.DefinitionID)
 | 
			
		||||
				return false, false, "", ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
			return true, less, ovalPack.Version, nil
 | 
			
		||||
			return true, less, "", ovalPack.Version, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false, false, "", nil
 | 
			
		||||
	return false, false, "", "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user