From 7d8a24ee1a81348ee691e18896d3a406d0f52ada Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Sat, 19 Feb 2022 09:20:45 +0900 Subject: [PATCH] refactor(detector): standardize db.NewDB to db.CloseDB (#1380) * feat(subcmds/report,server): read environment variables when configPath is "" * refactor: standardize db.NewDB to db.CloseDB * chore: clean up import * chore: error wrap * chore: update goval-dictionary * fix(oval): return Pseudo instead of nil for client * chore: fix comment * fix: lint error --- detector/cve_client.go | 157 ++++++++++++++++++++--------------------- detector/detector.go | 67 +++++++++--------- detector/exploitdb.go | 85 ++++++++++++++-------- detector/kevuln.go | 76 +++++++++++++------- detector/msf.go | 83 ++++++++++++++-------- go.mod | 3 +- go.sum | 6 +- gost/debian.go | 39 +++++----- gost/gost.go | 84 +++++++++++----------- gost/microsoft.go | 6 +- gost/pseudo.go | 2 +- gost/redhat.go | 33 ++++----- gost/ubuntu.go | 24 ++++--- oval/alpine.go | 31 ++++---- oval/debian.go | 58 ++++++--------- oval/oval.go | 102 +++++++++++++------------- oval/pseudo.go | 23 ++++++ oval/redhat.go | 73 ++++++++++--------- oval/suse.go | 32 ++++----- oval/util.go | 67 ++++++++++-------- server/server.go | 10 +-- subcmds/report.go | 20 +++++- subcmds/server.go | 23 ++++-- 23 files changed, 610 insertions(+), 494 deletions(-) create mode 100644 oval/pseudo.go diff --git a/detector/cve_client.go b/detector/cve_client.go index 076b5623..46decbdf 100644 --- a/detector/cve_client.go +++ b/detector/cve_client.go @@ -22,40 +22,27 @@ import ( ) type goCveDictClient struct { - cnf config.VulnDictInterface - driver cvedb.DB + driver cvedb.DB + baseURL string } func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) { if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { - return nil, err + return nil, xerrors.Errorf("Failed to set go-cve-dictionary logger. err: %w", err) } - driver, locked, err := newCveDB(cnf) - if locked { - return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { - return nil, err + driver, err := newCveDB(cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newCveDB. err: %w", err) } - return &goCveDictClient{cnf: cnf, driver: driver}, nil + return &goCveDictClient{driver: driver, baseURL: cnf.GetURL()}, nil } -func (api goCveDictClient) closeDB() error { - if api.driver == nil { +func (client goCveDictClient) closeDB() error { + if client.driver == nil { return nil } - return api.driver.CloseDB() -} - -func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) { - m, err := api.driver.GetMulti(cveIDs) - if err != nil { - return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err) - } - for _, v := range m { - cveDetails = append(cveDetails, v) - } - return cveDetails, nil + return client.driver.CloseDB() } type response struct { @@ -63,57 +50,67 @@ type response struct { CveDetail cvemodels.CveDetail } -func (api goCveDictClient) fetchCveDetailsViaHTTP(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) { - reqChan := make(chan string, len(cveIDs)) - resChan := make(chan response, len(cveIDs)) - errChan := make(chan error, len(cveIDs)) - defer close(reqChan) - defer close(resChan) - defer close(errChan) +func (client goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) { + if client.driver == nil { + reqChan := make(chan string, len(cveIDs)) + resChan := make(chan response, len(cveIDs)) + errChan := make(chan error, len(cveIDs)) + defer close(reqChan) + defer close(resChan) + defer close(errChan) - go func() { - for _, cveID := range cveIDs { - reqChan <- cveID - } - }() + go func() { + for _, cveID := range cveIDs { + reqChan <- cveID + } + }() - concurrency := 10 - tasks := util.GenWorkers(concurrency) - for range cveIDs { - tasks <- func() { - select { - case cveID := <-reqChan: - url, err := util.URLPathJoin(api.cnf.GetURL(), "cves", cveID) - if err != nil { - errChan <- err - } else { - logging.Log.Debugf("HTTP Request to %s", url) - api.httpGet(cveID, url, resChan, errChan) + concurrency := 10 + tasks := util.GenWorkers(concurrency) + for range cveIDs { + tasks <- func() { + select { + case cveID := <-reqChan: + url, err := util.URLPathJoin(client.baseURL, "cves", cveID) + if err != nil { + errChan <- err + } else { + logging.Log.Debugf("HTTP Request to %s", url) + httpGet(cveID, url, resChan, errChan) + } } } } - } - timeout := time.After(2 * 60 * time.Second) - var errs []error - for range cveIDs { - select { - case res := <-resChan: - cveDetails = append(cveDetails, res.CveDetail) - case err := <-errChan: - errs = append(errs, err) - case <-timeout: - return nil, xerrors.New("Timeout Fetching CVE") + timeout := time.After(2 * 60 * time.Second) + var errs []error + for range cveIDs { + select { + case res := <-resChan: + cveDetails = append(cveDetails, res.CveDetail) + case err := <-errChan: + errs = append(errs, err) + case <-timeout: + return nil, xerrors.New("Timeout Fetching CVE") + } + } + if len(errs) != 0 { + return nil, + xerrors.Errorf("Failed to fetch CVE. err: %w", errs) + } + } else { + m, err := client.driver.GetMulti(cveIDs) + if err != nil { + return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err) + } + for _, v := range m { + cveDetails = append(cveDetails, v) } } - if len(errs) != 0 { - return nil, - xerrors.Errorf("Failed to fetch CVE. err: %w", errs) - } - return + return cveDetails, nil } -func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) { +func httpGet(key, url string, resChan chan<- response, errChan chan<- error) { var body string var errs []error var resp *http.Response @@ -144,21 +141,21 @@ func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, err } } -func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) { - if api.cnf.IsFetchViaHTTP() { - url, err := util.URLPathJoin(api.cnf.GetURL(), "cpes") +func (client goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) { + if client.driver == nil { + url, err := util.URLPathJoin(client.baseURL, "cpes") if err != nil { - return nil, err + return nil, xerrors.Errorf("Failed to join URLPath. err: %w", err) } query := map[string]string{"name": cpeURI} logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query) - if cves, err = api.httpPost(url, query); err != nil { - return nil, err + if cves, err = httpPost(url, query); err != nil { + return nil, xerrors.Errorf("Failed to post HTTP Request. err: %w", err) } } else { - if cves, err = api.driver.GetByCpeURI(cpeURI); err != nil { - return nil, err + if cves, err = client.driver.GetByCpeURI(cpeURI); err != nil { + return nil, xerrors.Errorf("Failed to get CVEs by CPEURI. err: %w", err) } } @@ -177,7 +174,7 @@ func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves [ return nvdCves, nil } -func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) { +func httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) { var body string var errs []error var resp *http.Response @@ -208,18 +205,20 @@ func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvem return cveDetails, nil } -func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err error) { +func newCveDB(cnf config.VulnDictInterface) (cvedb.DB, error) { if cnf.IsFetchViaHTTP() { - return nil, false, nil + return nil, nil } path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{}) + driver, locked, err := cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{}) if err != nil { - err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path) - return nil, locked, err + if locked { + return nil, xerrors.Errorf("Failed to init CVE DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) + } + return nil, xerrors.Errorf("Failed to init CVE DB. DB Path: %s, err: %w", path, err) } - return driver, false, nil + return driver, nil } diff --git a/detector/detector.go b/detector/detector.go index dd1e7074..a81e1926 100644 --- a/detector/detector.go +++ b/detector/detector.go @@ -8,6 +8,8 @@ import ( "strings" "time" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/contrib/owasp-dependency-check/parser" @@ -19,7 +21,6 @@ import ( "github.com/future-architect/vuls/reporter" "github.com/future-architect/vuls/util" cvemodels "github.com/vulsio/go-cve-dictionary/models" - "golang.org/x/xerrors" ) // Cpe : @@ -47,7 +48,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err) } - if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil { + if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil { return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err) } @@ -91,7 +92,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err) } - if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil { + if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil { return nil, xerrors.Errorf("Failed to fill with gost: %w", err) } @@ -99,19 +100,19 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { return nil, xerrors.Errorf("Failed to fill with CVE: %w", err) } - nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit) + nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts) if err != nil { return nil, xerrors.Errorf("Failed to fill with exploit: %w", err) } logging.Log.Infof("%s: %d PoC are detected", r.FormatServerName(), nExploitCve) - nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit) + nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts) if err != nil { return nil, xerrors.Errorf("Failed to fill with metasploit: %w", err) } logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve) - if err := FillWithKEVuln(&r, config.Conf.KEVuln); err != nil { + if err := FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil { return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err) } @@ -143,7 +144,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { if config.Conf.DiffPlus || config.Conf.DiffMinus { prevs, err := loadPrevious(rs, config.Conf.ResultsDir) if err != nil { - return nil, err + return nil, xerrors.Errorf("Failed to load previous results. err: %w", err) } rs = diff(rs, prevs, config.Conf.DiffPlus, config.Conf.DiffMinus) } @@ -205,7 +206,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { // DetectPkgCves detects OS pkg cves // pass 2 configs -func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf) error { +func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, logOpts logging.LogOpts) error { // Pkg Scan if r.Release != "" { if len(r.Packages)+len(r.SrcPackages) > 0 { @@ -215,12 +216,12 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c } // OVAL - if err := detectPkgsCvesWithOval(ovalCnf, r); err != nil { + if err := detectPkgsCvesWithOval(ovalCnf, r, logOpts); err != nil { return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err) } // gost - if err := detectPkgsCvesWithGost(gostCnf, r); err != nil { + if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil { return xerrors.Errorf("Failed to detect CVE with gost: %w", err) } } else { @@ -308,7 +309,7 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts client, err := newGoCveDictClient(&cnf, logOpts) if err != nil { - return err + return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err) } defer func() { if err := client.closeDB(); err != nil { @@ -316,14 +317,9 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts } }() - var ds []cvemodels.CveDetail - if cnf.IsFetchViaHTTP() { - ds, err = client.fetchCveDetailsViaHTTP(cveIDs) - } else { - ds, err = client.fetchCveDetails(cveIDs) - } + ds, err := client.fetchCveDetails(cveIDs) if err != nil { - return err + return xerrors.Errorf("Failed to fetchCveDetails. err: %w", err) } for _, d := range ds { @@ -391,37 +387,43 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) { } // detectPkgsCvesWithOval fetches OVAL database -func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) error { - ovalClient, err := oval.NewOVALClient(r.Family, cnf) +func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult, logOpts logging.LogOpts) error { + client, err := oval.NewOVALClient(r.Family, cnf, logOpts) if err != nil { return err } - if ovalClient == nil { - return nil - } + defer func() { + if err := client.CloseDB(); err != nil { + logging.Log.Errorf("Failed to close the OVAL DB. err: %+v", err) + } + }() logging.Log.Debugf("Check if oval fetched: %s %s", r.Family, r.Release) - ok, err := ovalClient.CheckIfOvalFetched(r.Family, r.Release) + ok, err := client.CheckIfOvalFetched(r.Family, r.Release) if err != nil { return err } if !ok { - if r.Family == constant.Debian { + switch r.Family { + case constant.Debian: logging.Log.Infof("Skip OVAL and Scan with gost alone.") logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0) return nil + case constant.Windows, constant.FreeBSD, constant.ServerTypePseudo: + return nil + default: + return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release) } - return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release) } logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release) - _, err = ovalClient.CheckIfOvalFresh(r.Family, r.Release) + _, err = client.CheckIfOvalFresh(r.Family, r.Release) if err != nil { return err } logging.Log.Debugf("Fill with oval: %s %s", r.Family, r.Release) - nCVEs, err := ovalClient.FillWithOval(r) + nCVEs, err := client.FillWithOval(r) if err != nil { return err } @@ -430,12 +432,11 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro return nil } -func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error { - client, err := gost.NewClient(cnf, r.Family) +func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts logging.LogOpts) error { + client, err := gost.NewGostClient(cnf, r.Family, logOpts) if err != nil { return xerrors.Errorf("Failed to new a gost client: %w", err) } - defer func() { if err := client.CloseDB(); err != nil { logging.Log.Errorf("Failed to close the gost DB. err: %+v", err) @@ -464,7 +465,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error { func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictConf, logOpts logging.LogOpts) error { client, err := newGoCveDictClient(&cnf, logOpts) if err != nil { - return err + return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err) } defer func() { if err := client.closeDB(); err != nil { @@ -476,7 +477,7 @@ func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictCon for _, cpe := range cpes { details, err := client.detectCveByCpeURI(cpe.CpeURI, cpe.UseJVN) if err != nil { - return err + return xerrors.Errorf("Failed to detectCveByCpeURI. err: %w", err) } for _, detail := range details { diff --git a/detector/exploitdb.go b/detector/exploitdb.go index c7180d37..6e396f69 100644 --- a/detector/exploitdb.go +++ b/detector/exploitdb.go @@ -9,33 +9,73 @@ import ( "time" "github.com/cenkalti/backoff" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - "github.com/parnurzeal/gorequest" exploitdb "github.com/vulsio/go-exploitdb/db" exploitmodels "github.com/vulsio/go-exploitdb/models" - - "golang.org/x/xerrors" + exploitlog "github.com/vulsio/go-exploitdb/util" ) +// goExploitDBClient is a DB Driver +type goExploitDBClient struct { + driver exploitdb.DB + baseURL string +} + +// closeDB close a DB connection +func (client goExploitDBClient) closeDB() error { + if client.driver == nil { + return nil + } + return client.driver.CloseDB() +} + +func newGoExploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goExploitDBClient, error) { + if err := exploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { + return nil, xerrors.Errorf("Failed to set go-exploitdb logger. err: %w", err) + } + + db, err := newExploitDB(cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newExploitDB. err: %w", err) + } + return &goExploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil +} + // FillWithExploit fills exploit information that has in Exploit -func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve int, err error) { - if cnf.IsFetchViaHTTP() { +func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf, logOpts logging.LogOpts) (nExploitCve int, err error) { + client, err := newGoExploitDBClient(&cnf, logOpts) + if err != nil { + return 0, xerrors.Errorf("Failed to newGoExploitDBClient. err: %w", err) + } + defer func() { + if err := client.closeDB(); err != nil { + logging.Log.Errorf("Failed to close DB. err: %+v", err) + } + }() + + if client.driver == nil { var cveIDs []string for cveID := range r.ScannedCves { cveIDs = append(cveIDs, cveID) } - prefix, _ := util.URLPathJoin(cnf.GetURL(), "cves") + prefix, err := util.URLPathJoin(client.baseURL, "cves") + if err != nil { + return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err) + } responses, err := getExploitsViaHTTP(cveIDs, prefix) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Exploits via HTTP. err: %w", err) } for _, res := range responses { exps := []exploitmodels.Exploit{} if err := json.Unmarshal([]byte(res.json), &exps); err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err) } exploits := ConvertToModelsExploit(exps) v, ok := r.ScannedCves[res.request.cveID] @@ -46,25 +86,13 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve nExploitCve++ } } else { - driver, locked, err := newExploitDB(&cnf) - if locked { - return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - for cveID, vuln := range r.ScannedCves { if cveID == "" { continue } - es, err := driver.GetExploitByCveID(cveID) + es, err := client.driver.GetExploitByCveID(cveID) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Exploits by CVE-ID. err: %w", err) } if len(es) == 0 { continue @@ -203,19 +231,20 @@ func httpGetExploit(url string, req exploitRequest, resChan chan<- exploitRespon } } -func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked bool, err error) { +func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, err error) { if cnf.IsFetchViaHTTP() { - return nil, false, nil + return nil, nil } path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}); err != nil { + driver, locked, err := exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}) + if err != nil { if locked { - return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err) + return nil, xerrors.Errorf("Failed to init exploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) } - return nil, false, err + return nil, xerrors.Errorf("Failed to init exploit DB. DB Path: %s, err: %w", path, err) } - return driver, false, nil + return driver, nil } diff --git a/detector/kevuln.go b/detector/kevuln.go index 4b324f35..a32e0914 100644 --- a/detector/kevuln.go +++ b/detector/kevuln.go @@ -9,25 +9,62 @@ import ( "time" "github.com/cenkalti/backoff" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - "github.com/parnurzeal/gorequest" - "golang.org/x/xerrors" - kevulndb "github.com/vulsio/go-kev/db" kevulnmodels "github.com/vulsio/go-kev/models" + kevulnlog "github.com/vulsio/go-kev/utils" ) +// goKEVulnDBClient is a DB Driver +type goKEVulnDBClient struct { + driver kevulndb.DB + baseURL string +} + +// closeDB close a DB connection +func (client goKEVulnDBClient) closeDB() error { + if client.driver == nil { + return nil + } + return client.driver.CloseDB() +} + +func newGoKEVulnDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goKEVulnDBClient, error) { + if err := kevulnlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { + return nil, xerrors.Errorf("Failed to set go-kev logger. err: %w", err) + } + + db, err := newKEVulnDB(cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newKEVulnDB. err: %w", err) + } + return &goKEVulnDBClient{driver: db, baseURL: cnf.GetURL()}, nil +} + // FillWithKEVuln : -func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error { - if cnf.IsFetchViaHTTP() { +func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging.LogOpts) error { + client, err := newGoKEVulnDBClient(&cnf, logOpts) + if err != nil { + return err + } + defer func() { + if err := client.closeDB(); err != nil { + logging.Log.Errorf("Failed to close DB. err: %+v", err) + } + }() + + if client.driver == nil { var cveIDs []string for cveID := range r.ScannedCves { cveIDs = append(cveIDs, cveID) } - prefix, err := util.URLPathJoin(cnf.GetURL(), "cves") + prefix, err := util.URLPathJoin(client.baseURL, "cves") if err != nil { return err } @@ -57,23 +94,11 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error { r.ScannedCves[res.request.cveID] = v } } else { - driver, locked, err := newKEVulnDB(&cnf) - if locked { - return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { - return err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - for cveID, vuln := range r.ScannedCves { if cveID == "" { continue } - kevulns, err := driver.GetKEVulnByCveID(cveID) + kevulns, err := client.driver.GetKEVulnByCveID(cveID) if err != nil { return err } @@ -196,19 +221,20 @@ func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, } } -func newKEVulnDB(cnf config.VulnDictInterface) (driver kevulndb.DB, locked bool, err error) { +func newKEVulnDB(cnf config.VulnDictInterface) (kevulndb.DB, error) { if cnf.IsFetchViaHTTP() { - return nil, false, nil + return nil, nil } path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - if driver, locked, err = kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}); err != nil { + driver, locked, err := kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}) + if err != nil { if locked { - return nil, true, xerrors.Errorf("kevulnDB is locked. err: %w", err) + return nil, xerrors.Errorf("Failed to init kevuln DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) } - return nil, false, err + return nil, xerrors.Errorf("Failed to init kevuln DB. DB Path: %s, err: %w", path, err) } - return driver, false, nil + return driver, nil } diff --git a/detector/msf.go b/detector/msf.go index 2193cb74..b1aa8b4f 100644 --- a/detector/msf.go +++ b/detector/msf.go @@ -9,35 +9,73 @@ import ( "time" "github.com/cenkalti/backoff" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - "github.com/parnurzeal/gorequest" metasploitdb "github.com/vulsio/go-msfdb/db" metasploitmodels "github.com/vulsio/go-msfdb/models" - "golang.org/x/xerrors" + metasploitlog "github.com/vulsio/go-msfdb/utils" ) +// goMetasploitDBClient is a DB Driver +type goMetasploitDBClient struct { + driver metasploitdb.DB + baseURL string +} + +// closeDB close a DB connection +func (client goMetasploitDBClient) closeDB() error { + if client.driver == nil { + return nil + } + return client.driver.CloseDB() +} + +func newGoMetasploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goMetasploitDBClient, error) { + if err := metasploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { + return nil, xerrors.Errorf("Failed to set go-msfdb logger. err: %w", err) + } + + db, err := newMetasploitDB(cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newMetasploitDB. err: %w", err) + } + return &goMetasploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil +} + // FillWithMetasploit fills metasploit module information that has in module -func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetasploitCve int, err error) { - if cnf.IsFetchViaHTTP() { +func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf, logOpts logging.LogOpts) (nMetasploitCve int, err error) { + client, err := newGoMetasploitDBClient(&cnf, logOpts) + if err != nil { + return 0, xerrors.Errorf("Failed to newGoMetasploitDBClient. err: %w", err) + } + defer func() { + if err := client.closeDB(); err != nil { + logging.Log.Errorf("Failed to close DB. err: %+v", err) + } + }() + + if client.driver == nil { var cveIDs []string for cveID := range r.ScannedCves { cveIDs = append(cveIDs, cveID) } - prefix, err := util.URLPathJoin(cnf.GetURL(), "cves") + prefix, err := util.URLPathJoin(client.baseURL, "cves") if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err) } responses, err := getMetasploitsViaHTTP(cveIDs, prefix) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Metasploits via HTTP. err: %w", err) } for _, res := range responses { msfs := []metasploitmodels.Metasploit{} if err := json.Unmarshal([]byte(res.json), &msfs); err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err) } metasploits := ConvertToModelsMsf(msfs) v, ok := r.ScannedCves[res.request.cveID] @@ -48,25 +86,13 @@ func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetas nMetasploitCve++ } } else { - driver, locked, err := newMetasploitDB(&cnf) - if locked { - return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - for cveID, vuln := range r.ScannedCves { if cveID == "" { continue } - ms, err := driver.GetModuleByCveID(cveID) + ms, err := client.driver.GetModuleByCveID(cveID) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Metasploits by CVE-ID. err: %w", err) } if len(ms) == 0 { continue @@ -199,19 +225,20 @@ func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Meta return modules } -func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, locked bool, err error) { +func newMetasploitDB(cnf config.VulnDictInterface) (metasploitdb.DB, error) { if cnf.IsFetchViaHTTP() { - return nil, false, nil + return nil, nil } path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}); err != nil { + driver, locked, err := metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}) + if err != nil { if locked { - return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err) + return nil, xerrors.Errorf("Failed to init metasploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) } - return nil, false, err + return nil, xerrors.Errorf("Failed to init metasploit DB. DB Path: %s, err: %w", path, err) } - return driver, false, nil + return driver, nil } diff --git a/go.mod b/go.mod index d33cd956..b7d63284 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,6 @@ require ( github.com/gosuri/uitable v0.0.4 github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.4.0 - github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c github.com/jesseduffield/gocui v0.3.0 github.com/k0kubun/pp v3.0.1+incompatible github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f @@ -56,7 +55,7 @@ require ( github.com/vulsio/go-kev v0.1.0 github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8 - github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c + github.com/vulsio/goval-dictionary v0.7.1-0.20220215081041-a472884d0afa golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 diff --git a/go.sum b/go.sum index 994031dd..74ea121c 100644 --- a/go.sum +++ b/go.sum @@ -999,8 +999,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0= -github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk= -github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/htcat/htcat v1.0.2/go.mod h1:i8ViQbjSi2+lJzM6Lx20FIxHENCz6mzJglK3HH06W3s= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -1763,8 +1761,8 @@ github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 h1:2uYZw2gQ0kymw github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14/go.mod h1:NGdcwWxCK/ES8vZ/crzREqI69S5gH1MivCpSp1pa2Rc= github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8 h1:jqsECpLRp1EAXGOdhPxHzqYjWP5l980GjJ8s/AUYH/4= github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8/go.mod h1:DaWLus8dJ4DdhVsBe5TAEEZ3IdoTMIb/z2StR4Bhb7Q= -github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c h1:LV/HjQRJGhJiKq6huf6ywF09OW+btoo0Y96y01vVp7o= -github.com/vulsio/goval-dictionary v0.7.1-0.20220212015000-031fc960b77c/go.mod h1:BEvFNaiPCKwYWtITjn+hGJQT9N8WfigSd7NXHNnbxkI= +github.com/vulsio/goval-dictionary v0.7.1-0.20220215081041-a472884d0afa h1:WU96+6NY7WOPcZBt52ySI3SXd0RHnCBpSBC5F/VL4i8= +github.com/vulsio/goval-dictionary v0.7.1-0.20220215081041-a472884d0afa/go.mod h1:BEvFNaiPCKwYWtITjn+hGJQT9N8WfigSd7NXHNnbxkI= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= diff --git a/gost/debian.go b/gost/debian.go index bffd1094..d7197217 100644 --- a/gost/debian.go +++ b/gost/debian.go @@ -6,12 +6,13 @@ package gost import ( "encoding/json" + debver "github.com/knqyf263/go-deb-version" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - debver "github.com/knqyf263/go-deb-version" gostmodels "github.com/vulsio/gost/models" - "golang.org/x/xerrors" ) // Debian is Gost client for Debian GNU/Linux @@ -67,7 +68,7 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error } nFixedCVEs, err := deb.detectCVEsWithFixState(r, "resolved") if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to detect fixed CVEs. err: %w", err) } if stashLinuxPackage.Name != "" { @@ -75,7 +76,7 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error } nUnfixedCVEs, err := deb.detectCVEsWithFixState(r, "open") if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to detect unfixed CVEs. err: %w", err) } return (nFixedCVEs + nUnfixedCVEs), nil @@ -87,22 +88,25 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string) } packCvesList := []packCves{} - if deb.DBDriver.Cnf.IsFetchViaHTTP() { - url, _ := util.URLPathJoin(deb.DBDriver.Cnf.GetURL(), "debian", major(r.Release), "pkgs") + if deb.driver == nil { + url, err := util.URLPathJoin(deb.baseURL, "debian", major(r.Release), "pkgs") + if err != nil { + return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err) + } + s := "unfixed-cves" if s == "resolved" { s = "fixed-cves" } - responses, err := getCvesWithFixStateViaHTTP(r, url, s) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get CVEs via HTTP. err: %w", err) } for _, res := range responses { debCves := map[string]gostmodels.DebianCVE{} if err := json.Unmarshal([]byte(res.json), &debCves); err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err) } cves := []models.CveContent{} fixes := []models.PackageFixStatus{} @@ -118,13 +122,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string) }) } } else { - if deb.DBDriver.DB == nil { - return 0, nil - } for _, pack := range r.Packages { cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get CVEs for Package. err: %w", err) } packCvesList = append(packCvesList, packCves{ packName: pack.Name, @@ -138,7 +139,7 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string) for _, pack := range r.SrcPackages { cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get CVEs for SrcPackage. err: %w", err) } packCvesList = append(packCvesList, packCves{ packName: pack.Name, @@ -239,11 +240,11 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string) func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err error) { vera, err := debver.NewVersion(versionRelease) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", versionRelease, err) } verb, err := debver.NewVersion(gostVersion) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", gostVersion, err) } return vera.LessThan(verb), nil } @@ -251,13 +252,13 @@ func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err e func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) ([]models.CveContent, []models.PackageFixStatus, error) { var f func(string, string) (map[string]gostmodels.DebianCVE, error) if fixStatus == "resolved" { - f = deb.DBDriver.DB.GetFixedCvesDebian + f = deb.driver.GetFixedCvesDebian } else { - f = deb.DBDriver.DB.GetUnfixedCvesDebian + f = deb.driver.GetUnfixedCvesDebian } debCves, err := f(release, pkgName) if err != nil { - return nil, nil, err + return nil, nil, xerrors.Errorf("Failed to get CVEs. fixStatus: %s, release: %s, src package: %s, err: %w", fixStatus, release, pkgName, err) } cves := []models.CveContent{} diff --git a/gost/gost.go b/gost/gost.go index 97e373a7..372cb634 100644 --- a/gost/gost.go +++ b/gost/gost.go @@ -4,22 +4,17 @@ package gost import ( - "github.com/future-architect/vuls/config" - "github.com/future-architect/vuls/logging" - "github.com/future-architect/vuls/models" - "github.com/vulsio/gost/db" "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/constant" + "github.com/future-architect/vuls/logging" + "github.com/future-architect/vuls/models" + gostdb "github.com/vulsio/gost/db" + gostlog "github.com/vulsio/gost/util" ) -// DBDriver is a DB Driver -type DBDriver struct { - DB db.DB - Cnf config.VulnDictInterface -} - -// Client is the interface of OVAL client. +// Client is the interface of Gost client. type Client interface { DetectCVEs(*models.ScanResult, bool) (int, error) CloseDB() error @@ -27,74 +22,79 @@ type Client interface { // Base is a base struct type Base struct { - DBDriver DBDriver + driver gostdb.DB + baseURL string } // CloseDB close a DB connection func (b Base) CloseDB() error { - if b.DBDriver.DB == nil { + if b.driver == nil { return nil } - return b.DBDriver.DB.CloseDB() + return b.driver.CloseDB() } // FillCVEsWithRedHat fills CVE detailed with Red Hat Security -func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf) error { - db, locked, err := newGostDB(cnf) - if locked { - return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { +func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf, o logging.LogOpts) error { + if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { return err } + + db, err := newGostDB(&cnf) + if err != nil { + return xerrors.Errorf("Failed to newGostDB. err: %w", err) + } + + client := RedHat{Base{driver: db, baseURL: cnf.GetURL()}} defer func() { - if db != nil { - if err := db.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } + if err := client.CloseDB(); err != nil { + logging.Log.Errorf("Failed to close DB. err: %+v", err) } }() - return RedHat{Base{DBDriver{DB: db, Cnf: &cnf}}}.fillCvesWithRedHatAPI(r) + return client.fillCvesWithRedHatAPI(r) } // NewClient make Client by family -func NewClient(cnf config.GostConf, family string) (Client, error) { - db, locked, err := newGostDB(cnf) - if locked { - return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) - } else if err != nil { - return nil, err +func NewGostClient(cnf config.GostConf, family string, o logging.LogOpts) (Client, error) { + if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { + return nil, xerrors.Errorf("Failed to set gost logger. err: %w", err) } - driver := DBDriver{DB: db, Cnf: &cnf} + db, err := newGostDB(&cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newGostDB. err: %w", err) + } + base := Base{driver: db, baseURL: cnf.GetURL()} switch family { case constant.RedHat, constant.CentOS, constant.Rocky, constant.Alma: - return RedHat{Base{DBDriver: driver}}, nil + return RedHat{base}, nil case constant.Debian, constant.Raspbian: - return Debian{Base{DBDriver: driver}}, nil + return Debian{base}, nil case constant.Ubuntu: - return Ubuntu{Base{DBDriver: driver}}, nil + return Ubuntu{base}, nil case constant.Windows: - return Microsoft{Base{DBDriver: driver}}, nil + return Microsoft{base}, nil default: - return Pseudo{Base{DBDriver: driver}}, nil + return Pseudo{base}, nil } } // NewGostDB returns db client for Gost -func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) { +func newGostDB(cnf config.VulnDictInterface) (gostdb.DB, error) { if cnf.IsFetchViaHTTP() { - return nil, false, nil + return nil, nil } path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}); err != nil { + driver, locked, err := gostdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), gostdb.Option{}) + if err != nil { if locked { - return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err) + return nil, xerrors.Errorf("Failed to init gost DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) } - return nil, false, err + return nil, xerrors.Errorf("Failed to init gost DB. DB Path: %s, err: %w", path, err) } - return driver, false, nil + return driver, nil } diff --git a/gost/microsoft.go b/gost/microsoft.go index c33ab95b..72091c2a 100644 --- a/gost/microsoft.go +++ b/gost/microsoft.go @@ -18,14 +18,14 @@ type Microsoft struct { // DetectCVEs fills cve information that has in Gost func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) { - if ms.DBDriver.DB == nil { + if ms.driver == nil { return 0, nil } cveIDs := []string{} for cveID := range r.ScannedCves { cveIDs = append(cveIDs, cveID) } - msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs) + msCves, err := ms.driver.GetMicrosoftMulti(cveIDs) if err != nil { return 0, nil } @@ -34,7 +34,7 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err continue } cveCont, mitigations := ms.ConvertToModel(&msCve) - v, _ := r.ScannedCves[cveID] + v := r.ScannedCves[cveID] if v.CveContents == nil { v.CveContents = models.CveContents{} } diff --git a/gost/pseudo.go b/gost/pseudo.go index 880705cb..0c1c6a31 100644 --- a/gost/pseudo.go +++ b/gost/pseudo.go @@ -7,7 +7,7 @@ import ( "github.com/future-architect/vuls/models" ) -// Pseudo is Gost client except for RedHat family and Debian +// Pseudo is Gost client except for RedHat family, Debian, Ubuntu and Windows type Pseudo struct { Base } diff --git a/gost/redhat.go b/gost/redhat.go index 441a8d32..1c2d1b26 100644 --- a/gost/redhat.go +++ b/gost/redhat.go @@ -8,7 +8,8 @@ import ( "strconv" "strings" - "github.com/future-architect/vuls/config" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" @@ -26,17 +27,20 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs if r.Family == constant.CentOS { gostRelease = strings.TrimPrefix(r.Release, "stream") } - if red.DBDriver.Cnf.IsFetchViaHTTP() { - prefix, _ := util.URLPathJoin(red.DBDriver.Cnf.GetURL(), "redhat", major(gostRelease), "pkgs") + if red.driver == nil { + prefix, err := util.URLPathJoin(red.baseURL, "redhat", major(gostRelease), "pkgs") + if err != nil { + return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err) + } responses, err := getAllUnfixedCvesViaHTTP(r, prefix) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err) } for _, res := range responses { // CVE-ID: RedhatCVE cves := map[string]gostmodels.RedhatCVE{} if err := json.Unmarshal([]byte(res.json), &cves); err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err) } for _, cve := range cves { if newly := red.setUnfixedCveToScanResult(&cve, r); newly { @@ -45,14 +49,11 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs } } } else { - if red.DBDriver.DB == nil { - return 0, nil - } for _, pack := range r.Packages { // CVE-ID: RedhatCVE - cves, err := red.DBDriver.DB.GetUnfixedCvesRedhat(major(gostRelease), pack.Name, ignoreWillNotFix) + cves, err := red.driver.GetUnfixedCvesRedhat(major(gostRelease), pack.Name, ignoreWillNotFix) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Unfixed CVEs. err: %w", err) } for _, cve := range cves { if newly := red.setUnfixedCveToScanResult(&cve, r); newly { @@ -73,8 +74,11 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error { cveIDs = append(cveIDs, cveID) } - if red.DBDriver.Cnf.IsFetchViaHTTP() { - prefix, _ := util.URLPathJoin(config.Conf.Gost.URL, "redhat", "cves") + if red.driver == nil { + prefix, err := util.URLPathJoin(red.baseURL, "redhat", "cves") + if err != nil { + return err + } responses, err := getCvesViaHTTP(cveIDs, prefix) if err != nil { return err @@ -90,10 +94,7 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error { red.setFixedCveToScanResult(&redCve, r) } } else { - if red.DBDriver.DB == nil { - return nil - } - redCves, err := red.DBDriver.DB.GetRedhatMulti(cveIDs) + redCves, err := red.driver.GetRedhatMulti(cveIDs) if err != nil { return err } diff --git a/gost/ubuntu.go b/gost/ubuntu.go index a642b745..3730b44b 100644 --- a/gost/ubuntu.go +++ b/gost/ubuntu.go @@ -7,6 +7,8 @@ import ( "encoding/json" "strings" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" @@ -53,17 +55,20 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error } packCvesList := []packCves{} - if ubu.DBDriver.Cnf.IsFetchViaHTTP() { - url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs") + if ubu.driver == nil { + url, err := util.URLPathJoin(ubu.baseURL, "ubuntu", ubuReleaseVer, "pkgs") + if err != nil { + return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err) + } responses, err := getAllUnfixedCvesViaHTTP(r, url) if err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err) } for _, res := range responses { ubuCves := map[string]gostmodels.UbuntuCVE{} if err := json.Unmarshal([]byte(res.json), &ubuCves); err != nil { - return 0, err + return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err) } cves := []models.CveContent{} for _, ubucve := range ubuCves { @@ -76,13 +81,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error }) } } else { - if ubu.DBDriver.DB == nil { - return 0, nil - } for _, pack := range r.Packages { - ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) + ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) if err != nil { - return 0, nil + return 0, xerrors.Errorf("Failed to get Unfixed CVEs For Package. err: %w", err) } cves := []models.CveContent{} for _, ubucve := range ubuCves { @@ -97,9 +99,9 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error // SrcPack for _, pack := range r.SrcPackages { - ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) + ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) if err != nil { - return 0, nil + return 0, xerrors.Errorf("Failed to get Unfixed CVEs For SrcPackage. err: %w", err) } cves := []models.CveContent{} for _, ubucve := range ubuCves { diff --git a/oval/alpine.go b/oval/alpine.go index 3657fc44..30b786e2 100644 --- a/oval/alpine.go +++ b/oval/alpine.go @@ -4,10 +4,12 @@ package oval import ( - "github.com/future-architect/vuls/config" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" + ovaldb "github.com/vulsio/goval-dictionary/db" ) // Alpine is the struct of Alpine Linux @@ -16,11 +18,12 @@ type Alpine struct { } // NewAlpine creates OVAL client for SUSE -func NewAlpine(cnf config.VulnDictInterface) Alpine { +func NewAlpine(driver ovaldb.DB, baseURL string) Alpine { return Alpine{ Base{ - family: constant.Alpine, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Alpine, }, } } @@ -28,23 +31,13 @@ func NewAlpine(cnf config.VulnDictInterface) Alpine { // FillWithOval returns scan result after updating CVE info by OVAL func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) { var relatedDefs ovalResult - if o.Cnf.IsFetchViaHTTP() { - if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil { - return 0, err + if o.driver == nil { + if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err) } } else { - driver, err := newOvalDB(o.Cnf) - if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - - if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil { - return 0, err + if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err) } } for _, defPacks := range relatedDefs.entries { diff --git a/oval/debian.go b/oval/debian.go index 9489fd59..98f3d562 100644 --- a/oval/debian.go +++ b/oval/debian.go @@ -7,11 +7,13 @@ import ( "fmt" "strings" - "github.com/future-architect/vuls/config" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" + ovaldb "github.com/vulsio/goval-dictionary/db" ovalmodels "github.com/vulsio/goval-dictionary/models" ) @@ -122,12 +124,13 @@ type Debian struct { } // NewDebian creates OVAL client for Debian -func NewDebian(cnf config.VulnDictInterface) Debian { +func NewDebian(driver ovaldb.DB, baseURL string) Debian { return Debian{ DebianBase{ Base{ - family: constant.Debian, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Debian, }, }, } @@ -157,23 +160,13 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) { } var relatedDefs ovalResult - if o.Cnf.IsFetchViaHTTP() { - if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil { - return 0, err + if o.driver == nil { + if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err) } } else { - driver, err := newOvalDB(o.Cnf) - if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - - if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil { - return 0, err + if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err) } } @@ -213,12 +206,13 @@ type Ubuntu struct { } // NewUbuntu creates OVAL client for Debian -func NewUbuntu(cnf config.VulnDictInterface) Ubuntu { +func NewUbuntu(driver ovaldb.DB, baseURL string) Ubuntu { return Ubuntu{ DebianBase{ Base{ - family: constant.Ubuntu, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Ubuntu, }, }, } @@ -471,23 +465,13 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) ( } var relatedDefs ovalResult - if o.Cnf.IsFetchViaHTTP() { - if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil { - return 0, err + if o.driver == nil { + if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err) } } else { - driver, err := newOvalDB(o.Cnf) - if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - - if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil { - return 0, err + if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err) } } diff --git a/oval/oval.go b/oval/oval.go index 9b148b63..9235335c 100644 --- a/oval/oval.go +++ b/oval/oval.go @@ -8,14 +8,15 @@ import ( "strings" "time" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - "github.com/parnurzeal/gorequest" - "github.com/vulsio/goval-dictionary/db" - "golang.org/x/xerrors" + ovaldb "github.com/vulsio/goval-dictionary/db" ) // Client is the interface of OVAL client. @@ -23,51 +24,56 @@ type Client interface { FillWithOval(*models.ScanResult) (int, error) CheckIfOvalFetched(string, string) (bool, error) CheckIfOvalFresh(string, string) (bool, error) + CloseDB() error } // Base is a base struct type Base struct { - family string - Cnf config.VulnDictInterface + driver ovaldb.DB + baseURL string + family string +} + +// CloseDB close a DB connection +func (b Base) CloseDB() error { + if b.driver == nil { + return nil + } + return b.driver.CloseDB() } // CheckIfOvalFetched checks if oval entries are in DB by family, release. -func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) { +func (b Base) CheckIfOvalFetched(osFamily, release string) (bool, error) { ovalFamily, err := GetFamilyInOval(osFamily) if err != nil { return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) } + if ovalFamily == "" { + return false, nil + } ovalRelease := release if osFamily == constant.CentOS { ovalRelease = strings.TrimPrefix(release, "stream") } - if !b.Cnf.IsFetchViaHTTP() { - driver, err := newOvalDB(b.Cnf) - if err != nil { - return false, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - count, err := driver.CountDefs(ovalFamily, ovalRelease) + var count int + if b.driver == nil { + url, err := util.URLPathJoin(b.baseURL, "count", ovalFamily, ovalRelease) + if err != nil { + return false, xerrors.Errorf("Failed to join URLPath. err: %w", err) + } + resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End() + if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { + return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs) + } + if err := json.Unmarshal([]byte(body), &count); err != nil { + return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err) + } + } else { + count, err = b.driver.CountDefs(ovalFamily, ovalRelease) if err != nil { return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", ovalFamily, ovalRelease, err) } - logging.Log.Infof("OVAL %s %s found. defs: %d", ovalFamily, ovalRelease, count) - return 0 < count, nil - } - - url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "count", ovalFamily, ovalRelease) - resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End() - if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { - return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs) - } - count := 0 - if err := json.Unmarshal([]byte(body), &count); err != nil { - return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err) } logging.Log.Infof("OVAL %s %s found. defs: %d", ovalFamily, ovalRelease, count) return 0 < count, nil @@ -79,35 +85,32 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) { if err != nil { return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err) } + if ovalFamily == "" { + return false, nil + } ovalRelease := release if osFamily == constant.CentOS { ovalRelease = strings.TrimPrefix(release, "stream") } + var lastModified time.Time - if !b.Cnf.IsFetchViaHTTP() { - driver, err := newOvalDB(b.Cnf) + if b.driver == nil { + url, err := util.URLPathJoin(b.baseURL, "lastmodified", ovalFamily, ovalRelease) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to join URLPath. err: %w", err) } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - lastModified, err = driver.GetLastModified(ovalFamily, ovalRelease) - if err != nil { - return false, xerrors.Errorf("Failed to GetLastModified: %w", err) - } - } else { - url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "lastmodified", ovalFamily, ovalRelease) resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End() if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs) } - if err := json.Unmarshal([]byte(body), &lastModified); err != nil { return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err) } + } else { + lastModified, err = b.driver.GetLastModified(ovalFamily, ovalRelease) + if err != nil { + return false, xerrors.Errorf("Failed to GetLastModified: %w", err) + } } since := time.Now() @@ -122,23 +125,20 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) { } // NewOvalDB returns oval db client -func newOvalDB(cnf config.VulnDictInterface) (driver db.DB, err error) { +func newOvalDB(cnf config.VulnDictInterface) (ovaldb.DB, error) { if cnf.IsFetchViaHTTP() { return nil, nil } - path := cnf.GetURL() if cnf.GetType() == "sqlite3" { path = cnf.GetSQLite3Path() } - - driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}) + driver, locked, err := ovaldb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), ovaldb.Option{}) if err != nil { if locked { - err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err) + return nil, xerrors.Errorf("Failed to init OVAL DB. SQLite3: %s is locked. err: %w, ", cnf.GetSQLite3Path(), err) } - err = xerrors.Errorf("Failed to new OVAL DB. err: %w", err) - return nil, err + return nil, xerrors.Errorf("Failed to init OVAL DB. DB Path: %s, err: %w", path, err) } return driver, nil } diff --git a/oval/pseudo.go b/oval/pseudo.go new file mode 100644 index 00000000..e0a7e704 --- /dev/null +++ b/oval/pseudo.go @@ -0,0 +1,23 @@ +package oval + +import "github.com/future-architect/vuls/models" + +// Pseudo is OVAL client for Windows, FreeBSD and Pseudo +type Pseudo struct { + Base +} + +// NewPseudo creates OVAL client for Windows, FreeBSD and Pseudo +func NewPseudo(family string) Pseudo { + return Pseudo{ + Base{ + driver: nil, + baseURL: "", + family: family, + }, + } +} + +func (pse Pseudo) FillWithOval(_ *models.ScanResult) (int, error) { + return 0, nil +} diff --git a/oval/redhat.go b/oval/redhat.go index 647006b1..23f82407 100644 --- a/oval/redhat.go +++ b/oval/redhat.go @@ -7,10 +7,12 @@ import ( "fmt" "strings" - "github.com/future-architect/vuls/config" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" + ovaldb "github.com/vulsio/goval-dictionary/db" ovalmodels "github.com/vulsio/goval-dictionary/models" ) @@ -22,23 +24,13 @@ type RedHatBase struct { // FillWithOval returns scan result after updating CVE info by OVAL func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) { var relatedDefs ovalResult - if o.Cnf.IsFetchViaHTTP() { - if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil { - return 0, err + if o.driver == nil { + if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err) } } else { - driver, err := newOvalDB(o.Cnf) - if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - - if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil { - return 0, err + if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err) } } @@ -267,12 +259,13 @@ type RedHat struct { } // NewRedhat creates OVAL client for Redhat -func NewRedhat(cnf config.VulnDictInterface) RedHat { +func NewRedhat(driver ovaldb.DB, baseURL string) RedHat { return RedHat{ RedHatBase{ Base{ - family: constant.RedHat, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.RedHat, }, }, } @@ -284,12 +277,13 @@ type CentOS struct { } // NewCentOS creates OVAL client for CentOS -func NewCentOS(cnf config.VulnDictInterface) CentOS { +func NewCentOS(driver ovaldb.DB, baseURL string) CentOS { return CentOS{ RedHatBase{ Base{ - family: constant.CentOS, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.CentOS, }, }, } @@ -301,12 +295,13 @@ type Oracle struct { } // NewOracle creates OVAL client for Oracle -func NewOracle(cnf config.VulnDictInterface) Oracle { +func NewOracle(driver ovaldb.DB, baseURL string) Oracle { return Oracle{ RedHatBase{ Base{ - family: constant.Oracle, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Oracle, }, }, } @@ -319,12 +314,13 @@ type Amazon struct { } // NewAmazon creates OVAL client for Amazon Linux -func NewAmazon(cnf config.VulnDictInterface) Amazon { +func NewAmazon(driver ovaldb.DB, baseURL string) Amazon { return Amazon{ RedHatBase{ Base{ - family: constant.Amazon, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Amazon, }, }, } @@ -337,12 +333,13 @@ type Alma struct { } // NewAlma creates OVAL client for Alma Linux -func NewAlma(cnf config.VulnDictInterface) Alma { +func NewAlma(driver ovaldb.DB, baseURL string) Alma { return Alma{ RedHatBase{ Base{ - family: constant.Alma, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Alma, }, }, } @@ -355,12 +352,13 @@ type Rocky struct { } // NewRocky creates OVAL client for Rocky Linux -func NewRocky(cnf config.VulnDictInterface) Rocky { +func NewRocky(driver ovaldb.DB, baseURL string) Rocky { return Rocky{ RedHatBase{ Base{ - family: constant.Rocky, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Rocky, }, }, } @@ -373,12 +371,13 @@ type Fedora struct { } // NewFedora creates OVAL client for Fedora Linux -func NewFedora(cnf config.VulnDictInterface) Fedora { +func NewFedora(driver ovaldb.DB, baseURL string) Fedora { return Fedora{ RedHatBase{ Base{ - family: constant.Fedora, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: constant.Fedora, }, }, } diff --git a/oval/suse.go b/oval/suse.go index 21e63e72..e777cfcd 100644 --- a/oval/suse.go +++ b/oval/suse.go @@ -6,9 +6,11 @@ package oval import ( "fmt" - "github.com/future-architect/vuls/config" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" + ovaldb "github.com/vulsio/goval-dictionary/db" ovalmodels "github.com/vulsio/goval-dictionary/models" ) @@ -18,11 +20,13 @@ type SUSE struct { } // NewSUSE creates OVAL client for SUSE -func NewSUSE(cnf config.VulnDictInterface, family string) SUSE { +func NewSUSE(driver ovaldb.DB, baseURL, family string) SUSE { + // TODO implement other family return SUSE{ Base{ - family: family, - Cnf: cnf, + driver: driver, + baseURL: baseURL, + family: family, }, } } @@ -30,23 +34,13 @@ func NewSUSE(cnf config.VulnDictInterface, family string) SUSE { // FillWithOval returns scan result after updating CVE info by OVAL func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) { var relatedDefs ovalResult - if o.Cnf.IsFetchViaHTTP() { - if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil { - return 0, err + if o.driver == nil { + if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err) } } else { - driver, err := newOvalDB(o.Cnf) - if err != nil { - return 0, err - } - defer func() { - if err := driver.CloseDB(); err != nil { - logging.Log.Errorf("Failed to close DB. err: %+v", err) - } - }() - - if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil { - return 0, err + if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil { + return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err) } } for _, defPacks := range relatedDefs.entries { diff --git a/oval/util.go b/oval/util.go index 116c06cd..d383a112 100644 --- a/oval/util.go +++ b/oval/util.go @@ -14,18 +14,20 @@ import ( "time" "github.com/cenkalti/backoff" + apkver "github.com/knqyf263/go-apk-version" + debver "github.com/knqyf263/go-deb-version" + rpmver "github.com/knqyf263/go-rpm-version" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/constant" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - apkver "github.com/knqyf263/go-apk-version" - debver "github.com/knqyf263/go-deb-version" - rpmver "github.com/knqyf263/go-rpm-version" - "github.com/parnurzeal/gorequest" - "github.com/vulsio/goval-dictionary/db" + ovaldb "github.com/vulsio/goval-dictionary/db" + ovallog "github.com/vulsio/goval-dictionary/log" ovalmodels "github.com/vulsio/goval-dictionary/models" - "golang.org/x/xerrors" ) type ovalResult struct { @@ -245,7 +247,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er } } -func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDefs ovalResult, err error) { +func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relatedDefs ovalResult, err error) { requests := []request{} for _, pack := range r.Packages { requests = append(requests, request{ @@ -441,22 +443,22 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error constant.Raspbian: vera, err := debver.NewVersion(newVer) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err) } verb, err := debver.NewVersion(packInOVAL.Version) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err) } return vera.LessThan(verb), nil case constant.Alpine: vera, err := apkver.NewVersion(newVer) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err) } verb, err := apkver.NewVersion(packInOVAL.Version) if err != nil { - return false, err + return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err) } return vera.LessThan(verb), nil @@ -491,40 +493,49 @@ func rhelRebuildOSVersionToRHEL(ver string) string { } // NewOVALClient returns a client for OVAL database -func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) { +func NewOVALClient(family string, cnf config.GovalDictConf, o logging.LogOpts) (Client, error) { + if err := ovallog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil { + return nil, xerrors.Errorf("Failed to set goval-dictionary logger. err: %w", err) + } + + driver, err := newOvalDB(&cnf) + if err != nil { + return nil, xerrors.Errorf("Failed to newOvalDB. err: %w", err) + } + switch family { case constant.Debian, constant.Raspbian: - return NewDebian(&cnf), nil + return NewDebian(driver, cnf.GetURL()), nil case constant.Ubuntu: - return NewUbuntu(&cnf), nil + return NewUbuntu(driver, cnf.GetURL()), nil case constant.RedHat: - return NewRedhat(&cnf), nil + return NewRedhat(driver, cnf.GetURL()), nil case constant.CentOS: - return NewCentOS(&cnf), nil + return NewCentOS(driver, cnf.GetURL()), nil case constant.Alma: - return NewAlma(&cnf), nil + return NewAlma(driver, cnf.GetURL()), nil case constant.Rocky: - return NewRocky(&cnf), nil + return NewRocky(driver, cnf.GetURL()), nil case constant.Oracle: - return NewOracle(&cnf), nil + return NewOracle(driver, cnf.GetURL()), nil case constant.OpenSUSE: - return NewSUSE(&cnf, constant.OpenSUSE), nil + return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSE), nil case constant.OpenSUSELeap: - return NewSUSE(&cnf, constant.OpenSUSELeap), nil + return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSELeap), nil case constant.SUSEEnterpriseServer: - return NewSUSE(&cnf, constant.SUSEEnterpriseServer), nil + return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseServer), nil case constant.SUSEEnterpriseDesktop: - return NewSUSE(&cnf, constant.SUSEEnterpriseDesktop), nil + return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseDesktop), nil case constant.Alpine: - return NewAlpine(&cnf), nil + return NewAlpine(driver, cnf.GetURL()), nil case constant.Amazon: - return NewAmazon(&cnf), nil + return NewAmazon(driver, cnf.GetURL()), nil case constant.Fedora: - return NewFedora(&cnf), nil + return NewFedora(driver, cnf.GetURL()), nil case constant.FreeBSD, constant.Windows: - return nil, nil + return NewPseudo(family), nil case constant.ServerTypePseudo: - return nil, nil + return NewPseudo(family), nil default: if family == "" { return nil, xerrors.New("Probably an error occurred during scanning. Check the error message") diff --git a/server/server.go b/server/server.go index 6be2755b..06472076 100644 --- a/server/server.go +++ b/server/server.go @@ -62,14 +62,14 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil { + if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil { logging.Log.Errorf("Failed to detect Pkg CVE: %+v", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) return } logging.Log.Infof("Fill CVE detailed with gost") - if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil { + if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil { logging.Log.Errorf("Failed to fill with gost: %+v", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) } @@ -80,21 +80,21 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { http.Error(w, err.Error(), http.StatusServiceUnavailable) } - nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit) + nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts) if err != nil { logging.Log.Errorf("Failed to fill with exploit: %+v", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) } logging.Log.Infof("%s: %d PoC detected", r.FormatServerName(), nExploitCve) - nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit) + nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts) if err != nil { logging.Log.Errorf("Failed to fill with metasploit: %+v", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) } logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve) - if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln); err != nil { + if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil { logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) } diff --git a/subcmds/report.go b/subcmds/report.go index 58428ba1..7c424296 100644 --- a/subcmds/report.go +++ b/subcmds/report.go @@ -175,10 +175,24 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath); err != nil { - logging.Log.Errorf("Error loading %s, %+v", p.configPath, err) - return subcommands.ExitUsageError + if p.configPath == "" { + for _, cnf := range []config.VulnDictInterface{ + &config.Conf.CveDict, + &config.Conf.OvalDict, + &config.Conf.Gost, + &config.Conf.Exploit, + &config.Conf.Metasploit, + &config.Conf.KEVuln, + } { + cnf.Init() + } + } else { + if err := config.Load(p.configPath); err != nil { + logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err) + return subcommands.ExitUsageError + } } + config.Conf.Slack.Enabled = p.toSlack config.Conf.ChatWork.Enabled = p.toChatWork config.Conf.GoogleChat.Enabled = p.toGoogleChat diff --git a/subcmds/server.go b/subcmds/server.go index 9b1bb179..e2c46b2a 100644 --- a/subcmds/server.go +++ b/subcmds/server.go @@ -11,10 +11,11 @@ import ( "os" "path/filepath" + "github.com/google/subcommands" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/server" - "github.com/google/subcommands" ) // ServerCmd is subcommand for server @@ -93,9 +94,23 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) { func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath); err != nil { - logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err) - return subcommands.ExitUsageError + + if p.configPath == "" { + for _, cnf := range []config.VulnDictInterface{ + &config.Conf.CveDict, + &config.Conf.OvalDict, + &config.Conf.Gost, + &config.Conf.Exploit, + &config.Conf.Metasploit, + &config.Conf.KEVuln, + } { + cnf.Init() + } + } else { + if err := config.Load(p.configPath); err != nil { + logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err) + return subcommands.ExitUsageError + } } logging.Log.Info("Validating config...")