-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
458 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* 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 oval | ||
|
||
import ( | ||
"github.com/future-architect/vuls/config" | ||
"github.com/future-architect/vuls/models" | ||
"github.com/future-architect/vuls/util" | ||
"github.com/k0kubun/pp" | ||
ovalmodels "github.com/kotakanbe/goval-dictionary/models" | ||
) | ||
|
||
// SUSE is the struct of SUSE Linux | ||
type SUSE struct { | ||
Base | ||
} | ||
|
||
// NewSUSE creates OVAL client for SUSE | ||
func NewSUSE() SUSE { | ||
// TODO implement other family | ||
return SUSE{ | ||
Base{ | ||
family: config.SUSEEnterpriseServer, | ||
}, | ||
} | ||
} | ||
|
||
// FillWithOval returns scan result after updating CVE info by OVAL | ||
func (o SUSE) FillWithOval(r *models.ScanResult) (err error) { | ||
// TODO | ||
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version) | ||
// linuxImage := "linux-image-" + r.RunningKernel.Release | ||
// // Add linux and set the version of running kernel to search OVAL. | ||
// if r.Container.ContainerID == "" { | ||
// r.Packages["linux"] = models.Package{ | ||
// Name: "linux", | ||
// Version: r.RunningKernel.Version, | ||
// } | ||
// } | ||
|
||
var relatedDefs ovalResult | ||
if o.isFetchViaHTTP() { | ||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil { | ||
return err | ||
} | ||
} else { | ||
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil { | ||
return err | ||
} | ||
} | ||
pp.Println(relatedDefs) | ||
|
||
//TODO | ||
// delete(r.Packages, "linux") | ||
|
||
for _, defPacks := range relatedDefs.entries { | ||
//TODO | ||
// Remove linux added above to search for oval | ||
// linux is not a real package name (key of affected packages in OVAL) | ||
// if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok { | ||
// defPacks.actuallyAffectedPackNames[linuxImage] = true | ||
// delete(defPacks.actuallyAffectedPackNames, "linux") | ||
// for i, p := range defPacks.def.AffectedPacks { | ||
// if p.Name == "linux" { | ||
// p.Name = linuxImage | ||
// defPacks.def.AffectedPacks[i] = p | ||
// } | ||
// } | ||
// } | ||
o.update(r, defPacks) | ||
} | ||
|
||
for _, vuln := range r.ScannedCves { | ||
if cont, ok := vuln.CveContents[models.SUSE]; ok { | ||
//TODO | ||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID | ||
vuln.CveContents[models.SUSE] = cont | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) { | ||
ovalContent := *o.convertToModel(&defPacks.def) | ||
ovalContent.Type = models.NewCveContentType(o.family) | ||
vinfo, ok := r.ScannedCves[defPacks.def.Title] | ||
if !ok { | ||
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title) | ||
vinfo = models.VulnInfo{ | ||
CveID: defPacks.def.Title, | ||
Confidence: models.OvalMatch, | ||
CveContents: models.NewCveContents(ovalContent), | ||
} | ||
} else { | ||
cveContents := vinfo.CveContents | ||
ctype := models.NewCveContentType(o.family) | ||
if _, ok := vinfo.CveContents[ctype]; ok { | ||
util.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title) | ||
} else { | ||
util.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title) | ||
cveContents = models.CveContents{} | ||
} | ||
if vinfo.Confidence.Score < models.OvalMatch.Score { | ||
vinfo.Confidence = models.OvalMatch | ||
} | ||
cveContents[ctype] = ovalContent | ||
vinfo.CveContents = cveContents | ||
} | ||
|
||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames) | ||
for _, pack := range vinfo.AffectedPackages { | ||
defPacks.actuallyAffectedPackNames[pack.Name] = true | ||
} | ||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages) | ||
vinfo.AffectedPackages.Sort() | ||
r.ScannedCves[defPacks.def.Title] = vinfo | ||
} | ||
|
||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent { | ||
var refs []models.Reference | ||
for _, r := range def.References { | ||
refs = append(refs, models.Reference{ | ||
Link: r.RefURL, | ||
Source: r.Source, | ||
RefID: r.RefID, | ||
}) | ||
} | ||
|
||
return &models.CveContent{ | ||
CveID: def.Title, | ||
Title: def.Title, | ||
Summary: def.Description, | ||
References: refs, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package scan | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/future-architect/vuls/config" | ||
"github.com/future-architect/vuls/models" | ||
"github.com/future-architect/vuls/util" | ||
) | ||
|
||
// inherit OsTypeInterface | ||
type suse struct { | ||
redhat | ||
} | ||
|
||
// NewRedhat is constructor | ||
func newSUSE(c config.ServerInfo) *suse { | ||
r := &suse{ | ||
redhat: redhat{ | ||
base: base{ | ||
osPackages: osPackages{ | ||
Packages: models.Packages{}, | ||
VulnInfos: models.VulnInfos{}, | ||
}, | ||
}, | ||
}, | ||
} | ||
r.log = util.NewCustomLogger(c) | ||
r.setServerInfo(c) | ||
return r | ||
} | ||
|
||
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/suse.rb | ||
func detectSUSE(c config.ServerInfo) (itsMe bool, suse osTypeInterface) { | ||
suse = newSUSE(c) | ||
|
||
if r := exec(c, "ls /etc/os-release", noSudo); r.isSuccess() { | ||
if r := exec(c, "zypper -V", noSudo); r.isSuccess() { | ||
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() { | ||
name := "" | ||
if strings.Contains(r.Stdout, "ID=opensuse") { | ||
//TODO check opensuse or opensuse.leap | ||
name = config.OpenSUSE | ||
} else if strings.Contains(r.Stdout, `NAME="SLES"`) { | ||
//TODO check suse.enterprise.server or suse.enterprise.desktop | ||
name = config.SUSEEnterpriseServer | ||
} else { | ||
util.Log.Warn("Failed to parse SUSE edition: %s", r) | ||
return true, suse | ||
} | ||
|
||
re := regexp.MustCompile(`VERSION_ID=\"(\d+\.\d+|\d+)\"`) | ||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout)) | ||
if len(result) != 2 { | ||
util.Log.Warn("Failed to parse SUSE Linux version: %s", r) | ||
return true, suse | ||
} | ||
suse.setDistro(name, result[1]) | ||
return true, suse | ||
} | ||
} | ||
} else if r := exec(c, "ls /etc/SuSE-release", noSudo); r.isSuccess() { | ||
if r := exec(c, "zypper -V", noSudo); r.isSuccess() { | ||
if r := exec(c, "cat /etc/SuSE-release", noSudo); r.isSuccess() { | ||
re := regexp.MustCompile(`SUSE Linux Enterprise Server (\d+)`) | ||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout)) | ||
if len(result) == 2 { | ||
//TODO check suse.enterprise.server or suse.enterprise.desktop | ||
suse.setDistro(config.SUSEEnterpriseServer, result[1]) | ||
return true, suse | ||
} | ||
|
||
re = regexp.MustCompile(`openSUSE (\d+\.\d+|\d+)`) | ||
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout)) | ||
if len(result) == 2 { | ||
//TODO check opensuse or opensuse.leap | ||
suse.setDistro(config.OpenSUSE, result[1]) | ||
return true, suse | ||
} | ||
|
||
util.Log.Warn("Failed to parse SUSE Linux version: %s", r) | ||
return true, suse | ||
} | ||
} | ||
} | ||
util.Log.Debugf("Not SUSE Linux. servername: %s", c.ServerName) | ||
return false, suse | ||
} | ||
|
||
func (o *suse) checkDependencies() error { | ||
o.log.Infof("Dependencies... No need") | ||
return nil | ||
} | ||
|
||
func (o *suse) checkIfSudoNoPasswd() error { | ||
// SUSE doesn't need root privilege | ||
o.log.Infof("sudo ... No need") | ||
return nil | ||
} | ||
|
||
func (o *suse) scanPackages() error { | ||
installed, err := o.scanInstalledPackages() | ||
if err != nil { | ||
o.log.Errorf("Failed to scan installed packages: %s", err) | ||
return err | ||
} | ||
|
||
rebootRequired, err := o.rebootRequired() | ||
if err != nil { | ||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err) | ||
return err | ||
} | ||
o.Kernel.RebootRequired = rebootRequired | ||
|
||
updatable, err := o.scanUpdatablePackages() | ||
if err != nil { | ||
o.log.Errorf("Failed to scan updatable packages: %s", err) | ||
return err | ||
} | ||
installed.MergeNewVersion(updatable) | ||
o.Packages = installed | ||
|
||
return nil | ||
} | ||
|
||
//TODO | ||
func (o *suse) rebootRequired() (bool, error) { | ||
return false, nil | ||
} | ||
|
||
func (o *suse) scanUpdatablePackages() (models.Packages, error) { | ||
r := o.exec("zypper --no-color -q lu", noSudo) | ||
if !r.isSuccess() { | ||
return nil, fmt.Errorf("Failed to scan updatable packages: %v", r) | ||
} | ||
return o.parseZypperLULines(r.Stdout) | ||
} | ||
|
||
func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) { | ||
updatables := models.Packages{} | ||
scanner := bufio.NewScanner(strings.NewReader(stdout)) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
if strings.HasPrefix(line, "S | Repository") || | ||
strings.HasPrefix(line, "--+----------------") { | ||
continue | ||
} | ||
pack, err := o.parseZypperLUOneLine(line) | ||
if err != nil { | ||
return nil, err | ||
} | ||
updatables[pack.Name] = *pack | ||
} | ||
return updatables, nil | ||
} | ||
|
||
func (o *suse) parseZypperLUOneLine(line string) (*models.Package, error) { | ||
fs := strings.Fields(line) | ||
if len(fs) != 11 { | ||
return nil, fmt.Errorf("zypper -q lu Unknown format: %s", line) | ||
} | ||
available := strings.Split(fs[8], "-") | ||
return &models.Package{ | ||
Name: fs[4], | ||
NewVersion: available[0], | ||
NewRelease: available[1], | ||
Arch: fs[10], | ||
}, nil | ||
} |
Oops, something went wrong.