Merge pull request #506 from openSUSE/reintroduce-suse-opensuse
Reintroduce image scanning for openSUSE and SLE
This commit is contained in:
commit
b08ad9b8e6
@ -23,6 +23,7 @@ All of these components can be found in the `ext/` directory.
|
||||
| [Ubuntu CVE Tracker] | Ubuntu 12.04, 12.10, 13.04, 14.04, 14.10, 15.04, 15.10, 16.04 namespaces | [dpkg] | [GPLv2] |
|
||||
| [Red Hat Security Data] | CentOS 5, 6, 7 namespaces | [rpm] | [CVRF] |
|
||||
| [Oracle Linux Security Data] | Oracle Linux 5, 6, 7 namespaces | [rpm] | [CVRF] |
|
||||
| [SUSE OVAL Descriptions] | openSUSE, SUSE Linux Enterprise namespaces | [rpm] | [CC-BY-NC-4.0] |
|
||||
| [Alpine SecDB] | Alpine 3.3, Alpine 3.4, Alpine 3.5 namespaces | [apk] | [MIT] |
|
||||
| [NIST NVD] | Generic Vulnerability Metadata | N/A | [Public Domain] |
|
||||
|
||||
@ -30,6 +31,7 @@ All of these components can be found in the `ext/` directory.
|
||||
[Ubuntu CVE Tracker]: https://launchpad.net/ubuntu-cve-tracker
|
||||
[Red Hat Security Data]: https://www.redhat.com/security/data/metrics
|
||||
[Oracle Linux Security Data]: https://linux.oracle.com/security/
|
||||
[SUSE OVAL Descriptions]: https://www.suse.com/de-de/support/security/oval/
|
||||
[NIST NVD]: https://nvd.nist.gov
|
||||
[dpkg]: https://en.wikipedia.org/wiki/dpkg
|
||||
[rpm]: http://www.rpm.org
|
||||
@ -40,6 +42,7 @@ All of these components can be found in the `ext/` directory.
|
||||
[Alpine SecDB]: http://git.alpinelinux.org/cgit/alpine-secdb/
|
||||
[apk]: http://git.alpinelinux.org/cgit/apk-tools/
|
||||
[MIT]: https://gist.github.com/jzelinskie/6da1e2da728424d88518be2adbd76979
|
||||
[CC-BY-NC-4.0]: https://creativecommons.org/licenses/by-nc/4.0/]
|
||||
|
||||
## Adding new drivers
|
||||
|
||||
|
@ -58,6 +58,7 @@ import (
|
||||
_ "github.com/coreos/clair/ext/vulnsrc/debian"
|
||||
_ "github.com/coreos/clair/ext/vulnsrc/oracle"
|
||||
_ "github.com/coreos/clair/ext/vulnsrc/rhel"
|
||||
_ "github.com/coreos/clair/ext/vulnsrc/suse"
|
||||
_ "github.com/coreos/clair/ext/vulnsrc/ubuntu"
|
||||
)
|
||||
|
||||
|
@ -84,7 +84,7 @@ func (d detector) Detect(files tarutil.FilesMap) (*database.Namespace, error) {
|
||||
switch OS {
|
||||
case "debian", "ubuntu":
|
||||
versionFormat = dpkg.ParserName
|
||||
case "centos", "rhel", "fedora", "amzn", "ol", "oracle":
|
||||
case "centos", "rhel", "fedora", "amzn", "ol", "oracle", "opensuse", "sles":
|
||||
versionFormat = rpm.ParserName
|
||||
default:
|
||||
return nil, nil
|
||||
|
478
ext/vulnsrc/suse/suse.go
Normal file
478
ext/vulnsrc/suse/suse.go
Normal file
@ -0,0 +1,478 @@
|
||||
// Copyright 2017 clair authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package suse implements a vulnerability source updater using the
|
||||
// SUSE Linux and openSUSE OVAL Database.
|
||||
package suse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/ext/versionfmt"
|
||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||
"github.com/coreos/clair/ext/vulnsrc"
|
||||
"github.com/coreos/clair/pkg/commonerr"
|
||||
)
|
||||
|
||||
const (
|
||||
ovalURI = "http://ftp.suse.com/pub/projects/security/oval/"
|
||||
|
||||
// "Thu, 30 Nov 2017 03:07:57 GMT
|
||||
timeFormatLastModified = "Mon, 2 Jan 2006 15:04:05 MST"
|
||||
|
||||
// timestamp format 2017-10-23T04:07:14
|
||||
timeFormatOVAL = "2006-1-2T15:04:05"
|
||||
)
|
||||
|
||||
var (
|
||||
ignoredCriterions []string
|
||||
suseOpenSUSEInstalledCommentRegexp = regexp.MustCompile(`(SUSE Linux Enterprise |openSUSE ).*is installed`)
|
||||
suseInstalledCommentRegexp = regexp.MustCompile(`SUSE Linux Enterprise[A-Za-z\s]*? (\d+)[\w\s]*?(SP(\d+))? is installed`)
|
||||
)
|
||||
|
||||
type oval struct {
|
||||
Timestamp string `xml:"generator>timestamp"`
|
||||
Definitions []definition `xml:"definitions>definition"`
|
||||
}
|
||||
|
||||
type definition struct {
|
||||
Title string `xml:"metadata>title"`
|
||||
Description string `xml:"metadata>description"`
|
||||
References []reference `xml:"metadata>reference"`
|
||||
Criteria criteria `xml:"criteria"`
|
||||
}
|
||||
|
||||
type reference struct {
|
||||
Source string `xml:"source,attr"`
|
||||
URI string `xml:"ref_url,attr"`
|
||||
}
|
||||
|
||||
type criteria struct {
|
||||
Operator string `xml:"operator,attr"`
|
||||
Criterias []*criteria `xml:"criteria"`
|
||||
Criterions []criterion `xml:"criterion"`
|
||||
}
|
||||
|
||||
type criterion struct {
|
||||
Comment string `xml:"comment,attr"`
|
||||
}
|
||||
|
||||
type flavor int
|
||||
|
||||
const (
|
||||
SUSE flavor = iota
|
||||
OpenSUSE
|
||||
)
|
||||
|
||||
type updater struct {
|
||||
Name string
|
||||
NamespaceName string
|
||||
FilePrefix string
|
||||
UpdaterFlag string
|
||||
FileRegexp *regexp.Regexp
|
||||
}
|
||||
|
||||
func newUpdater(f flavor) updater {
|
||||
var up updater
|
||||
|
||||
switch f {
|
||||
case SUSE:
|
||||
up.Name = "SUSE Linux"
|
||||
up.NamespaceName = "sles"
|
||||
up.FilePrefix = "suse.linux.enterprise."
|
||||
up.UpdaterFlag = "SUSEUpdater"
|
||||
up.FileRegexp = regexp.MustCompile(`suse.linux.enterprise.(\d+).xml`)
|
||||
case OpenSUSE:
|
||||
up.Name = "openSUSE"
|
||||
up.NamespaceName = "opensuse"
|
||||
up.FilePrefix = "opensuse.leap."
|
||||
up.UpdaterFlag = "openSUSEUpdater"
|
||||
up.FileRegexp = regexp.MustCompile(`opensuse.leap.(\d+\.*\d*).xml`)
|
||||
default:
|
||||
panic("tried to create an updater for an unrecognized flavor of openSUSE/SUSE")
|
||||
}
|
||||
|
||||
return up
|
||||
}
|
||||
|
||||
func init() {
|
||||
suseUpdater := newUpdater(SUSE)
|
||||
openSUSEUpdater := newUpdater(OpenSUSE)
|
||||
vulnsrc.RegisterUpdater("suse", &suseUpdater)
|
||||
vulnsrc.RegisterUpdater("opensuse", &openSUSEUpdater)
|
||||
}
|
||||
|
||||
func (u *updater) Update(datastore database.Datastore) (resp vulnsrc.UpdateResponse, err error) {
|
||||
log.WithField("package", u.Name).Info("Start fetching vulnerabilities")
|
||||
|
||||
tx, err := datastore.Begin()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// openSUSE and SUSE have one single xml file for all the products, there are no incremental
|
||||
// xml files. We store into the database the value of the generation timestamp
|
||||
// of the latest file we parsed.
|
||||
flagValue, ok, err := tx.FindKeyValue(u.UpdaterFlag)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
log.WithField("flagvalue", flagValue).Debug("Generation timestamp of latest parsed file")
|
||||
|
||||
if !ok {
|
||||
flagValue = "0"
|
||||
}
|
||||
|
||||
// this contains the modification time of the most recent
|
||||
// file expressed as unix time (int64)
|
||||
latestOval, err := strconv.ParseInt(flagValue, 10, 64)
|
||||
if err != nil {
|
||||
// something went wrong, force parsing of all files
|
||||
latestOval = 0
|
||||
}
|
||||
|
||||
// Fetch the update list.
|
||||
r, err := http.Get(ovalURI)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Cannot download SUSE update list: %v", err)
|
||||
return resp, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var ovalFiles []string
|
||||
var generationTimes []int64
|
||||
|
||||
scanner := bufio.NewScanner(r.Body)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
r := u.FileRegexp.FindStringSubmatch(line)
|
||||
if len(r) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
ovalFile := ovalURI + u.FilePrefix + r[1] + ".xml"
|
||||
log.WithFields(log.Fields{
|
||||
"ovalFile": ovalFile,
|
||||
"updater": u.Name,
|
||||
}).Debug("file to check")
|
||||
|
||||
// Do not fetch the entire file to get the value of the
|
||||
// creation time. Rely on the "latest modified time"
|
||||
// value of the file hosted on the remote server.
|
||||
timestamp, err := getLatestModifiedTime(ovalFile)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("ovalFile", ovalFile).Warning("Ignoring OVAL file")
|
||||
}
|
||||
|
||||
if timestamp > latestOval {
|
||||
ovalFiles = append(ovalFiles, ovalFile)
|
||||
}
|
||||
}
|
||||
|
||||
for _, oval := range ovalFiles {
|
||||
// Download the oval XML file.
|
||||
r, err := http.Get(oval)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not download", u.Name, "update list")
|
||||
return resp, commonerr.ErrCouldNotDownload
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
match := u.FileRegexp.FindStringSubmatch(oval)
|
||||
if len(match) != 2 {
|
||||
log.Error("Skipping ", oval, "because it's not possible to extract osVersion")
|
||||
continue
|
||||
}
|
||||
osVersion := match[1]
|
||||
|
||||
// Parse the XML.
|
||||
vs, generationTime, err := parseOval(r.Body, u.NamespaceName, osVersion)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
generationTimes = append(generationTimes, generationTime)
|
||||
|
||||
// Collect vulnerabilities.
|
||||
resp.Vulnerabilities = append(resp.Vulnerabilities, vs...)
|
||||
}
|
||||
|
||||
// Set the flag if we found anything.
|
||||
if len(generationTimes) > 0 {
|
||||
resp.FlagName = u.UpdaterFlag
|
||||
resp.FlagValue = strconv.FormatInt(latest(generationTimes), 10)
|
||||
} else {
|
||||
log.WithField("package", u.Name).Debug("no update")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Get the latest modification time of a remote file
|
||||
// expressed as unix time
|
||||
func getLatestModifiedTime(url string) (int64, error) {
|
||||
resp, err := http.Head(url)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
last_modified := resp.Header.Get("Last-Modified")
|
||||
if len(last_modified) == 0 {
|
||||
return 0, fmt.Errorf("last modified header missing")
|
||||
}
|
||||
|
||||
timestamp, err := time.Parse(timeFormatLastModified, last_modified)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return timestamp.Unix(), nil
|
||||
}
|
||||
|
||||
func latest(values []int64) (ret int64) {
|
||||
for _, element := range values {
|
||||
if element > ret {
|
||||
ret = element
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (u *updater) Clean() {}
|
||||
|
||||
func parseOval(ovalReader io.Reader, osFlavor, osVersion string) (vulnerabilities []database.VulnerabilityWithAffected, generationTime int64, err error) {
|
||||
// Decode the XML.
|
||||
var ov oval
|
||||
err = xml.NewDecoder(ovalReader).Decode(&ov)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("could not decode XML")
|
||||
err = commonerr.ErrCouldNotParse
|
||||
return
|
||||
}
|
||||
|
||||
timestamp, err := time.Parse(timeFormatOVAL, ov.Timestamp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
generationTime = timestamp.Unix()
|
||||
|
||||
// Iterate over the definitions and collect any vulnerabilities that affect
|
||||
// at least one package.
|
||||
for _, definition := range ov.Definitions {
|
||||
pkgs := toFeatureVersions(definition.Criteria, osFlavor, osVersion)
|
||||
if len(pkgs) > 0 {
|
||||
vulnerability := database.VulnerabilityWithAffected{
|
||||
Vulnerability: database.Vulnerability{
|
||||
Name: name(definition),
|
||||
Link: link(definition),
|
||||
Severity: severity(definition),
|
||||
Description: description(definition),
|
||||
},
|
||||
}
|
||||
vulnerability.Affected = append(vulnerability.Affected, pkgs...)
|
||||
vulnerabilities = append(vulnerabilities, vulnerability)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getCriterions(node criteria) [][]criterion {
|
||||
// Filter useless criterions.
|
||||
var criterions []criterion
|
||||
for _, c := range node.Criterions {
|
||||
ignored := false
|
||||
|
||||
for _, ignoredItem := range ignoredCriterions {
|
||||
if strings.Contains(c.Comment, ignoredItem) {
|
||||
ignored = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ignored {
|
||||
criterions = append(criterions, c)
|
||||
}
|
||||
}
|
||||
|
||||
if node.Operator == "AND" {
|
||||
return [][]criterion{criterions}
|
||||
} else if node.Operator == "OR" {
|
||||
var possibilities [][]criterion
|
||||
for _, c := range criterions {
|
||||
possibilities = append(possibilities, []criterion{c})
|
||||
}
|
||||
return possibilities
|
||||
}
|
||||
|
||||
return [][]criterion{}
|
||||
}
|
||||
|
||||
func getPossibilities(node criteria) [][]criterion {
|
||||
if len(node.Criterias) == 0 {
|
||||
return getCriterions(node)
|
||||
}
|
||||
|
||||
var possibilitiesToCompose [][][]criterion
|
||||
for _, criteria := range node.Criterias {
|
||||
possibilitiesToCompose = append(possibilitiesToCompose, getPossibilities(*criteria))
|
||||
}
|
||||
if len(node.Criterions) > 0 {
|
||||
possibilitiesToCompose = append(possibilitiesToCompose, getCriterions(node))
|
||||
}
|
||||
|
||||
var possibilities [][]criterion
|
||||
if node.Operator == "AND" {
|
||||
for _, possibility := range possibilitiesToCompose[0] {
|
||||
possibilities = append(possibilities, possibility)
|
||||
}
|
||||
|
||||
for _, possibilityGroup := range possibilitiesToCompose[1:] {
|
||||
var newPossibilities [][]criterion
|
||||
|
||||
for _, possibility := range possibilities {
|
||||
for _, possibilityInGroup := range possibilityGroup {
|
||||
var p []criterion
|
||||
p = append(p, possibility...)
|
||||
p = append(p, possibilityInGroup...)
|
||||
newPossibilities = append(newPossibilities, p)
|
||||
}
|
||||
}
|
||||
|
||||
possibilities = newPossibilities
|
||||
}
|
||||
} else if node.Operator == "OR" {
|
||||
for _, possibilityGroup := range possibilitiesToCompose {
|
||||
for _, possibility := range possibilityGroup {
|
||||
possibilities = append(possibilities, possibility)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return possibilities
|
||||
}
|
||||
|
||||
func toFeatureVersions(criteria criteria, osFlavor, osVersion string) []database.AffectedFeature {
|
||||
// There are duplicates in SUSE .xml files.
|
||||
// This map is for deduplication.
|
||||
featureVersionParameters := make(map[string]database.AffectedFeature)
|
||||
|
||||
possibilities := getPossibilities(criteria)
|
||||
for _, criterions := range possibilities {
|
||||
var featureVersion database.AffectedFeature
|
||||
|
||||
// Attempt to parse package data from trees of criterions.
|
||||
for _, c := range criterions {
|
||||
if match := suseInstalledCommentRegexp.FindStringSubmatch(c.Comment); match != nil {
|
||||
if len(match) == 4 {
|
||||
osVersion = match[1]
|
||||
if match[3] != "" {
|
||||
osVersion = fmt.Sprintf("%s.%s", osVersion, match[3])
|
||||
}
|
||||
} else {
|
||||
log.WithField("comment", c.Comment).Warning("could not extract sles name and version from comment")
|
||||
}
|
||||
}
|
||||
|
||||
if suseOpenSUSEInstalledCommentRegexp.FindStringSubmatch(c.Comment) == nil && strings.HasSuffix(c.Comment, " is installed") {
|
||||
name, version, err := splitPackageNameAndVersion(c.Comment[:len(c.Comment)-len(" is installed")])
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("comment", c.Comment).Warning("Could not extract package name and version from comment")
|
||||
} else {
|
||||
featureVersion.FeatureName = name
|
||||
version := version
|
||||
err := versionfmt.Valid(rpm.ParserName, version)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("version", version).Warning("could not parse package version. skipping")
|
||||
} else {
|
||||
featureVersion.AffectedVersion = version
|
||||
if version != versionfmt.MaxVersion {
|
||||
featureVersion.FixedInVersion = version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
featureVersion.Namespace.Name = fmt.Sprintf("%s:%s", osFlavor, osVersion)
|
||||
featureVersion.Namespace.VersionFormat = rpm.ParserName
|
||||
|
||||
if featureVersion.Namespace.Name != "" && featureVersion.FeatureName != "" && featureVersion.AffectedVersion != "" && featureVersion.FixedInVersion != "" {
|
||||
featureVersionParameters[featureVersion.Namespace.Name+":"+featureVersion.FeatureName] = featureVersion
|
||||
} else {
|
||||
log.WithField("criterions", fmt.Sprintf("%v", criterions)).Warning("could not determine a valid package from criterions")
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the map to slice.
|
||||
var featureVersionParametersArray []database.AffectedFeature
|
||||
for _, fv := range featureVersionParameters {
|
||||
featureVersionParametersArray = append(featureVersionParametersArray, fv)
|
||||
}
|
||||
|
||||
return featureVersionParametersArray
|
||||
}
|
||||
|
||||
func description(def definition) (desc string) {
|
||||
// It is much more faster to proceed like this than using a Replacer.
|
||||
desc = strings.Replace(def.Description, "\n\n\n", " ", -1)
|
||||
desc = strings.Replace(desc, "\n\n", " ", -1)
|
||||
desc = strings.Replace(desc, "\n", " ", -1)
|
||||
return
|
||||
}
|
||||
|
||||
func name(def definition) string {
|
||||
return strings.TrimSpace(def.Title)
|
||||
}
|
||||
|
||||
func link(def definition) (link string) {
|
||||
for _, reference := range def.References {
|
||||
if reference.Source == "CVE" {
|
||||
link = reference.URI
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func severity(def definition) (severity database.Severity) {
|
||||
//TODO: handle that once openSUSE/SLE OVAL files have severity info
|
||||
return database.UnknownSeverity
|
||||
}
|
||||
|
||||
func splitPackageNameAndVersion(fullname string) (name, version string, err error) {
|
||||
re := regexp.MustCompile(`-\d+\.`)
|
||||
|
||||
matches := re.FindStringSubmatchIndex(fullname)
|
||||
if matches == nil {
|
||||
err = fmt.Errorf("Cannot extract package name and version from %s", fullname)
|
||||
} else {
|
||||
name = fullname[:matches[0]]
|
||||
version = fullname[matches[0]+1:]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
154
ext/vulnsrc/suse/suse_test.go
Normal file
154
ext/vulnsrc/suse/suse_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2017 clair authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package suse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpenSUSEParser(t *testing.T) {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
path := filepath.Join(filepath.Dir(filename))
|
||||
|
||||
// Test parsing testdata/fetcher_opensuse_test.1.xml
|
||||
testFile, _ := os.Open(path + "/testdata/fetcher_opensuse_test.1.xml")
|
||||
defer testFile.Close()
|
||||
|
||||
u := newUpdater(OpenSUSE)
|
||||
osVersion := "42.3"
|
||||
|
||||
vulnerabilities, generationTime, err := parseOval(testFile, u.NamespaceName, osVersion)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1467000286), generationTime)
|
||||
|
||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||
assert.Equal(t, "CVE-2012-2150", vulnerabilities[0].Name)
|
||||
assert.Equal(t, "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2150", vulnerabilities[0].Link)
|
||||
assert.Equal(t, `xfs_metadump in xfsprogs before 3.2.4 does not properly obfuscate file data, which allows remote attackers to obtain sensitive information by reading a generated image.`, vulnerabilities[0].Description)
|
||||
|
||||
expectedFeatures := []database.AffectedFeature{
|
||||
{
|
||||
Namespace: database.Namespace{
|
||||
Name: fmt.Sprintf("%s:%s", u.NamespaceName, osVersion),
|
||||
VersionFormat: rpm.ParserName,
|
||||
},
|
||||
FeatureName: "xfsprogs",
|
||||
FixedInVersion: "3.2.1-5.1",
|
||||
AffectedVersion: "3.2.1-5.1",
|
||||
},
|
||||
{
|
||||
Namespace: database.Namespace{
|
||||
Name: fmt.Sprintf("%s:%s", u.NamespaceName, osVersion),
|
||||
VersionFormat: rpm.ParserName,
|
||||
},
|
||||
FeatureName: "xfsprogs-devel",
|
||||
FixedInVersion: "3.2.1-5.1",
|
||||
AffectedVersion: "3.2.1-5.1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, expectedFeature := range expectedFeatures {
|
||||
assert.Contains(t, vulnerabilities[0].Affected, expectedFeature)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSUSEParser(t *testing.T) {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
path := filepath.Join(filepath.Dir(filename))
|
||||
|
||||
// Test parsing testdata/fetcher_opensuse_test.1.xml
|
||||
testFile, _ := os.Open(path + "/testdata/fetcher_sle_test.1.xml")
|
||||
defer testFile.Close()
|
||||
|
||||
u := newUpdater(SUSE)
|
||||
osVersion := "12"
|
||||
|
||||
vulnerabilities, generationTime, err := parseOval(testFile, u.NamespaceName, osVersion)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1467000286), generationTime)
|
||||
|
||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||
assert.Equal(t, "CVE-2012-2150", vulnerabilities[0].Name)
|
||||
assert.Equal(t, "http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2150", vulnerabilities[0].Link)
|
||||
assert.Equal(t, `xfs_metadump in xfsprogs before 3.2.4 does not properly obfuscate file data, which allows remote attackers to obtain sensitive information by reading a generated image.`, vulnerabilities[0].Description)
|
||||
|
||||
expectedFeatures := []database.AffectedFeature{
|
||||
{
|
||||
Namespace: database.Namespace{
|
||||
Name: fmt.Sprintf("%s:%s", u.NamespaceName, osVersion),
|
||||
VersionFormat: rpm.ParserName,
|
||||
},
|
||||
FeatureName: "xfsprogs",
|
||||
FixedInVersion: "3.2.1-3.5",
|
||||
AffectedVersion: "3.2.1-3.5",
|
||||
},
|
||||
{
|
||||
Namespace: database.Namespace{
|
||||
Name: "sles:12.1",
|
||||
VersionFormat: rpm.ParserName,
|
||||
},
|
||||
FeatureName: "xfsprogs",
|
||||
FixedInVersion: "3.2.1-3.5",
|
||||
AffectedVersion: "3.2.1-3.5",
|
||||
},
|
||||
}
|
||||
|
||||
for _, expectedFeature := range expectedFeatures {
|
||||
assert.Contains(t, vulnerabilities[0].Affected, expectedFeature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPkgInstalledCommentRegexp(t *testing.T) {
|
||||
testData := map[string][]string{
|
||||
"krb5-1.12.1-19.1 is installed": {"krb5", "1.12.1-19.1"},
|
||||
"krb5-32bit-1.12.1-19.1 is installed": {"krb5-32bit", "1.12.1-19.1"},
|
||||
"krb5-client-1.12.1-19.1 is installed": {"krb5-client", "1.12.1-19.1"},
|
||||
"krb5-plugin-kdb-ldap-1.12.1-19.1 is installed": {"krb5-plugin-kdb-ldap", "1.12.1-19.1"},
|
||||
"sysvinit-tools-2.88+-96.1 is installed": {"sysvinit-tools", "2.88+-96.1"},
|
||||
"ntp-4.2.8p10-63.3 is installed": {"ntp", "4.2.8p10-63.3"},
|
||||
"libid3tag0-0.15.1b-182.58 is installed": {"libid3tag0", "0.15.1b-182.58"},
|
||||
"libopenssl-devel-1.0.2j-55.1 is installed": {"libopenssl-devel", "1.0.2j-55.1"},
|
||||
"libMagickCore-6_Q16-1-6.8.8.1-5.8 is installed": {"libMagickCore-6_Q16-1", "6.8.8.1-5.8"},
|
||||
"libGraphicsMagick++-Q16-12-1.3.25-11.44.1 is installed": {"libGraphicsMagick++-Q16-12", "1.3.25-11.44.1"},
|
||||
"freerdp-2.0.0~git.1463131968.4e66df7-11.69 is installed": {"freerdp", "2.0.0~git.1463131968.4e66df7-11.69"},
|
||||
"libfreerdp2-2.0.0~git.1463131968.4e66df7-11.69 is installed": {"libfreerdp2", "2.0.0~git.1463131968.4e66df7-11.69"},
|
||||
"ruby2.1-rubygem-sle2docker-0.2.3-5.1 is installed": {"ruby2.1-rubygem-sle2docker", "0.2.3-5.1"},
|
||||
"xen-libs-4.4.1_06-2.2 is installed": {"xen-libs", "4.4.1_06-2.2"},
|
||||
"runc-0.1.1+gitr2816_02f8fa7 is installed": {"runc", "0.1.1+gitr2816_02f8fa7"},
|
||||
}
|
||||
|
||||
for pkg, expectations := range testData {
|
||||
name, version, err := splitPackageNameAndVersion(pkg[:len(pkg)-len(" is installed")])
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectations[0], name)
|
||||
assert.Equal(t, expectations[1], version)
|
||||
}
|
||||
|
||||
name, version, err := splitPackageNameAndVersion("invalid-package is installed")
|
||||
assert.NotNil(t, err)
|
||||
assert.Empty(t, name)
|
||||
assert.Empty(t, version)
|
||||
}
|
66
ext/vulnsrc/suse/testdata/fetcher_opensuse_test.1.xml
vendored
Normal file
66
ext/vulnsrc/suse/testdata/fetcher_opensuse_test.1.xml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<oval_definitions
|
||||
xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux linux-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#unix unix-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd"
|
||||
xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5"
|
||||
xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
<generator>
|
||||
<oval:product_name>Marcus Updateinfo to OVAL Converter</oval:product_name>
|
||||
<oval:schema_version>5.5</oval:schema_version>
|
||||
<oval:timestamp>2016-06-27T04:04:46</oval:timestamp>
|
||||
</generator>
|
||||
<definitions>
|
||||
<definition id="oval:org.opensuse.security:def:20122150" version="1" class="vulnerability">
|
||||
<metadata>
|
||||
<title>CVE-2012-2150</title>
|
||||
<affected family="unix">
|
||||
<platform>openSUSE Leap 42.1</platform>
|
||||
</affected>
|
||||
<reference ref_id="CVE-2012-2150" ref_url="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2150" source="CVE"/>
|
||||
<description>xfs_metadump in xfsprogs before 3.2.4 does not properly obfuscate file data, which allows remote attackers to obtain sensitive information by reading a generated image.</description>
|
||||
</metadata>
|
||||
<criteria operator="AND">
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009117743" comment="openSUSE Leap 42.1 is installed"/>
|
||||
<criteria operator="OR">
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009120999" comment="xfsprogs-3.2.1-5.1 is installed"/>
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009121000" comment="xfsprogs-devel-3.2.1-5.1 is installed"/>
|
||||
</criteria>
|
||||
</criteria>
|
||||
</definition>
|
||||
</definitions>
|
||||
<tests>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009117743" version="1" comment="openSUSE-release is ==42.1" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009031246"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009046321"/>
|
||||
</rpminfo_test>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009120999" version="1" comment="xfsprogs is <3.2.1-5.1" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009032555"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009046736"/>
|
||||
</rpminfo_test>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009121000" version="1" comment="xfsprogs-devel is <3.2.1-5.1" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009032648"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009046736"/>
|
||||
</rpminfo_test>
|
||||
</tests>
|
||||
<objects>
|
||||
<rpminfo_object id="oval:org.opensuse.security:obj:2009032648" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<name>xfsprogs-devel</name>
|
||||
</rpminfo_object>
|
||||
<rpminfo_object id="oval:org.opensuse.security:obj:2009031246" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<name>openSUSE-release</name>
|
||||
</rpminfo_object>
|
||||
<rpminfo_object id="oval:org.opensuse.security:obj:2009032555" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<name>xfsprogs</name>
|
||||
</rpminfo_object>
|
||||
</objects>
|
||||
<states>
|
||||
<rpminfo_state id="oval:org.opensuse.security:ste:2009046736" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<evr datatype="evr_string" operation="less than">0:3.2.1-5.1</evr>
|
||||
</rpminfo_state>
|
||||
<rpminfo_state id="oval:org.opensuse.security:ste:2009046321" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<version operation="equals">42.1</version>
|
||||
</rpminfo_state>
|
||||
</states>
|
||||
</oval_definitions>
|
69
ext/vulnsrc/suse/testdata/fetcher_sle_test.1.xml
vendored
Normal file
69
ext/vulnsrc/suse/testdata/fetcher_sle_test.1.xml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<oval_definitions
|
||||
xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux linux-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#unix unix-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5 oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 oval-common-schema.xsd"
|
||||
xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5"
|
||||
xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5">
|
||||
<generator>
|
||||
<oval:product_name>Marcus Updateinfo to OVAL Converter</oval:product_name>
|
||||
<oval:schema_version>5.5</oval:schema_version>
|
||||
<oval:timestamp>2016-06-27T04:04:46</oval:timestamp>
|
||||
</generator>
|
||||
<definitions>
|
||||
<definition id="oval:org.opensuse.security:def:20122150" version="1" class="vulnerability">
|
||||
<metadata>
|
||||
<title>CVE-2012-2150</title>
|
||||
<affected family="unix">
|
||||
<platform>SUSE Linux Enterprise Server 12</platform>
|
||||
</affected>
|
||||
<reference ref_id="CVE-2012-2150" ref_url="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2150" source="CVE"/>
|
||||
<description>xfs_metadump in xfsprogs before 3.2.4 does not properly obfuscate file data, which allows remote attackers to obtain sensitive information by reading a generated image.</description>
|
||||
</metadata>
|
||||
<criteria operator="OR">
|
||||
<criteria operator="AND">
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009116126" comment="SUSE Linux Enterprise Server 12 is installed"/>
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009116182" comment="xfsprogs-3.2.1-3.5 is installed"/>
|
||||
</criteria>
|
||||
<criteria operator="AND">
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009118803" comment="SUSE Linux Enterprise Server 12 SP1 is installed"/>
|
||||
<criterion test_ref="oval:org.opensuse.security:tst:2009116182" comment="xfsprogs-3.2.1-3.5 is installed"/>
|
||||
</criteria>
|
||||
</criteria>
|
||||
</definition>
|
||||
</definitions>
|
||||
<tests>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009116126" version="1" comment="sles-release is ==12" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009030884"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009045919"/>
|
||||
</rpminfo_test>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009116126" version="1" comment="sles-release is ==12.1" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009030884"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009045920"/>
|
||||
</rpminfo_test>
|
||||
<rpminfo_test id="oval:org.opensuse.security:tst:2009116182" version="1" comment="xfsprogs is <3.2.1-3.5" check="at least one" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<object object_ref="oval:org.opensuse.security:obj:2009032555"/>
|
||||
<state state_ref="oval:org.opensuse.security:ste:2009046736"/>
|
||||
</rpminfo_test>
|
||||
</tests>
|
||||
<objects>
|
||||
<rpminfo_object id="oval:org.opensuse.security:obj:2009030884" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<name>sles-release</name>
|
||||
</rpminfo_object>
|
||||
<rpminfo_object id="oval:org.opensuse.security:obj:2009032555" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<name>xfsprogs</name>
|
||||
</rpminfo_object>
|
||||
</objects>
|
||||
<states>
|
||||
<rpminfo_state id="oval:org.opensuse.security:ste:2009046736" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<evr datatype="evr_string" operation="less than">0:3.2.1-3.5</evr>
|
||||
</rpminfo_state>
|
||||
<rpminfo_state id="oval:org.opensuse.security:ste:2009045919" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<version operation="equals">12</version>
|
||||
</rpminfo_state>
|
||||
<rpminfo_state id="oval:org.opensuse.security:ste:2009045920" version="1" xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5#linux">
|
||||
<version operation="equals">12.1</version>
|
||||
</rpminfo_state>
|
||||
</states>
|
||||
</oval_definitions>
|
Loading…
Reference in New Issue
Block a user