refactor(scan): remove yum-security related code (#836)
* refactor(scan): remove yum-security related code * fix(reporting): error if no OVAL entry
This commit is contained in:
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -142,9 +141,6 @@ type redhatBase struct {
|
||||
|
||||
type rootPriv interface {
|
||||
repoquery() bool
|
||||
yumRepolist() bool
|
||||
yumUpdateInfo() bool
|
||||
yumChangelog() bool
|
||||
yumMakeCache() bool
|
||||
}
|
||||
|
||||
@@ -237,12 +233,7 @@ func (o *redhatBase) scanPackages() error {
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
switch o.Distro.Family {
|
||||
case config.Amazon:
|
||||
// nop
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
} else if o.Distro.Family == config.RedHat {
|
||||
if o.getServerInfo().Mode.IsFast() {
|
||||
return nil
|
||||
@@ -259,13 +250,6 @@ func (o *redhatBase) scanPackages() error {
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
}
|
||||
|
||||
var unsecures models.VulnInfos
|
||||
if unsecures, err = o.scanUnsecurePackages(updatable); err != nil {
|
||||
o.log.Errorf("Failed to scan vulnerable packages: %s", err)
|
||||
return err
|
||||
}
|
||||
o.VulnInfos = unsecures
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -457,24 +441,6 @@ func (o *redhatBase) isExecScanUsingYum() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *redhatBase) isExecFillChangelogs() bool {
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
return false
|
||||
}
|
||||
// Amazon linux has no changelos for updates
|
||||
return o.getServerInfo().Mode.IsDeep() &&
|
||||
o.Distro.Family != config.Amazon
|
||||
}
|
||||
|
||||
func (o *redhatBase) isExecScanChangelogs() bool {
|
||||
if o.getServerInfo().Mode.IsOffline() ||
|
||||
o.getServerInfo().Mode.IsFast() ||
|
||||
o.getServerInfo().Mode.IsFastRoot() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *redhatBase) isExecYumPS() bool {
|
||||
// RedHat has no yum-ps
|
||||
switch o.Distro.Family {
|
||||
@@ -526,658 +492,6 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *redhatBase) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
|
||||
if o.isExecFillChangelogs() {
|
||||
if err := o.fillChangelogs(updatable); err != nil {
|
||||
err = xerrors.Errorf("Failed to fetch changelogs: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
|
||||
if o.isExecScanUsingYum() {
|
||||
return o.scanUsingYum(updatable)
|
||||
}
|
||||
|
||||
// Parse changelog because CentOS does not have security channel...
|
||||
if o.isExecScanChangelogs() {
|
||||
return o.scanChangelogs(updatable)
|
||||
}
|
||||
|
||||
return models.VulnInfos{}, nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) fillChangelogs(updatables models.Packages) error {
|
||||
names := []string{}
|
||||
for name := range updatables {
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
if err := o.fillDiffChangelogs(names); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
emptyChangelogPackNames := []string{}
|
||||
for _, name := range names {
|
||||
if o.Packages[name].Changelog.Contents == "" {
|
||||
emptyChangelogPackNames = append(emptyChangelogPackNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for _, name := range emptyChangelogPackNames {
|
||||
i++
|
||||
o.log.Infof("(%d/%d) Fetched Changelogs %s", i, len(emptyChangelogPackNames), name)
|
||||
if err := o.fillDiffChangelogs([]string{name}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) getAvailableChangelogs(packNames []string) (map[string]string, error) {
|
||||
yumopts := ""
|
||||
if 0 < len(o.getServerInfo().Enablerepo) {
|
||||
yumopts = " --enablerepo=" + strings.Join(o.getServerInfo().Enablerepo, ",")
|
||||
}
|
||||
if config.Conf.SkipBroken {
|
||||
yumopts += " --skip-broken"
|
||||
}
|
||||
if o.hasYumColorOption() {
|
||||
yumopts += " --color=never"
|
||||
}
|
||||
cmd := `yum changelog all updates %s %s | grep -A 1000000 "==================== Updated Packages ===================="`
|
||||
cmd = fmt.Sprintf(cmd, yumopts, strings.Join(packNames, " "))
|
||||
|
||||
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumChangelog())
|
||||
if !r.isSuccess(0, 1) {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
return o.divideChangelogsIntoEachPackages(r.Stdout), nil
|
||||
}
|
||||
|
||||
// Divide available change logs of all updatable packages into each package's changelog
|
||||
func (o *redhatBase) divideChangelogsIntoEachPackages(stdout string) map[string]string {
|
||||
changelogs := make(map[string]string)
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
|
||||
crlf, newBlock := false, true
|
||||
packNameVer, contents := "", []string{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "==================== Updated Packages ====================") {
|
||||
continue
|
||||
}
|
||||
if len(strings.TrimSpace(line)) != 0 && newBlock {
|
||||
left := strings.Fields(line)[0]
|
||||
// ss := strings.Split(left, ".")
|
||||
// packNameVer = strings.Join(ss[0:len(ss)-1], ".")
|
||||
packNameVer = left
|
||||
newBlock = false
|
||||
continue
|
||||
}
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
if crlf {
|
||||
changelogs[packNameVer] = strings.Join(contents, "\n")
|
||||
packNameVer = ""
|
||||
contents = []string{}
|
||||
newBlock = true
|
||||
crlf = false
|
||||
} else {
|
||||
contents = append(contents, line)
|
||||
crlf = true
|
||||
}
|
||||
} else {
|
||||
contents = append(contents, line)
|
||||
crlf = false
|
||||
}
|
||||
}
|
||||
if 0 < len(contents) {
|
||||
changelogs[packNameVer] = strings.Join(contents, "\n")
|
||||
}
|
||||
return changelogs
|
||||
}
|
||||
|
||||
func (o *redhatBase) fillDiffChangelogs(packNames []string) error {
|
||||
changelogs, err := o.getAvailableChangelogs(packNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for s := range changelogs {
|
||||
// name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
|
||||
name, pack, found := o.Packages.FindOne(func(p models.Package) bool {
|
||||
var epochNameVerRel string
|
||||
if index := strings.Index(p.NewVersion, ":"); 0 < index {
|
||||
epoch := p.NewVersion[0:index]
|
||||
ver := p.NewVersion[index+1 : len(p.NewVersion)]
|
||||
epochNameVerRel = fmt.Sprintf("%s:%s-%s", epoch, p.Name, ver)
|
||||
} else {
|
||||
epochNameVerRel = fmt.Sprintf("%s-%s", p.Name, p.NewVersion)
|
||||
}
|
||||
return strings.HasPrefix(s, epochNameVerRel)
|
||||
})
|
||||
|
||||
if found {
|
||||
var detectionMethod string
|
||||
diff, err := o.getDiffChangelog(pack, changelogs[s])
|
||||
if err == nil {
|
||||
detectionMethod = models.ChangelogExactMatchStr
|
||||
} else {
|
||||
o.log.Debug(err)
|
||||
// Try without epoch
|
||||
if index := strings.Index(pack.Version, ":"); 0 < index {
|
||||
pack.Version = pack.Version[index+1 : len(pack.Version)]
|
||||
o.log.Debug("Try without epoch", pack)
|
||||
diff, err = o.getDiffChangelog(pack, changelogs[s])
|
||||
if err != nil {
|
||||
o.log.Debugf("Failed to find the version in changelog: %s-%s-%s",
|
||||
pack.Name, pack.Version, pack.Release)
|
||||
if len(diff) == 0 {
|
||||
detectionMethod = models.FailedToGetChangelog
|
||||
} else {
|
||||
detectionMethod = models.FailedToFindVersionInChangelog
|
||||
diff = ""
|
||||
}
|
||||
} else {
|
||||
o.log.Debugf("Found the version in changelog without epoch: %s-%s-%s",
|
||||
pack.Name, pack.Version, pack.Release)
|
||||
detectionMethod = models.ChangelogLenientMatchStr
|
||||
}
|
||||
} else {
|
||||
if len(diff) == 0 {
|
||||
detectionMethod = models.FailedToGetChangelog
|
||||
} else {
|
||||
detectionMethod = models.FailedToFindVersionInChangelog
|
||||
diff = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pack = o.Packages[name]
|
||||
pack.Changelog = models.Changelog{
|
||||
Contents: diff,
|
||||
Method: models.DetectionMethod(detectionMethod),
|
||||
}
|
||||
o.Packages[name] = pack
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) getDiffChangelog(pack models.Package, availableChangelog string) (string, error) {
|
||||
installedVer := ver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
|
||||
scanner := bufio.NewScanner(strings.NewReader(availableChangelog))
|
||||
diff := []string{}
|
||||
found := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.HasPrefix(line, "* ") {
|
||||
diff = append(diff, line)
|
||||
continue
|
||||
}
|
||||
|
||||
// openssh on RHEL
|
||||
// openssh-server-6.6.1p1-35.el7_3.x86_64 rhui-rhel-7-server-rhui-rpms
|
||||
// Wed Mar 1 21:00:00 2017 Jakub Jelen <jjelen@redhat.com> - 6.6.1p1-35 + 0.9.3-9
|
||||
ss := strings.Split(line, " + ")
|
||||
if 1 < len(ss) {
|
||||
line = ss[0]
|
||||
}
|
||||
|
||||
ss = strings.Split(line, " ")
|
||||
if len(ss) < 2 {
|
||||
diff = append(diff, line)
|
||||
continue
|
||||
}
|
||||
v := ss[len(ss)-1]
|
||||
v = strings.TrimPrefix(v, "-")
|
||||
v = strings.TrimPrefix(v, "[")
|
||||
v = strings.TrimSuffix(v, "]")
|
||||
|
||||
// On Amazon often end with email address. <aaa@aaa.com> Go to next line
|
||||
if strings.HasPrefix(v, "<") && strings.HasSuffix(v, ">") {
|
||||
diff = append(diff, line)
|
||||
continue
|
||||
}
|
||||
|
||||
version := ver.NewVersion(v)
|
||||
if installedVer.Equal(version) || installedVer.GreaterThan(version) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
diff = append(diff, line)
|
||||
}
|
||||
|
||||
if len(diff) == 0 || !found {
|
||||
return availableChangelog,
|
||||
xerrors.Errorf("Failed to find the version in changelog: %s-%s-%s",
|
||||
pack.Name, pack.Version, pack.Release)
|
||||
}
|
||||
return strings.TrimSpace(strings.Join(diff, "\n")), nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) scanChangelogs(updatable models.Packages) (models.VulnInfos, error) {
|
||||
packCveIDs := make(map[string][]string)
|
||||
for name := range updatable {
|
||||
cveIDs := []string{}
|
||||
pack := o.Packages[name]
|
||||
if pack.Changelog.Method == models.FailedToFindVersionInChangelog {
|
||||
continue
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(pack.Changelog.Contents))
|
||||
for scanner.Scan() {
|
||||
if matches := cveRe.FindAllString(scanner.Text(), -1); 0 < len(matches) {
|
||||
for _, m := range matches {
|
||||
cveIDs = util.AppendIfMissing(cveIDs, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
packCveIDs[name] = cveIDs
|
||||
}
|
||||
|
||||
// transform datastructure
|
||||
// - From
|
||||
// "packname": []{"CVE-2017-1111", ".../
|
||||
//
|
||||
// - To
|
||||
// map {
|
||||
// "CVE-2017-1111": "packname",
|
||||
// }
|
||||
vinfos := models.VulnInfos{}
|
||||
for name, cveIDs := range packCveIDs {
|
||||
for _, cid := range cveIDs {
|
||||
if v, ok := vinfos[cid]; ok {
|
||||
v.AffectedPackages = append(v.AffectedPackages, models.PackageFixStatus{Name: name})
|
||||
vinfos[cid] = v
|
||||
} else {
|
||||
vinfos[cid] = models.VulnInfo{
|
||||
CveID: cid,
|
||||
AffectedPackages: models.PackageFixStatuses{{Name: name}},
|
||||
Confidences: models.Confidences{models.ChangelogExactMatch},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vinfos, nil
|
||||
}
|
||||
|
||||
type distroAdvisoryCveIDs struct {
|
||||
DistroAdvisory models.DistroAdvisory
|
||||
CveIDs []string
|
||||
}
|
||||
|
||||
// Scaning unsecure packages using yum-plugin-security.
|
||||
// Amazon, RHEL, Oracle Linux
|
||||
func (o *redhatBase) scanUsingYum(updatable models.Packages) (models.VulnInfos, error) {
|
||||
if o.Distro.Family == config.CentOS {
|
||||
// CentOS has no security channel.
|
||||
return nil, xerrors.New(
|
||||
"yum updateinfo is not suppported on CentOS")
|
||||
}
|
||||
|
||||
// get advisoryID(RHSA, ALAS, ELSA) - package name,version
|
||||
major, err := (o.Distro.MajorVersion())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Not implemented yet: %s, err: %w", o.Distro, err)
|
||||
}
|
||||
|
||||
var cmd string
|
||||
if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major > 5 {
|
||||
cmd = "yum repolist --color=never"
|
||||
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumRepolist())
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major == 5 {
|
||||
cmd = "yum list-security --security"
|
||||
if o.hasYumColorOption() {
|
||||
cmd += " --color=never"
|
||||
}
|
||||
} else {
|
||||
cmd = "yum updateinfo list updates --security --color=never"
|
||||
}
|
||||
r := o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
|
||||
|
||||
dict := make(map[string]models.Packages)
|
||||
for _, advIDPackNames := range advIDPackNamesList {
|
||||
packages := models.Packages{}
|
||||
for _, packName := range advIDPackNames.PackNames {
|
||||
pack, found := updatable[packName]
|
||||
if !found {
|
||||
return nil, xerrors.Errorf(
|
||||
"Package not found. pack: %#v", packName)
|
||||
}
|
||||
packages[pack.Name] = pack
|
||||
continue
|
||||
}
|
||||
dict[advIDPackNames.AdvisoryID] = packages
|
||||
}
|
||||
|
||||
// get advisoryID(RHSA, ALAS, ELSA) - CVE IDs
|
||||
if (o.Distro.Family == config.RedHat || o.Distro.Family == config.Oracle) && major == 5 {
|
||||
cmd = "yum info-security"
|
||||
if o.hasYumColorOption() {
|
||||
cmd += " --color=never"
|
||||
}
|
||||
} else {
|
||||
cmd = "yum updateinfo updates --security --color=never"
|
||||
}
|
||||
r = o.exec(util.PrependProxyEnv(cmd), o.sudo.yumUpdateInfo())
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All information collected.
|
||||
// Convert to VulnInfos.
|
||||
vinfos := models.VulnInfos{}
|
||||
for _, advIDCveIDs := range advisoryCveIDsList {
|
||||
for _, cveID := range advIDCveIDs.CveIDs {
|
||||
vinfo, found := vinfos[cveID]
|
||||
if found {
|
||||
advAppended := append(vinfo.DistroAdvisories, advIDCveIDs.DistroAdvisory)
|
||||
vinfo.DistroAdvisories = advAppended
|
||||
|
||||
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
|
||||
for _, pack := range packs {
|
||||
vinfo.AffectedPackages = append(vinfo.AffectedPackages,
|
||||
models.PackageFixStatus{Name: pack.Name})
|
||||
}
|
||||
} else {
|
||||
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
|
||||
affected := models.PackageFixStatuses{}
|
||||
for _, p := range packs {
|
||||
affected = append(affected, models.PackageFixStatus{Name: p.Name})
|
||||
}
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: cveID,
|
||||
DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory},
|
||||
AffectedPackages: affected,
|
||||
Confidences: models.Confidences{models.YumUpdateSecurityMatch},
|
||||
}
|
||||
}
|
||||
vinfos[cveID] = vinfo
|
||||
}
|
||||
}
|
||||
return vinfos, nil
|
||||
}
|
||||
|
||||
var horizontalRulePattern = regexp.MustCompile(`^=+$`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveIDs, err error) {
|
||||
sectionState := Outside
|
||||
lines := strings.Split(stdout, "\n")
|
||||
lines = append(lines, "=============")
|
||||
|
||||
// Amazon Linux AMI Security Information
|
||||
advisory := models.DistroAdvisory{}
|
||||
|
||||
cveIDsSetInThisSection := make(map[string]bool)
|
||||
|
||||
// use this flag to Collect CVE IDs in CVEs field.
|
||||
inDesctiption, inCves := false, false
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// find the new section pattern
|
||||
if horizontalRulePattern.MatchString(line) {
|
||||
// set previous section's result to return-variable
|
||||
if sectionState == Content {
|
||||
foundCveIDs := []string{}
|
||||
for cveID := range cveIDsSetInThisSection {
|
||||
foundCveIDs = append(foundCveIDs, cveID)
|
||||
}
|
||||
result = append(result, distroAdvisoryCveIDs{
|
||||
DistroAdvisory: advisory,
|
||||
CveIDs: foundCveIDs,
|
||||
})
|
||||
|
||||
// reset for next section.
|
||||
cveIDsSetInThisSection = make(map[string]bool)
|
||||
inDesctiption, inCves = false, false
|
||||
advisory = models.DistroAdvisory{}
|
||||
}
|
||||
|
||||
// Go to next section
|
||||
sectionState = o.changeSectionState(sectionState)
|
||||
continue
|
||||
}
|
||||
|
||||
switch sectionState {
|
||||
case Header:
|
||||
switch o.Distro.Family {
|
||||
case config.CentOS:
|
||||
// CentOS has no security channel.
|
||||
return result, xerrors.New(
|
||||
"yum updateinfo is not suppported on CentOS")
|
||||
case config.RedHat, config.Amazon, config.Oracle:
|
||||
// nop
|
||||
}
|
||||
|
||||
case Content:
|
||||
if found := o.isDescriptionLine(line); found {
|
||||
inDesctiption, inCves = true, false
|
||||
ss := strings.Split(line, " : ")
|
||||
advisory.Description += fmt.Sprintf("%s\n",
|
||||
strings.Join(ss[1:], " : "))
|
||||
continue
|
||||
}
|
||||
|
||||
// severity
|
||||
if severity, found := o.parseYumUpdateinfoToGetSeverity(line); found {
|
||||
advisory.Severity = severity
|
||||
continue
|
||||
}
|
||||
|
||||
// No need to parse in description except severity
|
||||
if inDesctiption {
|
||||
if ss := strings.Split(line, ": "); 1 < len(ss) {
|
||||
advisory.Description += fmt.Sprintf("%s\n",
|
||||
strings.Join(ss[1:], ": "))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if found := o.isCvesHeaderLine(line); found {
|
||||
inCves = true
|
||||
ss := strings.Split(line, "CVEs : ")
|
||||
line = strings.Join(ss[1:], " ")
|
||||
cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
|
||||
for _, cveID := range cveIDs {
|
||||
cveIDsSetInThisSection[cveID] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if inCves {
|
||||
cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
|
||||
for _, cveID := range cveIDs {
|
||||
cveIDsSetInThisSection[cveID] = true
|
||||
}
|
||||
}
|
||||
|
||||
advisoryID, found := o.parseYumUpdateinfoToGetAdvisoryID(line)
|
||||
if found {
|
||||
advisory.AdvisoryID = advisoryID
|
||||
continue
|
||||
}
|
||||
|
||||
issued, found := o.parseYumUpdateinfoLineToGetIssued(line)
|
||||
if found {
|
||||
advisory.Issued = issued
|
||||
continue
|
||||
}
|
||||
|
||||
updated, found := o.parseYumUpdateinfoLineToGetUpdated(line)
|
||||
if found {
|
||||
advisory.Updated = updated
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// state
|
||||
const (
|
||||
Outside = iota
|
||||
Header = iota
|
||||
Content = iota
|
||||
)
|
||||
|
||||
func (o *redhatBase) changeSectionState(state int) (newState int) {
|
||||
switch state {
|
||||
case Outside, Content:
|
||||
newState = Header
|
||||
case Header:
|
||||
newState = Content
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
func (o *redhatBase) isCvesHeaderLine(line string) bool {
|
||||
return strings.Contains(line, "CVEs : ")
|
||||
}
|
||||
|
||||
var yumCveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoLineToGetCveIDs(line string) []string {
|
||||
return yumCveIDPattern.FindAllString(line, -1)
|
||||
}
|
||||
|
||||
var yumAdvisoryIDPattern = regexp.MustCompile(`^ *Update ID : (.*)$`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoToGetAdvisoryID(line string) (advisoryID string, found bool) {
|
||||
result := yumAdvisoryIDPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(result[1]), true
|
||||
}
|
||||
|
||||
var yumIssuedPattern = regexp.MustCompile(`^\s*Issued : (\d{4}-\d{2}-\d{2})`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoLineToGetIssued(line string) (date time.Time, found bool) {
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, yumIssuedPattern)
|
||||
}
|
||||
|
||||
var yumUpdatedPattern = regexp.MustCompile(`^\s*Updated : (\d{4}-\d{2}-\d{2})`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoLineToGetUpdated(line string) (date time.Time, found bool) {
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, yumUpdatedPattern)
|
||||
}
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoLineToGetDate(line string, regexpPattern *regexp.Regexp) (date time.Time, found bool) {
|
||||
result := regexpPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return date, false
|
||||
}
|
||||
t, err := time.Parse("2006-01-02", result[1])
|
||||
if err != nil {
|
||||
return date, false
|
||||
}
|
||||
return t, true
|
||||
}
|
||||
|
||||
var yumDescriptionPattern = regexp.MustCompile(`^\s*Description : `)
|
||||
|
||||
func (o *redhatBase) isDescriptionLine(line string) bool {
|
||||
return yumDescriptionPattern.MatchString(line)
|
||||
}
|
||||
|
||||
var yumSeverityPattern = regexp.MustCompile(`^ *Severity : (.*)$`)
|
||||
|
||||
func (o *redhatBase) parseYumUpdateinfoToGetSeverity(line string) (severity string, found bool) {
|
||||
result := yumSeverityPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(result[1]), true
|
||||
}
|
||||
|
||||
type advisoryIDPacks struct {
|
||||
AdvisoryID string
|
||||
PackNames []string
|
||||
}
|
||||
|
||||
type advisoryIDPacksList []advisoryIDPacks
|
||||
|
||||
func (list advisoryIDPacksList) find(advisoryID string) (advisoryIDPacks, bool) {
|
||||
for _, a := range list {
|
||||
if a.AdvisoryID == advisoryID {
|
||||
return a, true
|
||||
}
|
||||
}
|
||||
return advisoryIDPacks{}, false
|
||||
}
|
||||
func (o *redhatBase) extractPackNameVerRel(nameVerRel string) (name, ver, rel string) {
|
||||
fields := strings.Split(nameVerRel, ".")
|
||||
archTrimed := strings.Join(fields[0:len(fields)-1], ".")
|
||||
|
||||
fields = strings.Split(archTrimed, "-")
|
||||
rel = fields[len(fields)-1]
|
||||
ver = fields[len(fields)-2]
|
||||
name = strings.Join(fields[0:(len(fields)-2)], "-")
|
||||
return
|
||||
}
|
||||
|
||||
// parseYumUpdateinfoListAvailable collect AdvisorID(RHSA, ALAS, ELSA), packages
|
||||
func (o *redhatBase) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacksList, error) {
|
||||
result := []advisoryIDPacks{}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
|
||||
if !(strings.HasPrefix(line, "RHSA") ||
|
||||
strings.HasPrefix(line, "ALAS") ||
|
||||
strings.HasPrefix(line, "ELSA")) {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 3 {
|
||||
return []advisoryIDPacks{}, xerrors.Errorf(
|
||||
"Unknown format. line: %s", line)
|
||||
}
|
||||
|
||||
// extract fields
|
||||
advisoryID := fields[0]
|
||||
packVersion := fields[2]
|
||||
packName, _, _ := o.extractPackNameVerRel(packVersion)
|
||||
|
||||
found := false
|
||||
for i, s := range result {
|
||||
if s.AdvisoryID == advisoryID {
|
||||
names := s.PackNames
|
||||
names = append(names, packName)
|
||||
result[i].PackNames = names
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
result = append(result, advisoryIDPacks{
|
||||
AdvisoryID: advisoryID,
|
||||
PackNames: []string{packName},
|
||||
})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *redhatBase) yumPS() error {
|
||||
cmd := "LANGUAGE=en_US.UTF-8 yum info yum"
|
||||
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
|
||||
Reference in New Issue
Block a user