diff --git a/commands/report.go b/commands/report.go index d19ab81a..8fcf43db 100644 --- a/commands/report.go +++ b/commands/report.go @@ -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) diff --git a/oval/debian.go b/oval/debian.go index 80f87f87..a28784a0 100644 --- a/oval/debian.go +++ b/oval/debian.go @@ -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 diff --git a/oval/oval.go b/oval/oval.go index 891580ce..174ba371 100644 --- a/oval/oval.go +++ b/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)) diff --git a/oval/redhat.go b/oval/redhat.go index e11d392d..4492b533 100644 --- a/oval/redhat.go +++ b/oval/redhat.go @@ -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 diff --git a/report/cve_client.go b/report/cve_client.go index cde90e19..5f8808ca 100644 --- a/report/cve_client.go +++ b/report/cve_client.go @@ -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") } diff --git a/report/report.go b/report/report.go index 5926b779..bbcb725f 100644 --- a/report/report.go +++ b/report/report.go @@ -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": diff --git a/scan/debian.go b/scan/debian.go index 19c8804c..42cd3687 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -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