From 2cdeef4ffee0352abc3c2ba2301a4de34b3636d4 Mon Sep 17 00:00:00 2001 From: Kota Kanbe Date: Thu, 25 Feb 2021 07:41:49 +0900 Subject: [PATCH] refactor(config): validateOnReport (#1182) --- config/config.go | 49 ++++----- detector/github.go | 1 - reporter/util.go | 255 +++++++++++++++++++++++---------------------- 3 files changed, 146 insertions(+), 159 deletions(-) diff --git a/config/config.go b/config/config.go index 98bc90d5..b6722c39 100644 --- a/config/config.go +++ b/config/config.go @@ -58,6 +58,11 @@ type Config struct { ReportOpts } +// ReportConf is an interface to Validate Report Config +type ReportConf interface { + Validate() []error +} + // ScanOpts is options for scan type ScanOpts struct { Vvv bool `json:"vvv,omitempty"` @@ -175,37 +180,19 @@ func (c Config) ValidateOnReport() bool { errs = append(errs, err) } - //TODO refactor interface - if es := c.EMail.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.Slack.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.ChatWork.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.Telegram.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.Syslog.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.HTTP.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.AWS.Validate(); 0 < len(es) { - errs = append(errs, es...) - } - - if es := c.Azure.Validate(); 0 < len(es) { - errs = append(errs, es...) + for _, rc := range []ReportConf{ + &c.EMail, + &c.Slack, + &c.ChatWork, + &c.Telegram, + &c.Syslog, + &c.HTTP, + &c.AWS, + &c.Azure, + } { + if es := rc.Validate(); 0 < len(es) { + errs = append(errs, es...) + } } for _, err := range errs { diff --git a/detector/github.go b/detector/github.go index a012ba97..cf8d61c2 100644 --- a/detector/github.go +++ b/detector/github.go @@ -16,7 +16,6 @@ import ( // DetectGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult. // https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/ -//TODO move to report func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string, ignoreDismissed bool) (nCVEs int, err error) { src := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: token}, diff --git a/reporter/util.go b/reporter/util.go index 8f0d417d..2c34e69b 100644 --- a/reporter/util.go +++ b/reporter/util.go @@ -25,6 +25,134 @@ const ( maxColWidth = 100 ) +// OverwriteJSONFile overwrites scanresults JSON in the dir +func OverwriteJSONFile(dir string, r models.ScanResult) error { + w := LocalFileWriter{ + CurrentDir: dir, + FormatJSON: true, + } + if err := w.Write(r); err != nil { + return xerrors.Errorf("Failed to write summary report: %w", err) + } + return nil +} + +// LoadScanResults read JSON data +func LoadScanResults(jsonDir string) (results models.ScanResults, err error) { + var files []os.FileInfo + if files, err = ioutil.ReadDir(jsonDir); err != nil { + return nil, xerrors.Errorf("Failed to read %s: %w", jsonDir, err) + } + for _, f := range files { + if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") { + continue + } + + var r *models.ScanResult + path := filepath.Join(jsonDir, f.Name()) + if r, err = loadOneServerScanResult(path); err != nil { + return nil, err + } + results = append(results, *r) + } + if len(results) == 0 { + return nil, xerrors.Errorf("There is no json file under %s", jsonDir) + } + return +} + +// loadOneServerScanResult read JSON data of one server +func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) { + var ( + data []byte + err error + ) + if data, err = ioutil.ReadFile(jsonFile); err != nil { + return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err) + } + result := &models.ScanResult{} + if err := json.Unmarshal(data, result); err != nil { + return nil, xerrors.Errorf("Failed to parse %s: %w", jsonFile, err) + } + return result, nil +} + +// jsonDirPattern is file name pattern of JSON directory +// 2016-11-16T10:43:28+09:00 +// 2016-11-16T10:43:28Z +var jsonDirPattern = regexp.MustCompile( + `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`) + +// ListValidJSONDirs returns valid json directory as array +// Returned array is sorted so that recent directories are at the head +func ListValidJSONDirs() (dirs []string, err error) { + var dirInfo []os.FileInfo + if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil { + err = xerrors.Errorf("Failed to read %s: %w", + config.Conf.ResultsDir, err) + return + } + for _, d := range dirInfo { + if d.IsDir() && jsonDirPattern.MatchString(d.Name()) { + jsonDir := filepath.Join(config.Conf.ResultsDir, d.Name()) + dirs = append(dirs, jsonDir) + } + } + sort.Slice(dirs, func(i, j int) bool { + return dirs[j] < dirs[i] + }) + return +} + +// JSONDir returns +// If there is an arg, check if it is a valid format and return the corresponding path under results. +// If arg passed via PIPE (such as history subcommand), return that path. +// Otherwise, returns the path of the latest directory +func JSONDir(args []string) (string, error) { + var err error + var dirs []string + + if 0 < len(args) { + if dirs, err = ListValidJSONDirs(); err != nil { + return "", err + } + + path := filepath.Join(config.Conf.ResultsDir, args[0]) + for _, d := range dirs { + ss := strings.Split(d, string(os.PathSeparator)) + timedir := ss[len(ss)-1] + if timedir == args[0] { + return path, nil + } + } + + return "", xerrors.Errorf("Invalid path: %s", path) + } + + // PIPE + if config.Conf.Pipe { + bytes, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return "", xerrors.Errorf("Failed to read stdin: %w", err) + } + fields := strings.Fields(string(bytes)) + if 0 < len(fields) { + return filepath.Join(config.Conf.ResultsDir, fields[0]), nil + } + return "", xerrors.Errorf("Stdin is invalid: %s", string(bytes)) + } + + // returns latest dir when no args or no PIPE + if dirs, err = ListValidJSONDirs(); err != nil { + return "", err + } + if len(dirs) == 0 { + return "", xerrors.Errorf("No results under %s", + config.Conf.ResultsDir) + } + return dirs[0], nil +} + func formatScanSummary(rs ...models.ScanResult) string { table := uitable.New() table.MaxColWidth = maxColWidth @@ -428,17 +556,6 @@ func cweJvnURL(cweID string) string { return fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID) } -func OverwriteJSONFile(dir string, r models.ScanResult) error { - w := LocalFileWriter{ - CurrentDir: dir, - FormatJSON: true, - } - if err := w.Write(r); err != nil { - return xerrors.Errorf("Failed to write summary report: %w", err) - } - return nil -} - func diff(curResults, preResults models.ScanResults, isPlus, isMinus bool) (diffed models.ScanResults) { for _, current := range curResults { found := false @@ -591,119 +708,3 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool { } return false } - -// jsonDirPattern is file name pattern of JSON directory -// 2016-11-16T10:43:28+09:00 -// 2016-11-16T10:43:28Z -var jsonDirPattern = regexp.MustCompile( - `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`) - -// ListValidJSONDirs returns valid json directory as array -// Returned array is sorted so that recent directories are at the head -func ListValidJSONDirs() (dirs []string, err error) { - var dirInfo []os.FileInfo - if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil { - err = xerrors.Errorf("Failed to read %s: %w", - config.Conf.ResultsDir, err) - return - } - for _, d := range dirInfo { - if d.IsDir() && jsonDirPattern.MatchString(d.Name()) { - jsonDir := filepath.Join(config.Conf.ResultsDir, d.Name()) - dirs = append(dirs, jsonDir) - } - } - sort.Slice(dirs, func(i, j int) bool { - return dirs[j] < dirs[i] - }) - return -} - -// JSONDir returns -// If there is an arg, check if it is a valid format and return the corresponding path under results. -// If arg passed via PIPE (such as history subcommand), return that path. -// Otherwise, returns the path of the latest directory -func JSONDir(args []string) (string, error) { - var err error - var dirs []string - - if 0 < len(args) { - if dirs, err = ListValidJSONDirs(); err != nil { - return "", err - } - - path := filepath.Join(config.Conf.ResultsDir, args[0]) - for _, d := range dirs { - ss := strings.Split(d, string(os.PathSeparator)) - timedir := ss[len(ss)-1] - if timedir == args[0] { - return path, nil - } - } - - return "", xerrors.Errorf("Invalid path: %s", path) - } - - // PIPE - if config.Conf.Pipe { - bytes, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return "", xerrors.Errorf("Failed to read stdin: %w", err) - } - fields := strings.Fields(string(bytes)) - if 0 < len(fields) { - return filepath.Join(config.Conf.ResultsDir, fields[0]), nil - } - return "", xerrors.Errorf("Stdin is invalid: %s", string(bytes)) - } - - // returns latest dir when no args or no PIPE - if dirs, err = ListValidJSONDirs(); err != nil { - return "", err - } - if len(dirs) == 0 { - return "", xerrors.Errorf("No results under %s", - config.Conf.ResultsDir) - } - return dirs[0], nil -} - -// LoadScanResults read JSON data -func LoadScanResults(jsonDir string) (results models.ScanResults, err error) { - var files []os.FileInfo - if files, err = ioutil.ReadDir(jsonDir); err != nil { - return nil, xerrors.Errorf("Failed to read %s: %w", jsonDir, err) - } - for _, f := range files { - if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") { - continue - } - - var r *models.ScanResult - path := filepath.Join(jsonDir, f.Name()) - if r, err = loadOneServerScanResult(path); err != nil { - return nil, err - } - results = append(results, *r) - } - if len(results) == 0 { - return nil, xerrors.Errorf("There is no json file under %s", jsonDir) - } - return -} - -// loadOneServerScanResult read JSON data of one server -func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) { - var ( - data []byte - err error - ) - if data, err = ioutil.ReadFile(jsonFile); err != nil { - return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err) - } - result := &models.ScanResult{} - if err := json.Unmarshal(data, result); err != nil { - return nil, xerrors.Errorf("Failed to parse %s: %w", jsonFile, err) - } - return result, nil -}