Add OVAL HTTP health check
This commit is contained in:
		@@ -26,6 +26,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	c "github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/oval"
 | 
			
		||||
	"github.com/future-architect/vuls/report"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
@@ -395,7 +396,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
	if !c.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
	if ok, err := report.CveClient.CheckHealth(); !ok {
 | 
			
		||||
	if err := report.CveClient.CheckHealth(); err != nil {
 | 
			
		||||
		util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
 | 
			
		||||
		util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with --cvedb-path option")
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
@@ -408,6 +409,15 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Conf.OvalDBURL != "" {
 | 
			
		||||
		err := oval.Base{}.CheckHealth()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
 | 
			
		||||
			util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with --ovaldb-path option")
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	if res, err = report.LoadScanResults(dir); err != nil {
 | 
			
		||||
		util.Log.Error(err)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DebianBase is the base struct of Debian and Ubuntu
 | 
			
		||||
type DebianBase struct{}
 | 
			
		||||
type DebianBase struct{ Base }
 | 
			
		||||
 | 
			
		||||
// fillFromOvalDB returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o DebianBase) fillFromOvalDB(r *models.ScanResult) error {
 | 
			
		||||
@@ -109,7 +109,7 @@ func NewDebian() Debian {
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Debian) FillWithOval(r *models.ScanResult) error {
 | 
			
		||||
	if config.Conf.OvalDBURL != "" {
 | 
			
		||||
	if o.isFetchViaHTTP() {
 | 
			
		||||
		defs, err := getDefsByPackNameViaHTTP(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -144,9 +144,20 @@ func NewUbuntu() Ubuntu {
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Ubuntu) FillWithOval(r *models.ScanResult) error {
 | 
			
		||||
	if err := o.fillFromOvalDB(r); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	if o.isFetchViaHTTP() {
 | 
			
		||||
		defs, err := getDefsByPackNameViaHTTP(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, def := range defs {
 | 
			
		||||
			o.update(r, &def)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := o.fillFromOvalDB(r); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
 | 
			
		||||
			cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								oval/oval.go
									
									
									
									
									
								
							@@ -17,9 +17,40 @@ import (
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	CheckHealth() error
 | 
			
		||||
	FillWithOval(r *models.ScanResult) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct{}
 | 
			
		||||
 | 
			
		||||
// CheckHealth do health check
 | 
			
		||||
func (b Base) CheckHealth() error {
 | 
			
		||||
	if !b.isFetchViaHTTP() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := fmt.Sprintf("%s/health", config.Conf.OvalDBURL)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	resp, _, errs = gorequest.New().Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return fmt.Errorf("Failed to request to OVAL server. url: %s, errs: %v",
 | 
			
		||||
			url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b Base) isFetchViaHTTP() bool {
 | 
			
		||||
	// Default value of OvalDBType is sqlite3
 | 
			
		||||
	if config.Conf.OvalDBURL != "" && config.Conf.OvalDBType == "sqlite3" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type request struct {
 | 
			
		||||
	pack models.Package
 | 
			
		||||
}
 | 
			
		||||
@@ -33,7 +64,6 @@ type response struct {
 | 
			
		||||
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
 | 
			
		||||
	relatedDefs []ovalmodels.Definition, err error) {
 | 
			
		||||
 | 
			
		||||
	//TODO Health Check
 | 
			
		||||
	reqChan := make(chan request, len(r.Packages))
 | 
			
		||||
	resChan := make(chan response, len(r.Packages))
 | 
			
		||||
	errChan := make(chan error, len(r.Packages))
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,11 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedHatBase is the base struct for RedHat and CentOS
 | 
			
		||||
type RedHatBase struct{}
 | 
			
		||||
type RedHatBase struct{ Base }
 | 
			
		||||
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o RedHatBase) FillWithOval(r *models.ScanResult) error {
 | 
			
		||||
	if config.Conf.OvalDBURL != "" {
 | 
			
		||||
	if o.isFetchViaHTTP() {
 | 
			
		||||
		defs, err := getDefsByPackNameViaHTTP(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
 
 | 
			
		||||
@@ -45,10 +45,10 @@ func (api *cvedictClient) initialize() {
 | 
			
		||||
	api.baseURL = config.Conf.CveDBURL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) CheckHealth() (ok bool, err error) {
 | 
			
		||||
	if config.Conf.CveDBURL == "" || config.Conf.CveDBType == "mysql" || config.Conf.CveDBType == "postgres" {
 | 
			
		||||
func (api cvedictClient) CheckHealth() error {
 | 
			
		||||
	if !api.isFetchViaHTTP() {
 | 
			
		||||
		util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDBType)
 | 
			
		||||
		return true, nil
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.initialize()
 | 
			
		||||
@@ -58,9 +58,10 @@ func (api cvedictClient) CheckHealth() (ok bool, err error) {
 | 
			
		||||
	resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
	//  resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v", url, errs)
 | 
			
		||||
		return fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
 | 
			
		||||
			url, errs)
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -69,8 +70,7 @@ type response struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDetails, err error) {
 | 
			
		||||
	switch config.Conf.CveDBType {
 | 
			
		||||
	case "sqlite3", "mysql", "postgres":
 | 
			
		||||
	if !api.isFetchViaHTTP() {
 | 
			
		||||
		return api.FetchCveDetailsFromCveDB(cveIDs)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -195,21 +195,28 @@ type responseGetCveDetailByCpeName struct {
 | 
			
		||||
	CveDetails []cve.CveDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) isFetchViaHTTP() bool {
 | 
			
		||||
	// Default value of CveDBType is sqlite3
 | 
			
		||||
	if config.Conf.CveDBURL != "" && config.Conf.CveDBType == "sqlite3" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]cve.CveDetail, error) {
 | 
			
		||||
	switch config.Conf.CveDBType {
 | 
			
		||||
	case "sqlite3", "mysql", "postgres":
 | 
			
		||||
		return api.FetchCveDetailsByCpeNameFromDB(cpeName)
 | 
			
		||||
	if api.isFetchViaHTTP() {
 | 
			
		||||
		api.baseURL = config.Conf.CveDBURL
 | 
			
		||||
		url, err := util.URLPathJoin(api.baseURL, "cpes")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return []cve.CveDetail{}, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		query := map[string]string{"name": cpeName}
 | 
			
		||||
		util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
		return api.httpPost(cpeName, url, query)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	api.baseURL = config.Conf.CveDBURL
 | 
			
		||||
	url, err := util.URLPathJoin(api.baseURL, "cpes")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return []cve.CveDetail{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	query := map[string]string{"name": cpeName}
 | 
			
		||||
	util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
	return api.httpPost(cpeName, url, query)
 | 
			
		||||
	return api.FetchCveDetailsByCpeNameFromDB(cpeName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
 | 
			
		||||
@@ -217,7 +224,8 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
 | 
			
		||||
		//  req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
 | 
			
		||||
		req := gorequest.New().Post(url)
 | 
			
		||||
		for key := range query {
 | 
			
		||||
			req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -70,44 +70,31 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO remove debug code
 | 
			
		||||
	//  for _, r := range filled {
 | 
			
		||||
	//      pp.Printf("filled: %d\n", len(r.ScannedCves))
 | 
			
		||||
	//  }
 | 
			
		||||
 | 
			
		||||
	filtered := []models.ScanResult{}
 | 
			
		||||
	for _, r := range filled {
 | 
			
		||||
		filtered = append(filtered, r.FilterByCvssOver(c.Conf.CvssScoreOver))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO remove debug code
 | 
			
		||||
	//  for _, r := range filtered {
 | 
			
		||||
	//      pp.Printf("filtered: %d\n", len(r.ScannedCves))
 | 
			
		||||
	//  }
 | 
			
		||||
 | 
			
		||||
	return filtered, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillCveInfo(r *models.ScanResult) error {
 | 
			
		||||
	util.Log.Debugf("need to refresh")
 | 
			
		||||
	if c.Conf.CveDBType == "sqlite3" {
 | 
			
		||||
		if c.Conf.CveDBURL == "" {
 | 
			
		||||
			if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) {
 | 
			
		||||
				return fmt.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
 | 
			
		||||
					c.Conf.CveDBPath)
 | 
			
		||||
			}
 | 
			
		||||
	if c.Conf.CveDBType == "sqlite3" && c.Conf.CveDBURL == "" {
 | 
			
		||||
		if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) {
 | 
			
		||||
			return fmt.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
 | 
			
		||||
				c.Conf.CveDBPath)
 | 
			
		||||
		}
 | 
			
		||||
		if c.Conf.OvalDBURL == "" {
 | 
			
		||||
			if _, err := os.Stat(c.Conf.OvalDBPath); os.IsNotExist(err) {
 | 
			
		||||
				//TODO Warning
 | 
			
		||||
				return fmt.Errorf("SQLite3 DB(OVAL-Dictionary) is not exist: %s",
 | 
			
		||||
					c.Conf.OvalDBPath)
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
	if c.Conf.OvalDBType == "sqlite3" && c.Conf.OvalDBURL == "" {
 | 
			
		||||
		if _, err := os.Stat(c.Conf.OvalDBPath); os.IsNotExist(err) {
 | 
			
		||||
			// TODO Warning??
 | 
			
		||||
			return fmt.Errorf("SQLite3 DB(OVAL-Dictionary) is not exist: %s",
 | 
			
		||||
				c.Conf.OvalDBPath)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	util.Log.Debugf("Fill CVE detailed information with OVAL")
 | 
			
		||||
	if err := fillWithOvalDB(r); err != nil {
 | 
			
		||||
	if err := fillWithOval(r); err != nil {
 | 
			
		||||
		return fmt.Errorf("Failed to fill OVAL information: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +153,7 @@ func fillWithCveDB(r *models.ScanResult) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillWithOvalDB(r *models.ScanResult) error {
 | 
			
		||||
func fillWithOval(r *models.ScanResult) error {
 | 
			
		||||
	var ovalClient oval.Client
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case "debian":
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
 | 
			
		||||
			return false, deb, nil
 | 
			
		||||
		}
 | 
			
		||||
		if r.ExitStatus == 255 {
 | 
			
		||||
			return false, deb, fmt.Errorf("Unable to connect via SSH. Check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add a HostKey. %s", r)
 | 
			
		||||
			return false, deb, fmt.Errorf("Unable to connect via SSH. Check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
 | 
			
		||||
		}
 | 
			
		||||
		util.Log.Debugf("Not Debian like Linux. %s", r)
 | 
			
		||||
		return false, deb, nil
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user