diff --git a/commands/configtest.go b/commands/configtest.go index 6feca8f5..f8cb6d24 100644 --- a/commands/configtest.go +++ b/commands/configtest.go @@ -36,11 +36,14 @@ type ConfigtestCmd struct { logDir string askKeyPassword bool containersOnly bool - deep bool sshNative bool httpProxy string timeoutSec int + fast bool + offline bool + deep bool + debug bool } @@ -54,6 +57,8 @@ func (*ConfigtestCmd) Synopsis() string { return "Test configuration" } func (*ConfigtestCmd) Usage() string { return `configtest: configtest + [-fast] + [-offline] [-deep] [-config=/path/to/config.toml] [-log-dir=/path/to/log] @@ -88,6 +93,18 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) { "Ask ssh privatekey password before scanning", ) + f.BoolVar( + &p.fast, + "fast", + false, + "Config test for online fast scan mode") + + f.BoolVar( + &p.offline, + "offline", + false, + "Config test for offline scan mode") + f.BoolVar(&p.deep, "deep", false, "Config test for deep scan mode") f.StringVar( @@ -137,7 +154,13 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa c.Conf.SSHNative = p.sshNative c.Conf.HTTPProxy = p.httpProxy c.Conf.ContainersOnly = p.containersOnly + + c.Conf.Fast = p.fast + c.Conf.Offline = p.offline c.Conf.Deep = p.deep + if !(c.Conf.Fast || c.Conf.Offline || c.Conf.Deep) { + c.Conf.Fast = true + } var servernames []string if 0 < len(f.Args()) { diff --git a/commands/scan.go b/commands/scan.go index 0ee4dae2..5eae661f 100644 --- a/commands/scan.go +++ b/commands/scan.go @@ -43,6 +43,8 @@ type ScanCmd struct { httpProxy string askKeyPassword bool containersOnly bool + fast bool + offline bool deep bool skipBroken bool sshNative bool @@ -61,6 +63,8 @@ func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" } func (*ScanCmd) Usage() string { return `scan: scan + [-fast] + [-offline] [-deep] [-config=/path/to/config.toml] [-results-dir=/path/to/results] @@ -134,6 +138,18 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { "Ask ssh privatekey password before scanning", ) + f.BoolVar( + &p.fast, + "fast", + false, + "Online fast scan mode.") + + f.BoolVar( + &p.offline, + "offline", + false, + "Offline scan mode. Unable to get updatable packages information.") + f.BoolVar( &p.deep, "deep", @@ -163,7 +179,6 @@ 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 @@ -231,9 +246,15 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) c.Conf.SSHNative = p.sshNative c.Conf.HTTPProxy = p.httpProxy c.Conf.ContainersOnly = p.containersOnly - c.Conf.Deep = p.deep c.Conf.SkipBroken = p.skipBroken + c.Conf.Fast = p.fast + c.Conf.Offline = p.offline + c.Conf.Deep = p.deep + if !(c.Conf.Fast || c.Conf.Offline || c.Conf.Deep) { + c.Conf.Fast = true + } + util.Log.Info("Validating config...") if !c.Conf.ValidateOnScan() { return subcommands.ExitUsageError diff --git a/config/config.go b/config/config.go index 7585699d..fd35a3a2 100644 --- a/config/config.go +++ b/config/config.go @@ -103,6 +103,8 @@ type Config struct { SSHNative bool ContainersOnly bool + Fast bool + Offline bool Deep bool SkipBroken bool @@ -198,6 +200,16 @@ func (c Config) ValidateOnScan() bool { } } + numTrue := 0 + for _, b := range []bool{c.Fast, c.Offline, c.Deep} { + if b { + numTrue++ + } + } + if numTrue != 1 { + errs = append(errs, fmt.Errorf("Specify only one of -fast, -fast-offline, -deep")) + } + _, err := valid.ValidateStruct(c) if err != nil { errs = append(errs, err) diff --git a/models/packages.go b/models/packages.go index 48965a8f..615622d6 100644 --- a/models/packages.go +++ b/models/packages.go @@ -21,6 +21,8 @@ import ( "bytes" "fmt" "strings" + + "github.com/future-architect/vuls/config" ) // Packages is Map of Package @@ -62,13 +64,17 @@ func (ps Packages) Merge(other Packages) Packages { // FormatUpdatablePacksSummary returns a summary of updatable packages func (ps Packages) FormatUpdatablePacksSummary() string { + if config.Conf.Offline { + return fmt.Sprintf("%d installed", len(ps)) + } + nUpdatable := 0 for _, p := range ps { if p.NewVersion != "" { nUpdatable++ } } - return fmt.Sprintf("%d updatable packages", nUpdatable) + return fmt.Sprintf("%d installed, %d updatable", len(ps), nUpdatable) } // FindOne search a element by name-newver-newrel-arch diff --git a/scan/debian.go b/scan/debian.go index 0d352dbd..bbea2adc 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -164,7 +164,7 @@ func (o *debian) checkDependencies() error { // https://askubuntu.com/a/742844 packNames = append(packNames, "reboot-notifier") - if !config.Conf.Deep { + if config.Conf.Deep { // Debian needs aptitude to get changelogs. // Because unable to get changelogs via apt-get changelog on Debian. packNames = append(packNames, "aptitude") @@ -212,6 +212,10 @@ func (o *debian) scanPackages() error { o.Packages = installed o.SrcPackages = srcPacks + if config.Conf.Offline { + return nil + } + if config.Conf.Deep || o.Distro.Family == config.Raspbian { unsecures, err := o.scanUnsecurePackages(updatable) if err != nil { @@ -300,6 +304,13 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, mode delete(srcPacks, name) } + if config.Conf.Offline { + return installed, updatable, srcPacks, nil + } + + if err := o.aptGetUpdate(); err != nil { + return nil, nil, nil, err + } updatableNames, err := o.getUpdatablePackNames() if err != nil { return nil, nil, nil, err @@ -346,14 +357,12 @@ func (o *debian) aptGetUpdate() error { o.log.Infof("apt-get update...") cmd := util.PrependProxyEnv("apt-get update") if r := o.exec(cmd, sudo); !r.isSuccess() { - return fmt.Errorf("Failed to SSH: %s", r) + return fmt.Errorf("Failed to apt-get update: %s", r) } return nil } func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) { - o.aptGetUpdate() - // Setup changelog cache current := cache.Meta{ Name: o.getServerInfo().GetServerName(), diff --git a/scan/redhat.go b/scan/redhat.go index 45ea78d3..d048651c 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -177,9 +177,12 @@ func (o *redhat) checkIfSudoNoPasswd() error { return nil } -// - Fast scan mode +// - Fast offline scan mode // Amazon ... yum-utils // +// - Fast scan mode +// All ... yum-utils +// // - Deep scan mode // CentOS 6,7 ... yum-utils, yum-plugin-changelog // RHEL 5 (U1-) ... yum-utils, yum-security, yum-changelog @@ -203,11 +206,13 @@ func (o *redhat) checkDependencies() error { } packNames := []string{} - - if !config.Conf.Deep { - // Fast Scan + if config.Conf.Fast { + // Online fast scan needs yum-utils to issue repoquery cmd + packNames = append(packNames, "yum-utils") + } else if config.Conf.Offline { switch o.Distro.Family { case config.Amazon: + // Offline scan doesn't support Amazon Linux packNames = append(packNames, "yum-utils") } } else { @@ -255,7 +260,7 @@ func (o *redhat) scanPackages() error { } o.Kernel.RebootRequired = rebootRequired - if !config.Conf.Deep { + if config.Conf.Offline { switch o.Distro.Family { case config.Amazon: // nop