Continue scanning even when some hosts have tech issues

see #264
This commit is contained in:
Kota Kanbe
2017-01-31 14:35:16 +09:00
parent 00660485b7
commit 386b97d2be
19 changed files with 447 additions and 311 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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")

View File

@@ -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
}

2
glide.lock generated
View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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),
}
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)
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,
}
return nil
if err := send(msg); err != nil {
return err
}
continue
}
notify := func(err error, t time.Duration) {
log.Warnf("Error %s", err)
log.Warn("Retrying in ", t)
// 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)
}
boff := backoff.NewExponentialBackOff()
if err := backoff.RetryNotify(f, boff, notify); err != nil {
return fmt.Errorf("HTTP error: %s", err)
chunkKeys := []int{}
for k := range m {
chunkKeys = append(chunkKeys, k)
}
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())
}

View File

@@ -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}}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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":

View File

@@ -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

43
scan/unknownDistro.go Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View File

@@ -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)
}
}