Skip to content

Commit

Permalink
Merge pull request #90 from justyntemme/master
Browse files Browse the repository at this point in the history
Added FreeBSD support.
  • Loading branch information
kotakanbe committed Jun 3, 2016
2 parents a29b2a2 + e5bfa1b commit 1044fb8
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 1 deletion.
254 changes: 254 additions & 0 deletions scan/freebsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package scan

import (
"fmt"
"regexp"
"strings"
"time"

"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)

// inherit OsTypeInterface
type bsd struct {
linux
}

// NewBSD constructor
func newBsd(c config.ServerInfo) *bsd {
d := &bsd{}
d.log = util.NewCustomLogger(c)
return d
}

//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
//set sudo option flag
c.SudoOpt = config.SudoOption{ExecBySudo: true}
bsd.setServerInfo(c)

if r := sshExec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(r.Stdout, "FreeBSD") == true {
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
bsd.setDistributionInfo("FreeBSD", b.Stdout)
} else {
return false, bsd
}
return true, bsd
} else {
return false, bsd
}
}
return
}
func (o *bsd) install() error {
//pkg upgrade
cmd := "pkg upgrade"
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
return nil
}

func (o *bsd) scanPackages() error {
var err error
var packs []models.PackageInfo
if packs, err = o.scanInstalledPackages(); err != nil {
o.log.Errorf("Failed to scan installed packages")
return err
}
o.setPackages(packs)
var unsecurePacks []CvePacksInfo
if unsecurePacks, err = o.scanUnsecurePackages(packs); err != nil {
o.log.Errorf("Failed to scan vulnerable packages")
return err
}
o.setUnsecurePackages(unsecurePacks)
return nil
}

func (o *bsd) scanInstalledPackages() (packs []models.PackageInfo, err error) {
//pkg query is a FreeBSD to provide info on a certain package : %n=name %v=version: formatting for string split later
r := o.ssh("pkg query '%n*%v'", noSudo)
if !r.isSuccess() {
return packs, fmt.Errorf(
"Failed to scan packages. status: %d, stdout:%s, Stderr: %s",
r.ExitStatus, r.Stdout, r.Stderr)
}
//same format as debain.go
lines := strings.Split(r.Stdout, "\n")
for _, line := range lines {
//for every \n
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
name, version, err := o.parseScanedPackagesLine(trimmed)
if err != nil {
return nil, fmt.Errorf("FreeBSD: Failed to parse package")
}
packs = append(packs, models.PackageInfo{
Name: name,
Version: version,
})
}
}
return
}

func (o *bsd) parseScanedPackagesLine(line string) (name, version string, err error) {
name = strings.Split(line, "*")[0]
if len(strings.Split(line, "*")) == 2 {

version = strings.Split(line, "*")[1]
}
return name, version, nil
}

func (o *bsd) checkRequiredPackagesInstalled() error {
return nil
}

func (o *bsd) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInfo, error) {
cmd := util.PrependProxyEnv("pkg version -l '>'")
r := o.ssh(cmd, noSudo)
if !r.isSuccess() {
return nil, nil
}
match := regexp.MustCompile("(.)+[^[-](\\d)]")
upgradablePackNames := match.FindAllString(r.Stdout, -1)

// Convert package name to PackageInfo struct
var unsecurePacks []models.PackageInfo
var err error
for _, name := range upgradablePackNames {
for _, pack := range packs {
if pack.Name == name {
unsecurePacks = append(unsecurePacks, pack)
break
}
}
}
/* unsecurePacks, err = o.fillCanidateVersion(unsecurePacks)
if err != nil {
return nil, fmt.Errorf("Failed to fill canidate versions. err: %s", err)
}
*/

// Collect CVE information of upgradable packages
cvePacksInfos, err := o.scanPackageCveInfos(unsecurePacks)
if err != nil {
return nil, fmt.Errorf("Failed to scan unsecure packages. err: %s", err)
}

return cvePacksInfos, nil
}

func (o *bsd) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePacksList CvePacksList, err error) {

// { CVE ID: [packageInfo] }
cvePackages := make(map[string][]models.PackageInfo)

type strarray []string
resChan := make(chan struct {
models.PackageInfo
strarray
}, len(unsecurePacks))
errChan := make(chan error, len(unsecurePacks))
reqChan := make(chan models.PackageInfo, len(unsecurePacks))
defer close(resChan)
defer close(errChan)
defer close(reqChan)

go func() {
for _, pack := range unsecurePacks {
reqChan <- pack
}
}()

timeout := time.After(30 * 60 * time.Second)

concurrency := 10
tasks := util.GenWorkers(concurrency)
for range unsecurePacks {
tasks <- func() {
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
errChan <- err
} else {
resChan <- struct {
models.PackageInfo
strarray
}{p, cveIDs}
}
}(pack)
}
}
}

errs := []error{}
for i := 0; i < len(unsecurePacks); i++ {
o.log.Info(unsecurePacks[i])
select {
case pair := <-resChan:
pack := pair.PackageInfo
cveIDs := pair.strarray
for _, cveID := range cveIDs {
cvePackages[cveID] = appendPackIfMissing(cvePackages[cveID], pack)
}
o.log.Infof("(%d/%d) Scanned %s-%s : %s",
i+1, len(unsecurePacks), pair.Name, pair.PackageInfo.Version, cveIDs)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout scanPackageCveIDs")
}
}

if 0 < len(errs) {
return nil, fmt.Errorf("%v", errs)
}

var cveIDs []string
for k := range cvePackages {
cveIDs = append(cveIDs, k)
}

o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)

o.log.Info("Fetching CVE details...")
cveDetails, err := cveapi.CveClient.FetchCveDetails(cveIDs)
if err != nil {
return nil, err
}
o.log.Info("Done")

for _, detail := range cveDetails {
cvePacksList = append(cvePacksList, CvePacksInfo{
CveID: detail.CveID,
CveDetail: detail,
Packs: cvePackages[detail.CveID],
// CvssScore: cinfo.CvssScore(conf.Lang),
})
}
return
}

func (o *bsd) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
cmd := fmt.Sprintf("pkg audit -F %s | grep CVE-\\w", pack.Name)
cmd = util.PrependProxyEnv(cmd)

r := o.ssh(cmd, noSudo)
if !r.isSuccess() {
o.log.Warnf("Failed to %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, nil
}
match := regexp.MustCompile("(CVE-\\d{4}-\\d{4})")
return match.FindAllString(r.Stdout, -1), nil
}
5 changes: 4 additions & 1 deletion scan/serverapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
if itsMe {
return
}
itsMe, osType = detectFreebsd(c)
if itsMe {
return
}

osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
return
Expand All @@ -126,7 +130,6 @@ func InitServers(localLogger *logrus.Entry) error {
return fmt.Errorf("Failed to detect server OSes. err: %s", err)
}
servers = hosts

Log.Info("Detecting Container OS...")
containers, err := detectContainerOSes()
if err != nil {
Expand Down

0 comments on commit 1044fb8

Please sign in to comment.