Add offline option to scan and configtest (#588)
Add offline option to scan and configtest
This commit is contained in:
		@@ -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()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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(),
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user