diff --git a/commands/configtest.go b/commands/configtest.go index f8b8f788ad..4a4f5da26e 100644 --- a/commands/configtest.go +++ b/commands/configtest.go @@ -23,7 +23,6 @@ import ( "os" "path/filepath" - "github.com/Sirupsen/logrus" "github.com/google/subcommands" c "github.com/future-architect/vuls/config" @@ -88,26 +87,27 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + // Setup Logger + c.Conf.Debug = p.debug + c.Conf.LogDir = p.logDir + util.Log = util.NewCustomLogger(c.ServerInfo{}) var keyPass string var err error if p.askKeyPassword { prompt := "SSH key password: " if keyPass, err = getPasswd(prompt); err != nil { - logrus.Error(err) + util.Log.Error(err) return subcommands.ExitFailure } } - c.Conf.Debug = p.debug - c.Conf.SSHExternal = p.sshExternal - c.Conf.LogDir = p.logDir - err = c.Load(p.configPath, keyPass) if err != nil { - logrus.Errorf("Error loading %s, %s", p.configPath, err) + util.Log.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } + c.Conf.SSHExternal = p.sshExternal var servernames []string if 0 < len(f.Args()) { @@ -125,7 +125,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa } } if !found { - logrus.Errorf("%s is not in config", arg) + util.Log.Errorf("%s is not in config", arg) return subcommands.ExitUsageError } } @@ -133,25 +133,20 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa c.Conf.Servers = target } - // logger - Log := util.NewCustomLogger(c.ServerInfo{}) - - Log.Info("Validating Config...") + util.Log.Info("Validating config...") if !c.Conf.ValidateOnConfigtest() { return subcommands.ExitUsageError } - Log.Info("Detecting Server/Container OS... ") - if err := scan.InitServers(Log); err != nil { - Log.Errorf("Failed to init servers: %s", err) + util.Log.Info("Detecting Server/Container OS... ") + if err := scan.InitServers(); err != nil { + util.Log.Errorf("Failed to init servers: %s", err) return subcommands.ExitFailure } - Log.Info("Checking sudo configuration... ") - if err := scan.CheckIfSudoNoPasswd(Log); err != nil { - Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers. err: %s", err) - return subcommands.ExitFailure - } + util.Log.Info("Checking sudo configuration...") + scan.CheckIfSudoNoPasswd() + scan.PrintSSHableServerNames() return subcommands.ExitSuccess } diff --git a/commands/prepare.go b/commands/prepare.go index 0fa70ef7b9..b582fba923 100644 --- a/commands/prepare.go +++ b/commands/prepare.go @@ -23,7 +23,6 @@ import ( "os" "path/filepath" - "github.com/Sirupsen/logrus" c "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/scan" "github.com/future-architect/vuls/util" @@ -116,27 +115,31 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + c.Conf.LogDir = p.logDir + c.Conf.Debug = p.debug + util.Log = util.NewCustomLogger(c.ServerInfo{}) + var keyPass string var err error if p.askKeyPassword { prompt := "SSH key password: " if keyPass, err = getPasswd(prompt); err != nil { - logrus.Error(err) + util.Log.Error(err) return subcommands.ExitFailure } } if p.askSudoPassword { - logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication") + util.Log.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication") return subcommands.ExitFailure } err = c.Load(p.configPath, keyPass) if err != nil { - logrus.Errorf("Error loading %s, %s", p.configPath, err) + util.Log.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } - logrus.Infof("Start Preparing (config: %s)", p.configPath) + util.Log.Infof("Start Preparing (config: %s)", p.configPath) target := make(map[string]c.ServerInfo) for _, arg := range f.Args() { found := false @@ -148,7 +151,7 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{ } } if !found { - logrus.Errorf("%s is not in config", arg) + util.Log.Errorf("%s is not in config", arg) return subcommands.ExitUsageError } } @@ -156,34 +159,25 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{ c.Conf.Servers = target } - c.Conf.Debug = p.debug c.Conf.SSHExternal = p.sshExternal c.Conf.AssumeYes = p.assumeYes - c.Conf.LogDir = p.logDir - logrus.Info("Validating Config...") + util.Log.Info("Validating config...") if !c.Conf.ValidateOnPrepare() { return subcommands.ExitUsageError } - // Set up custom logger - logger := util.NewCustomLogger(c.ServerInfo{}) - logger.Info("Detecting OS... ") - if err := scan.InitServers(logger); err != nil { - logger.Errorf("Failed to init servers: %s", err) + util.Log.Info("Detecting OS... ") + if err := scan.InitServers(); err != nil { + util.Log.Errorf("Failed to init servers: %s", err) return subcommands.ExitFailure } - logger.Info("Checking sudo configuration... ") - if err := scan.CheckIfSudoNoPasswd(logger); err != nil { - logger.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers") - return subcommands.ExitFailure - } + util.Log.Info("Checking sudo configuration... ") + scan.CheckIfSudoNoPasswd() - if errs := scan.Prepare(); 0 < len(errs) { - for _, e := range errs { - logger.Errorf("Failed to prepare: %s", e) - } + if err := scan.Prepare(); err != nil { + util.Log.Errorf("Failed to prepare: %s", err) return subcommands.ExitFailure } diff --git a/commands/report.go b/commands/report.go index ed890bfead..ba327a5c24 100644 --- a/commands/report.go +++ b/commands/report.go @@ -249,10 +249,10 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} c.Conf.Debug = p.debug c.Conf.DebugSQL = p.debugSQL c.Conf.LogDir = p.logDir - Log := util.NewCustomLogger(c.ServerInfo{}) + util.Log = util.NewCustomLogger(c.ServerInfo{}) if err := c.Load(p.configPath, ""); err != nil { - Log.Errorf("Error loading %s, %s", p.configPath, err) + util.Log.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } @@ -268,7 +268,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} c.Conf.Pipe = p.pipe jsonDir, err := jsonDir(f.Args()) if err != nil { - Log.Errorf("Failed to read from JSON: %s", err) + util.Log.Errorf("Failed to read from JSON: %s", err) return subcommands.ExitFailure } @@ -304,7 +304,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} c.Conf.AwsProfile = p.awsProfile c.Conf.S3Bucket = p.awsS3Bucket if err := report.CheckIfBucketExists(); err != nil { - Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err) + util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err) return subcommands.ExitUsageError } reports = append(reports, report.S3Writer{}) @@ -323,11 +323,11 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} c.Conf.AzureContainer = p.azureContainer if len(c.Conf.AzureContainer) == 0 { - Log.Error("Azure storage container name is requied with --azure-container option") + util.Log.Error("Azure storage container name is requied with --azure-container option") return subcommands.ExitUsageError } if err := report.CheckIfAzureContainerExists(); err != nil { - Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err) + util.Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err) return subcommands.ExitUsageError } reports = append(reports, report.AzureBlobWriter{}) @@ -338,20 +338,20 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} c.Conf.FormatShortText = true } - Log.Info("Validating Config...") + util.Log.Info("Validating config...") if !c.Conf.ValidateOnReport() { return subcommands.ExitUsageError } if ok, err := cveapi.CveClient.CheckHealth(); !ok { - Log.Errorf("CVE HTTP server is not running. err: %s", err) - Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with --cvedb-path option") + 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 } if c.Conf.CveDictionaryURL != "" { - Log.Infof("cve-dictionary: %s", c.Conf.CveDictionaryURL) + util.Log.Infof("cve-dictionary: %s", c.Conf.CveDictionaryURL) } else { if c.Conf.CveDBType == "sqlite3" { - Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath) + util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath) } } @@ -360,10 +360,10 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} var results []models.ScanResult for _, r := range history.ScanResults { if p.refreshCve || needToRefreshCve(r) { - Log.Debugf("need to refresh") + util.Log.Debugf("need to refresh") if c.Conf.CveDBType == "sqlite3" && c.Conf.CveDictionaryURL == "" { if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) { - Log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s", + util.Log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s", c.Conf.CveDBPath) return subcommands.ExitFailure } @@ -371,18 +371,18 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} filled, err := fillCveInfoFromCveDB(r) if err != nil { - Log.Errorf("Failed to fill CVE information: %s", err) + util.Log.Errorf("Failed to fill CVE information: %s", err) return subcommands.ExitFailure } filled.Lang = c.Conf.Lang if err := overwriteJSONFile(jsonDir, filled); err != nil { - Log.Errorf("Failed to write JSON: %s", err) + util.Log.Errorf("Failed to write JSON: %s", err) return subcommands.ExitFailure } results = append(results, filled) } else { - Log.Debugf("no need to refresh") + util.Log.Debugf("no need to refresh") results = append(results, r) } } @@ -393,7 +393,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} } for _, w := range reports { if err := w.Write(res...); err != nil { - Log.Errorf("Failed to report: %s", err) + util.Log.Errorf("Failed to report: %s", err) return subcommands.ExitFailure } } diff --git a/commands/scan.go b/commands/scan.go index 9b3fc71527..c65affdd0d 100644 --- a/commands/scan.go +++ b/commands/scan.go @@ -26,7 +26,6 @@ import ( "path/filepath" "strings" - "github.com/Sirupsen/logrus" c "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/scan" "github.com/future-architect/vuls/util" @@ -138,25 +137,30 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + + // Setup Logger + c.Conf.Debug = p.debug + c.Conf.LogDir = p.logDir + util.Log = util.NewCustomLogger(c.ServerInfo{}) + var keyPass string var err error if p.askKeyPassword { prompt := "SSH key password: " if keyPass, err = getPasswd(prompt); err != nil { - logrus.Error(err) + util.Log.Error(err) return subcommands.ExitFailure } } - c.Conf.Debug = p.debug err = c.Load(p.configPath, keyPass) if err != nil { - logrus.Errorf("Error loading %s, %s", p.configPath, err) + util.Log.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } - logrus.Info("Start scanning") - logrus.Infof("config: %s", p.configPath) + util.Log.Info("Start scanning") + util.Log.Infof("config: %s", p.configPath) c.Conf.Pipe = p.pipe var servernames []string @@ -165,7 +169,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) } else if c.Conf.Pipe { bytes, err := ioutil.ReadAll(os.Stdin) if err != nil { - logrus.Errorf("Failed to read stdin: %s", err) + util.Log.Errorf("Failed to read stdin: %s", err) return subcommands.ExitFailure } fields := strings.Fields(string(bytes)) @@ -185,18 +189,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) } } if !found { - logrus.Errorf("%s is not in config", arg) + util.Log.Errorf("%s is not in config", arg) return subcommands.ExitUsageError } } if 0 < len(servernames) { c.Conf.Servers = target } - logrus.Debugf("%s", pp.Sprintf("%v", target)) - - // logger - c.Conf.LogDir = p.logDir - Log := util.NewCustomLogger(c.ServerInfo{}) + util.Log.Debugf("%s", pp.Sprintf("%v", target)) c.Conf.ResultsDir = p.resultsDir c.Conf.CacheDBPath = p.cacheDBPath @@ -205,31 +205,26 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) c.Conf.ContainersOnly = p.containersOnly c.Conf.SkipBroken = p.skipBroken - Log.Info("Validating Config...") + util.Log.Info("Validating config...") if !c.Conf.ValidateOnScan() { return subcommands.ExitUsageError } - Log.Info("Detecting Server/Container OS... ") - if err := scan.InitServers(Log); err != nil { - Log.Errorf("Failed to init servers: %s", err) + util.Log.Info("Detecting Server/Container OS... ") + if err := scan.InitServers(); err != nil { + util.Log.Errorf("Failed to init servers: %s", err) return subcommands.ExitFailure } - Log.Info("Checking sudo configuration... ") - if err := scan.CheckIfSudoNoPasswd(Log); err != nil { - Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers") - return subcommands.ExitFailure - } + util.Log.Info("Checking sudo configuration... ") + scan.CheckIfSudoNoPasswd() - Log.Info("Detecting Platforms... ") - scan.DetectPlatforms(Log) + util.Log.Info("Detecting Platforms... ") + scan.DetectPlatforms() - Log.Info("Scanning vulnerabilities... ") - if errs := scan.Scan(); 0 < len(errs) { - for _, e := range errs { - Log.Errorf("Failed to scan. err: %s", e) - } + util.Log.Info("Scanning vulnerabilities... ") + if err := scan.Scan(); err != nil { + util.Log.Errorf("Failed to scan. err: %s", err) return subcommands.ExitFailure } fmt.Printf("\n\n\n") diff --git a/commands/tui.go b/commands/tui.go index 55b4abb084..25aa5bd0c1 100644 --- a/commands/tui.go +++ b/commands/tui.go @@ -115,7 +115,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s c.Conf.CveDBPath = p.cvedbpath c.Conf.CveDictionaryURL = p.cveDictionaryURL - log.Info("Validating Config...") + log.Info("Validating config...") if !c.Conf.ValidateOnTui() { return subcommands.ExitUsageError } diff --git a/glide.lock b/glide.lock index d610a267f5..46782ff08c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: c3167d83e68562cd7ef73f138ce60cb9e60b72b50394e8615388d1f3ba9fbef2 -updated: 2017-01-02T09:37:09.437363123+09:00 +updated: 2017-02-08T11:59:59.893522816+09:00 imports: - name: github.com/asaskevich/govalidator version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877 diff --git a/models/models.go b/models/models.go index 1d329bb4c3..fc59d1e789 100644 --- a/models/models.go +++ b/models/models.go @@ -73,6 +73,7 @@ type ScanResult struct { Packages PackageInfoList + Errors []string Optional [][]interface{} } @@ -164,8 +165,7 @@ func (r ScanResult) ReportKeyName() (name string) { func (r ScanResult) ServerInfo() string { if len(r.Container.ContainerID) == 0 { return fmt.Sprintf("%s (%s%s)", - r.ServerName, r.Family, r.Release, - ) + r.ServerName, r.Family, r.Release) } return fmt.Sprintf( "%s / %s (%s%s) on %s", diff --git a/report/email.go b/report/email.go index e2b7c71ad4..cd17ec4e9f 100644 --- a/report/email.go +++ b/report/email.go @@ -42,11 +42,14 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) { } for _, r := range rs { - subject := fmt.Sprintf("%s%s %s", - conf.EMail.SubjectPrefix, - r.ServerInfo(), - r.CveSummary(), - ) + var subject string + if len(r.Errors) != 0 { + subject = fmt.Sprintf("%s%s An error occurred while scanning", + conf.EMail.SubjectPrefix, r.ServerInfo()) + } else { + subject = fmt.Sprintf("%s%s %s", + conf.EMail.SubjectPrefix, r.ServerInfo(), r.CveSummary()) + } headers := make(map[string]string) headers["From"] = conf.EMail.From diff --git a/report/slack.go b/report/slack.go index 985f569fe2..17fba6c209 100644 --- a/report/slack.go +++ b/report/slack.go @@ -20,6 +20,7 @@ package report import ( "encoding/json" "fmt" + "sort" "strings" "time" @@ -59,48 +60,90 @@ type SlackWriter struct{} func (w SlackWriter) Write(rs ...models.ScanResult) error { conf := config.Conf.Slack channel := conf.Channel - count := 0 - retryMax := 10 for _, r := range rs { if channel == "${servername}" { channel = fmt.Sprintf("#%s", r.ServerName) } - msg := message{ - Text: msgText(r), - Username: conf.AuthUser, - IconEmoji: conf.IconEmoji, - Channel: channel, - Attachments: toSlackAttachments(r), + if 0 < len(r.Errors) { + serverInfo := fmt.Sprintf("*%s*", r.ServerInfo()) + notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers) + txt := fmt.Sprintf("%s\n%s\nError: %s", notifyUsers, serverInfo, r.Errors) + msg := message{ + Text: txt, + Username: conf.AuthUser, + IconEmoji: conf.IconEmoji, + Channel: channel, + } + if err := send(msg); err != nil { + return err + } + continue } - bytes, _ := json.Marshal(msg) - jsonBody := string(bytes) - - f := func() (err error) { - resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End() - if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { - count++ - if count == retryMax { - return nil - } - return fmt.Errorf( - "HTTP POST error: %v, url: %s, resp: %v, body: %s", - errs, conf.HookURL, resp, body) - } - return nil + // A maximum of 100 attachments are allowed on a message. + // Split into chunks with 100 elements + // https://api.slack.com/methods/chat.postMessage + maxAttachments := 100 + m := map[int][]*attachment{} + for i, a := range toSlackAttachments(r) { + m[i/maxAttachments] = append(m[i/maxAttachments], a) } - notify := func(err error, t time.Duration) { - log.Warnf("Error %s", err) - log.Warn("Retrying in ", t) + chunkKeys := []int{} + for k := range m { + chunkKeys = append(chunkKeys, k) } - boff := backoff.NewExponentialBackOff() - if err := backoff.RetryNotify(f, boff, notify); err != nil { - return fmt.Errorf("HTTP error: %s", err) + sort.Ints(chunkKeys) + + for i, k := range chunkKeys { + txt := "" + if i == 0 { + txt = msgText(r) + } + msg := message{ + Text: txt, + Username: conf.AuthUser, + IconEmoji: conf.IconEmoji, + Channel: channel, + Attachments: m[k], + } + if err := send(msg); err != nil { + return err + } } } + return nil +} +func send(msg message) error { + conf := config.Conf.Slack + count, retryMax := 0, 10 + + bytes, _ := json.Marshal(msg) + jsonBody := string(bytes) + + f := func() (err error) { + resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End() + if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { + count++ + if count == retryMax { + return nil + } + return fmt.Errorf( + "HTTP POST error: %v, url: %s, resp: %v, body: %s", + errs, conf.HookURL, resp, body) + } + return nil + } + notify := func(err error, t time.Duration) { + log.Warnf("Error %s", err) + log.Warn("Retrying in ", t) + } + boff := backoff.NewExponentialBackOff() + if err := backoff.RetryNotify(f, boff, notify); err != nil { + return fmt.Errorf("HTTP error: %s", err) + } if count == retryMax { return fmt.Errorf("Retry count exceeded") } @@ -112,7 +155,6 @@ func msgText(r models.ScanResult) string { if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) { notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers) } - serverInfo := fmt.Sprintf("*%s*", r.ServerInfo()) return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, serverInfo, r.CveSummary()) } diff --git a/report/tui.go b/report/tui.go index ce5c9965ef..d51a058a07 100644 --- a/report/tui.go +++ b/report/tui.go @@ -545,6 +545,10 @@ func summaryLines() string { stable.MaxColWidth = 1000 stable.Wrap = false + if len(currentScanResult.Errors) != 0 { + return "Error: Scan with --debug to view the details" + } + indexFormat := "" if len(currentScanResult.AllCves()) < 10 { indexFormat = "[%1d]" @@ -650,6 +654,10 @@ type dataForTmpl struct { } func detailLines() (string, error) { + if len(currentScanResult.Errors) != 0 { + return "", nil + } + if len(currentScanResult.AllCves()) == 0 { return "No vulnerable packages", nil } @@ -730,8 +738,6 @@ func detailLines() (string, error) { return string(buf.Bytes()), nil } -// * {{.Name}}-{{.Version}}-{{.Release}} - func detailTemplate() string { return ` {{.CveID}} diff --git a/report/util.go b/report/util.go index 1bc650efed..748de464a3 100644 --- a/report/util.go +++ b/report/util.go @@ -34,11 +34,21 @@ func toScanSummary(rs ...models.ScanResult) string { table.MaxColWidth = maxColWidth table.Wrap = true for _, r := range rs { - cols := []interface{}{ - r.FormatServerName(), - fmt.Sprintf("%s%s", r.Family, r.Release), - fmt.Sprintf("%d CVEs", len(r.ScannedCves)), - r.Packages.ToUpdatablePacksSummary(), + var cols []interface{} + if len(r.Errors) == 0 { + cols = []interface{}{ + r.FormatServerName(), + fmt.Sprintf("%s%s", r.Family, r.Release), + fmt.Sprintf("%d CVEs", len(r.ScannedCves)), + r.Packages.ToUpdatablePacksSummary(), + } + } else { + cols = []interface{}{ + r.FormatServerName(), + "Error", + "", + "Run with --debug to view the details", + } } table.AddRow(cols...) } @@ -50,10 +60,19 @@ func toOneLineSummary(rs ...models.ScanResult) string { table.MaxColWidth = maxColWidth table.Wrap = true for _, r := range rs { - cols := []interface{}{ - r.FormatServerName(), - r.CveSummary(), - r.Packages.ToUpdatablePacksSummary(), + var cols []interface{} + if len(r.Errors) == 0 { + cols = []interface{}{ + r.FormatServerName(), + r.CveSummary(), + r.Packages.ToUpdatablePacksSummary(), + } + } else { + cols = []interface{}{ + r.FormatServerName(), + "Error: Scan with --debug to view the details", + "", + } } table.AddRow(cols...) } @@ -81,6 +100,12 @@ func toShortPlainText(r models.ScanResult) string { r.Packages.ToUpdatablePacksSummary(), ) + if len(r.Errors) != 0 { + return fmt.Sprintf( + "%s\nError: Scan with --debug to view the details\n%s\n\n", + header, r.Errors) + } + if len(cves) == 0 { return fmt.Sprintf(` %s @@ -168,6 +193,12 @@ func toFullPlainText(r models.ScanResult) string { r.Packages.ToUpdatablePacksSummary(), ) + if len(r.Errors) != 0 { + return fmt.Sprintf( + "%s\nError: Scan with --debug to view the details\n%s\n\n", + header, r.Errors) + } + if len(r.KnownCves) == 0 && len(r.UnknownCves) == 0 { return fmt.Sprintf(` %s diff --git a/scan/base.go b/scan/base.go index 5def53e11a..d8c91e6ac6 100644 --- a/scan/base.go +++ b/scan/base.go @@ -199,24 +199,23 @@ func (l *base) parseLxdPs(stdout string) (containers []config.Container, err err return } -func (l *base) detectPlatform() error { +func (l *base) detectPlatform() { ok, instanceID, err := l.detectRunningOnAws() if err != nil { - return err + l.setPlatform(models.Platform{Name: "other"}) + return } if ok { l.setPlatform(models.Platform{ Name: "aws", InstanceID: instanceID, }) - return nil + return } //TODO Azure, GCP... - l.setPlatform(models.Platform{ - Name: "other", - }) - return nil + l.setPlatform(models.Platform{Name: "other"}) + return } func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) { @@ -271,7 +270,7 @@ func (l base) isAwsInstanceID(str string) bool { return awsInstanceIDPattern.MatchString(str) } -func (l *base) convertToModel() (models.ScanResult, error) { +func (l *base) convertToModel() models.ScanResult { for _, p := range l.VulnInfos { sort.Sort(models.PackageInfosByName(p.Packages)) } @@ -283,6 +282,11 @@ func (l *base) convertToModel() (models.ScanResult, error) { Image: l.ServerInfo.Container.Image, } + errs := []string{} + for _, e := range l.errs { + errs = append(errs, fmt.Sprintf("%s", e)) + } + return models.ScanResult{ ServerName: l.ServerInfo.ServerName, ScannedAt: time.Now(), @@ -293,7 +297,8 @@ func (l *base) convertToModel() (models.ScanResult, error) { ScannedCves: l.VulnInfos, Packages: l.Packages, Optional: l.ServerInfo.Optional, - }, nil + Errors: errs, + } } func (l *base) setErrs(errs []error) { diff --git a/scan/debian.go b/scan/debian.go index c880dd979f..fe6cf7f807 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -56,7 +56,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err return false, deb, fmt.Errorf( "Unable to connect via SSH. Check SSH settings. %s", r) } - Log.Debugf("Not Debian like Linux. %s", r) + util.Log.Debugf("Not Debian like Linux. %s", r) return false, deb, nil } @@ -70,7 +70,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err if len(result) == 0 { deb.setDistro("debian/ubuntu", "unknown") - Log.Warnf( + util.Log.Warnf( "Unknown Debian/Ubuntu version. lsb_release -ir: %s", r) } else { distro := strings.ToLower(trim(result[1])) @@ -88,7 +88,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`) result := re.FindStringSubmatch(trim(r.Stdout)) if len(result) == 0 { - Log.Warnf( + util.Log.Warnf( "Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r) deb.setDistro("debian/ubuntu", "unknown") } else { @@ -105,7 +105,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err return true, deb, nil } - Log.Debugf("Not Debian like Linux: %s", c.ServerName) + util.Log.Debugf("Not Debian like Linux: %s", c.ServerName) return false, deb, nil } diff --git a/scan/sshutil.go b/scan/executil.go similarity index 90% rename from scan/sshutil.go rename to scan/executil.go index 31e75e8733..affc71f09c 100644 --- a/scan/sshutil.go +++ b/scan/executil.go @@ -71,35 +71,30 @@ func (s execResult) isSuccess(expectedStatusCodes ...int) bool { return false } -// Sudo is Const value for sudo mode +// sudo is Const value for sudo mode const sudo = true -// NoSudo is Const value for normal user mode +// noSudo is Const value for normal user mode const noSudo = false -func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []error) { - resChan := make(chan string, len(servers)) - errChan := make(chan error, len(servers)) - defer close(errChan) +// Issue commands to the target servers in parallel via SSH or local execution. If execution fails, the server will be excluded from the target server list(servers) and added to the error server list(errServers). +func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) { + resChan := make(chan osTypeInterface, len(servers)) defer close(resChan) for _, s := range servers { go func(s osTypeInterface) { defer func() { if p := recover(); p != nil { - logrus.Debugf("Panic: %s on %s", + util.Log.Debugf("Panic: %s on %s", p, s.getServerInfo().GetServerName()) } }() if err := fn(s); err != nil { - errChan <- fmt.Errorf("%s@%s:%s: %s", - s.getServerInfo().User, - s.getServerInfo().Host, - s.getServerInfo().Port, - err, - ) + s.setErrs([]error{err}) + resChan <- s } else { - resChan <- s.getServerInfo().GetServerName() + resChan <- s } }(s) } @@ -111,40 +106,44 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs [] timeout = timeoutSec[0] } - var snames []string + var successes []osTypeInterface isTimedout := false for i := 0; i < len(servers); i++ { select { case s := <-resChan: - snames = append(snames, s) - case err := <-errChan: - errs = append(errs, err) + if len(s.getErrs()) == 0 { + successes = append(successes, s) + } else { + util.Log.Errorf("Error: %s, err: %s", + s.getServerInfo().GetServerName(), s.getErrs()) + errServers = append(errServers, s) + } case <-time.After(time.Duration(timeout) * time.Second): isTimedout = true } } - // collect timed out servernames - var timedoutSnames []string if isTimedout { + // set timed out error and append to errServers for _, s := range servers { name := s.getServerInfo().GetServerName() found := false - for _, t := range snames { - if name == t { + for _, ss := range successes { + if name == ss.getServerInfo().GetServerName() { found = true break } } if !found { - timedoutSnames = append(timedoutSnames, name) + msg := fmt.Sprintf("Timed out: %s", + s.getServerInfo().GetServerName()) + util.Log.Errorf(msg) + s.setErrs([]error{fmt.Errorf(msg)}) + errServers = append(errServers, s) } } } - if isTimedout { - errs = append(errs, fmt.Errorf( - "Timed out: %s", timedoutSnames)) - } + servers = successes return } diff --git a/scan/freebsd.go b/scan/freebsd.go index e1ffe5c5f0..e318f404b7 100644 --- a/scan/freebsd.go +++ b/scan/freebsd.go @@ -55,13 +55,13 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) { } } } - Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName) + util.Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName) return false, bsd } func (o *bsd) checkIfSudoNoPasswd() error { // FreeBSD doesn't need root privilege - o.log.Infof("sudo ... OK") + o.log.Infof("sudo ... No need") return nil } diff --git a/scan/redhat.go b/scan/redhat.go index e37d6c7fc7..67c2e05f94 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -50,7 +50,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) { if r := exec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() { red.setDistro("fedora", "unknown") - Log.Warn("Fedora not tested yet: %s", r) + util.Log.Warn("Fedora not tested yet: %s", r) return true, red } @@ -63,7 +63,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) { re := regexp.MustCompile(`(.*) release (\d[\d.]*)`) result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout)) if len(result) != 3 { - Log.Warn("Failed to parse RedHat/CentOS version: %s", r) + util.Log.Warn("Failed to parse RedHat/CentOS version: %s", r) return true, red } @@ -92,11 +92,16 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) { return true, red } - Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName) + util.Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName) return false, red } func (o *redhat) checkIfSudoNoPasswd() error { + if !o.sudo() { + o.log.Infof("sudo ... No need") + return nil + } + cmd := "yum --version" if o.Distro.Family == "centos" { cmd = "echo N | " + cmd @@ -117,7 +122,6 @@ func (o *redhat) checkIfSudoNoPasswd() error { func (o *redhat) checkDependencies() error { switch o.Distro.Family { case "rhel", "amazon": - // o.log.Infof("Nothing to do") return nil case "centos": diff --git a/scan/serverapi.go b/scan/serverapi.go index c7b08b90c6..c6662267d0 100644 --- a/scan/serverapi.go +++ b/scan/serverapi.go @@ -25,17 +25,14 @@ import ( "strings" "time" - "github.com/Sirupsen/logrus" "github.com/future-architect/vuls/cache" "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/report" + "github.com/future-architect/vuls/util" ) -// Log for localhsot -var Log *logrus.Entry - -var servers []osTypeInterface +var servers, errServers []osTypeInterface // Base Interface of redhat, debian, freebsd type osTypeInterface interface { @@ -50,13 +47,13 @@ type osTypeInterface interface { getLackDependencies() []string checkIfSudoNoPasswd() error - detectPlatform() error + detectPlatform() getPlatform() models.Platform checkRequiredPackagesInstalled() error scanPackages() error install() error - convertToModel() (models.ScanResult, error) + convertToModel() models.ScanResult runningContainers() ([]config.Container, error) exitedContainers() ([]config.Container, error) @@ -89,33 +86,32 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) { itsMe, osType, fatalErr = detectDebian(c) if fatalErr != nil { - osType.setServerInfo(c) - osType.setErrs([]error{fatalErr}) return - } else if itsMe { - Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port) + } + + if itsMe { + util.Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port) return } if itsMe, osType = detectRedhat(c); itsMe { - Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port) + util.Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port) return } + if itsMe, osType = detectFreebsd(c); itsMe { - Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port) + util.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port) return } //TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb - - osType.setServerInfo(c) osType.setErrs([]error{fmt.Errorf("Unknown OS Type")}) return } // PrintSSHableServerNames print SSH-able servernames func PrintSSHableServerNames() { - Log.Info("SSH-able servers are below...") + util.Log.Info("SSH-able servers are below...") for _, s := range servers { if s.getServerInfo().IsContainer() { fmt.Printf("%s@%s ", @@ -130,67 +126,74 @@ func PrintSSHableServerNames() { } // InitServers detect the kind of OS distribution of target servers -func InitServers(localLogger *logrus.Entry) error { - Log = localLogger - servers = detectServerOSes() +func InitServers() error { + servers, errServers = detectServerOSes() if len(servers) == 0 { return fmt.Errorf("No scannable servers") } - containers := detectContainerOSes() + actives, inactives := detectContainerOSes() if config.Conf.ContainersOnly { - servers = containers + servers = actives + errServers = inactives } else { - servers = append(servers, containers...) + servers = append(servers, actives...) + errServers = append(errServers, inactives...) } return nil } -func detectServerOSes() (sshAbleOses []osTypeInterface) { - Log.Info("Detecting OS of servers... ") +func detectServerOSes() (servers, errServers []osTypeInterface) { + util.Log.Info("Detecting OS of servers... ") osTypeChan := make(chan osTypeInterface, len(config.Conf.Servers)) defer close(osTypeChan) for _, s := range config.Conf.Servers { go func(s config.ServerInfo) { defer func() { if p := recover(); p != nil { - Log.Debugf("Panic: %s on %s", p, s.ServerName) + util.Log.Debugf("Panic: %s on %s", p, s.ServerName) } }() osTypeChan <- detectOS(s) }(s) } - var oses []osTypeInterface timeout := time.After(30 * time.Second) for i := 0; i < len(config.Conf.Servers); i++ { select { case res := <-osTypeChan: - oses = append(oses, res) if 0 < len(res.getErrs()) { - Log.Errorf("(%d/%d) Failed: %s, err: %s", + errServers = append(errServers, res) + util.Log.Errorf("(%d/%d) Failed: %s, err: %s", i+1, len(config.Conf.Servers), res.getServerInfo().ServerName, res.getErrs()) } else { - Log.Infof("(%d/%d) Detected: %s: %s", + servers = append(servers, res) + util.Log.Infof("(%d/%d) Detected: %s: %s", i+1, len(config.Conf.Servers), res.getServerInfo().ServerName, res.getDistro()) } case <-timeout: msg := "Timed out while detecting servers" - Log.Error(msg) - for servername := range config.Conf.Servers { + util.Log.Error(msg) + for servername, sInfo := range config.Conf.Servers { found := false - for _, o := range oses { + for _, o := range append(servers, errServers...) { if servername == o.getServerInfo().ServerName { found = true break } } if !found { - Log.Errorf("(%d/%d) Timed out: %s", + u := &unknown{} + u.setServerInfo(sInfo) + u.setErrs([]error{ + fmt.Errorf("Timed out"), + }) + errServers = append(errServers, u) + util.Log.Errorf("(%d/%d) Timed out: %s", i+1, len(config.Conf.Servers), servername) i++ @@ -198,24 +201,18 @@ func detectServerOSes() (sshAbleOses []osTypeInterface) { } } } - - for _, o := range oses { - if len(o.getErrs()) == 0 { - sshAbleOses = append(sshAbleOses, o) - } - } return } -func detectContainerOSes() (actives []osTypeInterface) { - Log.Info("Detecting OS of containers... ") +func detectContainerOSes() (actives, inactives []osTypeInterface) { + util.Log.Info("Detecting OS of containers... ") osTypesChan := make(chan []osTypeInterface, len(servers)) defer close(osTypesChan) for _, s := range servers { go func(s osTypeInterface) { defer func() { if p := recover(); p != nil { - Log.Debugf("Panic: %s on %s", + util.Log.Debugf("Panic: %s on %s", p, s.getServerInfo().GetServerName()) } }() @@ -223,7 +220,6 @@ func detectContainerOSes() (actives []osTypeInterface) { }(s) } - var oses []osTypeInterface timeout := time.After(30 * time.Second) for i := 0; i < len(servers); i++ { select { @@ -231,36 +227,38 @@ func detectContainerOSes() (actives []osTypeInterface) { for _, osi := range res { sinfo := osi.getServerInfo() if 0 < len(osi.getErrs()) { - Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs()) + inactives = append(inactives, osi) + util.Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs()) continue } - oses = append(oses, osi) - Log.Infof("Detected: %s@%s: %s", + actives = append(actives, osi) + util.Log.Infof("Detected: %s@%s: %s", sinfo.Container.Name, sinfo.ServerName, osi.getDistro()) } case <-timeout: msg := "Timed out while detecting containers" - Log.Error(msg) - for servername := range config.Conf.Servers { + util.Log.Error(msg) + for servername, sInfo := range config.Conf.Servers { found := false - for _, o := range oses { + for _, o := range append(actives, inactives...) { if servername == o.getServerInfo().ServerName { found = true break } } if !found { - Log.Errorf("Timed out: %s", servername) + u := &unknown{} + u.setServerInfo(sInfo) + u.setErrs([]error{ + fmt.Errorf("Timed out"), + }) + inactives = append(inactives) + util.Log.Errorf("Timed out: %s", servername) } } } } - for _, o := range oses { - if len(o.getErrs()) == 0 { - actives = append(actives, o) - } - } return } @@ -339,28 +337,20 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn } // CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH -func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error { +func CheckIfSudoNoPasswd() { timeoutSec := 15 - errs := parallelSSHExec(func(o osTypeInterface) error { + parallelExec(func(o osTypeInterface) error { return o.checkIfSudoNoPasswd() }, timeoutSec) - - if 0 < len(errs) { - return fmt.Errorf(fmt.Sprintf("%s", errs)) - } - return nil + return } // DetectPlatforms detects the platform of each servers. -func DetectPlatforms(localLogger *logrus.Entry) { - errs := detectPlatforms() - if 0 < len(errs) { - // Only logging - Log.Warnf("Failed to detect platforms. err: %v", errs) - } +func DetectPlatforms() { + detectPlatforms() for i, s := range servers { if s.getServerInfo().IsContainer() { - Log.Infof("(%d/%d) %s on %s is running on %s", + util.Log.Infof("(%d/%d) %s on %s is running on %s", i+1, len(servers), s.getServerInfo().Container.Name, s.getServerInfo().ServerName, @@ -368,7 +358,7 @@ func DetectPlatforms(localLogger *logrus.Entry) { ) } else { - Log.Infof("(%d/%d) %s is running on %s", + util.Log.Infof("(%d/%d) %s is running on %s", i+1, len(servers), s.getServerInfo().ServerName, s.getPlatform().Name, @@ -378,52 +368,68 @@ func DetectPlatforms(localLogger *logrus.Entry) { return } -func detectPlatforms() []error { +func detectPlatforms() { timeoutSec := 1 * 60 - return parallelSSHExec(func(o osTypeInterface) error { - return o.detectPlatform() + parallelExec(func(o osTypeInterface) error { + o.detectPlatform() + // Logging only if platform can not be specified + return nil }, timeoutSec) + return } // Prepare installs requred packages to scan vulnerabilities. -func Prepare() []error { - errs := parallelSSHExec(func(o osTypeInterface) error { +func Prepare() error { + parallelExec(func(o osTypeInterface) error { if err := o.checkDependencies(); err != nil { return err } return nil }) - if len(errs) != 0 { - return errs - } - var targets []osTypeInterface + var targets, nonTargets []osTypeInterface for _, s := range servers { deps := s.getLackDependencies() if len(deps) != 0 { targets = append(targets, s) + } else { + nonTargets = append(nonTargets, s) } } if len(targets) == 0 { - Log.Info("No need to install dependencies") + if 0 < len(nonTargets) { + util.Log.Info("The following servers were already installed dependencies") + for _, s := range nonTargets { + util.Log.Infof(" - %s", s.getServerInfo().GetServerName()) + } + } + + if 0 < len(errServers) { + util.Log.Error("Some errors occurred in the following servers") + for _, s := range errServers { + util.Log.Errorf(" - %s", s.getServerInfo().GetServerName()) + } + } else { + util.Log.Info("Success") + } return nil } - Log.Info("The following servers need dependencies installed") + util.Log.Info("The following servers need to install dependencies") for _, s := range targets { for _, d := range s.getLackDependencies() { - Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName()) + util.Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName()) } } if !config.Conf.AssumeYes { - Log.Info("Is this ok to install dependencies on the servers? [y/N]") + util.Log.Info("Is this ok to install dependencies on the servers? [y/N]") reader := bufio.NewReader(os.Stdin) for { text, err := reader.ReadString('\n') if err != nil { - return []error{err} + return err } switch strings.TrimSpace(text) { case "", "N", "n": @@ -431,62 +437,81 @@ func Prepare() []error { case "y", "Y": goto yes default: - Log.Info("Please enter y or N") + util.Log.Info("Please enter y or N") } } } yes: servers = targets - errs = parallelSSHExec(func(o osTypeInterface) error { + parallelExec(func(o osTypeInterface) error { if err := o.install(); err != nil { return err } return nil }) - if len(errs) != 0 { - return errs + + if 0 < len(servers) { + util.Log.Info("Successfully installed in the followring servers") + for _, s := range servers { + util.Log.Infof(" - %s", s.getServerInfo().GetServerName()) + } + } + + if 0 < len(nonTargets) { + util.Log.Info("The following servers were already installed dependencies") + for _, s := range nonTargets { + util.Log.Infof(" - %s", s.getServerInfo().GetServerName()) + } + } + + if 0 < len(errServers) { + util.Log.Error("Some errors occurred in the following servers") + for _, s := range errServers { + util.Log.Errorf(" - %s", s.getServerInfo().GetServerName()) + } + } + + if len(errServers) == 0 { + util.Log.Info("Success") + } else { + util.Log.Error("Failure") } - Log.Info("All dependencies were installed correctly") return nil } // Scan scan -func Scan() []error { +func Scan() error { if len(servers) == 0 { - return []error{fmt.Errorf("No server defined. Check the configuration")} + return fmt.Errorf("No server defined. Check the configuration") } - Log.Info("Check required packages for scanning...") - if errs := checkRequiredPackagesInstalled(); errs != nil { - Log.Error("Please execute with [prepare] subcommand to install required packages before scanning") - return errs - } + util.Log.Info("Check required packages for scanning...") + checkRequiredPackagesInstalled() - if err := setupCangelogCache(); err != nil { - return []error{err} + if err := setupChangelogCache(); err != nil { + return err } - defer func() { if cache.DB != nil { cache.DB.Close() } }() - Log.Info("Scanning vulnerable OS packages...") + util.Log.Info("Scanning vulnerable OS packages...") scannedAt := time.Now() dir, err := ensureResultDir(scannedAt) if err != nil { - return []error{err} + return err } - if errs := scanVulns(dir, scannedAt); errs != nil { - return errs + if err := scanVulns(dir, scannedAt); err != nil { + return err } return nil } -func setupCangelogCache() error { +func setupChangelogCache() error { needToSetupCache := false for _, s := range servers { switch s.getDistro().Family { @@ -496,7 +521,7 @@ func setupCangelogCache() error { } } if needToSetupCache { - if err := cache.SetupBolt(config.Conf.CacheDBPath, Log); err != nil { + if err := cache.SetupBolt(config.Conf.CacheDBPath, util.Log); err != nil { return err } } @@ -505,28 +530,24 @@ func setupCangelogCache() error { func checkRequiredPackagesInstalled() []error { timeoutSec := 30 * 60 - return parallelSSHExec(func(o osTypeInterface) error { + parallelExec(func(o osTypeInterface) error { return o.checkRequiredPackagesInstalled() }, timeoutSec) + return nil } -func scanVulns(jsonDir string, scannedAt time.Time) []error { +func scanVulns(jsonDir string, scannedAt time.Time) error { var results models.ScanResults timeoutSec := 120 * 60 - errs := parallelSSHExec(func(o osTypeInterface) error { - if err := o.scanPackages(); err != nil { - return err - } + parallelExec(func(o osTypeInterface) error { + return o.scanPackages() + }, timeoutSec) - r, err := o.convertToModel() - if err != nil { - return err - } + for _, s := range append(servers, errServers...) { + r := s.convertToModel() r.ScannedAt = scannedAt results = append(results, r) - - return nil - }, timeoutSec) + } config.Conf.FormatJSON = true ws := []report.ResultWriter{ @@ -534,14 +555,9 @@ func scanVulns(jsonDir string, scannedAt time.Time) []error { } for _, w := range ws { if err := w.Write(results...); err != nil { - return []error{ - fmt.Errorf("Failed to write summary report: %s", err), - } + return fmt.Errorf("Failed to write summary report: %s", err) } } - if errs != nil { - return errs - } report.StdoutWriter{}.WriteScanSummary(results...) return nil diff --git a/scan/unknownDistro.go b/scan/unknownDistro.go new file mode 100644 index 0000000000..b7221ba800 --- /dev/null +++ b/scan/unknownDistro.go @@ -0,0 +1,43 @@ +/* Vuls - Vulnerability Scanner +Copyright (C) 2016 Future Architect, Inc. Japan. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package scan + +// inherit OsTypeInterface +type unknown struct { + base +} + +func (o *unknown) checkIfSudoNoPasswd() error { + return nil +} + +func (o unknown) checkDependencies() error { + return nil +} + +func (o *unknown) install() error { + return nil +} + +func (o *unknown) checkRequiredPackagesInstalled() error { + return nil +} + +func (o *unknown) scanPackages() error { + return nil +} diff --git a/util/logutil.go b/util/logutil.go index 63e51be2aa..021462eab3 100644 --- a/util/logutil.go +++ b/util/logutil.go @@ -29,6 +29,9 @@ import ( formatter "github.com/kotakanbe/logrus-prefixed-formatter" ) +// Log for localhsot +var Log *logrus.Entry + // NewCustomLogger creates logrus func NewCustomLogger(c config.ServerInfo) *logrus.Entry { log := logrus.New() @@ -47,7 +50,7 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry { if _, err := os.Stat(logDir); os.IsNotExist(err) { if err := os.Mkdir(logDir, 0700); err != nil { - logrus.Errorf("Failed to create log directory: %s", err) + log.Errorf("Failed to create log directory: %s", err) } }