Merge pull request #270 from future-architect/report-subcommand

Add report subcommand, change scan options. #239
This commit is contained in:
Kota Kanbe
2017-01-10 16:15:01 +09:00
committed by GitHub
43 changed files with 2761 additions and 1979 deletions

View File

@@ -21,6 +21,7 @@ import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"time"
@@ -28,7 +29,7 @@ import (
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
cve "github.com/kotakanbe/go-cve-dictionary/models"
"github.com/future-architect/vuls/report"
)
// Log for localhsot
@@ -54,7 +55,6 @@ type osTypeInterface interface {
checkRequiredPackagesInstalled() error
scanPackages() error
scanVulnByCpeName() error
install() error
convertToModel() (models.ScanResult, error)
@@ -72,64 +72,15 @@ type osPackages struct {
Packages models.PackageInfoList
// unsecure packages
UnsecurePackages CvePacksList
VulnInfos models.VulnInfos
}
func (p *osPackages) setPackages(pi models.PackageInfoList) {
p.Packages = pi
}
func (p *osPackages) setUnsecurePackages(pi []CvePacksInfo) {
p.UnsecurePackages = pi
}
// CvePacksList have CvePacksInfo list, getter/setter, sortable methods.
type CvePacksList []CvePacksInfo
// CvePacksInfo hold the CVE information.
type CvePacksInfo struct {
CveID string
CveDetail cve.CveDetail
Packs models.PackageInfoList
DistroAdvisories []models.DistroAdvisory // for Aamazon, RHEL, FreeBSD
CpeNames []string
}
// FindByCveID find by CVEID
func (s CvePacksList) FindByCveID(cveID string) (pi CvePacksInfo, found bool) {
for _, p := range s {
if cveID == p.CveID {
return p, true
}
}
return CvePacksInfo{CveID: cveID}, false
}
// immutable
func (s CvePacksList) set(cveID string, cvePacksInfo CvePacksInfo) CvePacksList {
for i, p := range s {
if cveID == p.CveID {
s[i] = cvePacksInfo
return s
}
}
return append(s, cvePacksInfo)
}
// Len implement Sort Interface
func (s CvePacksList) Len() int {
return len(s)
}
// Swap implement Sort Interface
func (s CvePacksList) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less implement Sort Interface
func (s CvePacksList) Less(i, j int) bool {
return s[i].CveDetail.CvssScore(config.Conf.Lang) >
s[j].CveDetail.CvssScore(config.Conf.Lang)
func (p *osPackages) setVulnInfos(vi []models.VulnInfo) {
p.VulnInfos = vi
}
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
@@ -518,14 +469,15 @@ func Scan() []error {
}()
Log.Info("Scanning vulnerable OS packages...")
if errs := scanPackages(); errs != nil {
scannedAt := time.Now()
dir, err := ensureResultDir(scannedAt)
if err != nil {
return []error{err}
}
if errs := scanVulns(dir, scannedAt); errs != nil {
return errs
}
Log.Info("Scanning vulnerable software specified in the CPE...")
if errs := scanVulnByCpeName(); errs != nil {
return errs
}
return nil
}
@@ -553,31 +505,67 @@ func checkRequiredPackagesInstalled() []error {
}, timeoutSec)
}
func scanPackages() []error {
func scanVulns(jsonDir string, scannedAt time.Time) []error {
var results models.ScanResults
timeoutSec := 120 * 60
return parallelSSHExec(func(o osTypeInterface) error {
return o.scanPackages()
}, timeoutSec)
}
// scanVulnByCpeName search vulnerabilities that specified in config file.
func scanVulnByCpeName() []error {
timeoutSec := 30 * 60
return parallelSSHExec(func(o osTypeInterface) error {
return o.scanVulnByCpeName()
}, timeoutSec)
}
// GetScanResults returns Scan Resutls
func GetScanResults() (results models.ScanResults, err error) {
for _, s := range servers {
r, err := s.convertToModel()
if err != nil {
return results, fmt.Errorf("Failed converting to model: %s", err)
errs := parallelSSHExec(func(o osTypeInterface) error {
if err := o.scanPackages(); err != nil {
return err
}
r, err := o.convertToModel()
if err != nil {
return err
}
r.ScannedAt = scannedAt
results = append(results, r)
return nil
}, timeoutSec)
config.Conf.FormatJSON = true
ws := []report.ResultWriter{
report.LocalFileWriter{CurrentDir: jsonDir},
}
return
for _, w := range ws {
if err := w.Write(results...); err != nil {
return []error{
fmt.Errorf("Failed to write summary report: %s", err),
}
}
}
if errs != nil {
return errs
}
report.StdoutWriter{}.WriteScanSummary(results...)
return nil
}
func ensureResultDir(scannedAt time.Time) (currentDir string, err error) {
jsonDirName := scannedAt.Format(time.RFC3339)
resultsDir := config.Conf.ResultsDir
if len(resultsDir) == 0 {
wd, _ := os.Getwd()
resultsDir = filepath.Join(wd, "results")
}
jsonDir := filepath.Join(resultsDir, jsonDirName)
if err := os.MkdirAll(jsonDir, 0700); err != nil {
return "", fmt.Errorf("Failed to create dir: %s", err)
}
symlinkPath := filepath.Join(resultsDir, "current")
if _, err := os.Lstat(symlinkPath); err == nil {
if err := os.Remove(symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
}
}
if err := os.Symlink(jsonDir, symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
}
return jsonDir, nil
}