diff --git a/updater/fetchers/opensuse/opensuse.go b/updater/fetchers/opensuse/opensuse.go index 1cd96f790b..7269c995de 100644 --- a/updater/fetchers/opensuse/opensuse.go +++ b/updater/fetchers/opensuse/opensuse.go @@ -15,329 +15,88 @@ package opensuse import ( - "bufio" - "encoding/xml" "fmt" - "io" - "net/http" "regexp" - "strconv" - "strings" - - "github.com/coreos/clair/database" + "strconv" "github.com/coreos/clair/updater" - cerrors "github.com/coreos/clair/utils/errors" - "github.com/coreos/clair/utils/types" + "github.com/coreos/clair/updater/fetchers/oval" "github.com/coreos/pkg/capnslog" ) -const ( - ovalURI = "http://ftp.suse.com/pub/projects/security/oval/" - updaterFlag = "opensuseUpdater" - distMinVersion = 13.1 -) - -var ( - ignoredCriterions = []string{} +var log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/sle") - critSystem = regexp.MustCompile(`openSUSE [^0-9]*(\d+\.\d+)[^0-9]* is installed`) - critPackage = regexp.MustCompile(`(.*)-(.*\-[\d\.]+) is installed`) - distRegexp = regexp.MustCompile(`opensuse.[^0-9]*(\d+\.\d+).xml`) - - log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/opensuse") -) - -type oval struct { - Definitions []definition `xml:"definitions>definition"` -} +func init() { + opensuse_info := &OpenSUSE_Info{} -type definition struct { - Title string `xml:"metadata>title"` - Description string `xml:"metadata>description"` - References []reference `xml:"metadata>reference"` - Criteria criteria `xml:"criteria"` + updater.RegisterFetcher(opensuse_info.DistName(), + &oval.OvalFetcher{Os_info: opensuse_info}) } -type reference struct { - Source string `xml:"source,attr"` - URI string `xml:"ref_url,attr"` +type OpenSUSE_Info struct { } -type criteria struct { - Operator string `xml:"operator,attr"` - Criterias []*criteria `xml:"criteria"` - Criterions []criterion `xml:"criterion"` +func (f *OpenSUSE_Info) CritSystem() *regexp.Regexp { + return regexp.MustCompile(`openSUSE [^0-9]*(\d+\.\d+)[^0-9]* is installed`) } -type criterion struct { - Comment string `xml:"comment,attr"` +func (f *OpenSUSE_Info) CritPackage() *regexp.Regexp { + return regexp.MustCompile(`(.*)-(.*\-[\d\.]+) is installed`) } -func init() { - updater.RegisterFetcher("openSUSE", &OpenSUSEFetcher{}) +func (f *OpenSUSE_Info) DistRegexp() *regexp.Regexp { + return regexp.MustCompile(`opensuse.[^0-9]*(\d+\.\d+).xml`) } -// OpenSUSEFetcher implements updater.Fetcher. -type OpenSUSEFetcher struct{} - -// FetchUpdate gets vulnerability updates from the OVAL definitions. -func (f *OpenSUSEFetcher) FetchUpdate(datastore database.Datastore) (resp updater.FetcherResponse, err error) { - log.Info("fetching openSUSE vulnerabilities") - - // TODO: Skip already loaded records - - // flagValue, err := datastore.GetKeyValue(updaterFlag) - - // if err != nil { - // return resp, err - // } - - r, err := http.Get(ovalURI) - - if err != nil { - log.Errorf("could not download openSUSE's update list: %s", err) - return resp, cerrors.ErrCouldNotDownload - } - - var distList []string - scanner := bufio.NewScanner(r.Body) - - for scanner.Scan() { - line := scanner.Text() - r := distRegexp.FindStringSubmatch(line) - - if len(r) == 2 { - distVersion, _ := strconv.ParseFloat(r[1], 32) - if distVersion >= distMinVersion { - distList = append(distList, r[0]) - } - } - } - - for _, distFile := range distList { - r, err := http.Get(ovalURI + distFile) - - if err != nil { - log.Errorf("could not download openSUSE's update file: %s", err) - return resp, cerrors.ErrCouldNotDownload - } - - vs, err := parseOval(r.Body) - - if err != nil { - return resp, err - } - - for _, v := range vs { - resp.Vulnerabilities = append(resp.Vulnerabilities, v) - } - } - - return resp, nil +func (f *OpenSUSE_Info) OvalURI() string { + return "http://ftp.suse.com/pub/projects/security/oval/" } -// Clean deletes any allocated resources. -func (f *OpenSUSEFetcher) Clean() {} - -func parseOval(ovalReader io.Reader) (vulnerabilities []database.Vulnerability, err error) { - var ov oval - err = xml.NewDecoder(ovalReader).Decode(&ov) - - if err != nil { - log.Errorf("could not decode openSUSE's XML: %s", err) - return vulnerabilities, cerrors.ErrCouldNotParse - } - - for _, definition := range ov.Definitions { - pkgs := toFeatureVersions(definition.Criteria) - - if len(pkgs) > 0 { - vulnerability := database.Vulnerability{ - Name: name(definition), - Link: link(definition), - Severity: priority(definition), - Description: description(definition), - } - - for _, p := range pkgs { - vulnerability.FixedIn = append(vulnerability.FixedIn, p) - } - - vulnerabilities = append(vulnerabilities, vulnerability) - } - } - - return +func (f *OpenSUSE_Info) UpdaterFlag() string { + return "opensuseUpdater" } -func toFeatureVersions(criteria criteria) []database.FeatureVersion { - featureVersionParameters := make(map[string]database.FeatureVersion) - possibilities := getPossibilities(criteria) - - for _, criterions := range possibilities { - var ( - featureVersion database.FeatureVersion - osVersion string - err error - ) - - for _, c := range criterions { - systemMatch := critSystem.FindStringSubmatch(c.Comment) - - if len(systemMatch) == 2 { - osVersion = systemMatch[1] - - // log.Infof("got %s as system version from: %s", osVersion, c.Comment) - - continue - } - - packageMatch := critPackage.FindStringSubmatch(c.Comment) - - if len(packageMatch) == 3 { - featureVersion.Feature.Name = packageMatch[1] - featureVersion.Version, err = types.NewVersion(packageMatch[2]) - - if err != nil { - log.Warningf("could not parse package version '%s': %s. skipping", packageMatch[2], err.Error()) - } - - // log.Infof("got %s = %s as pkg version from: %s", packageMatch[1], packageMatch[2], c.Comment) - - continue - } - - log.Warningf("could not parse criteria: '%s'.", c.Comment) - } - - if osVersion == "" { - log.Warning(criterions) - } - - featureVersion.Feature.Namespace.Name = fmt.Sprintf("opensuse:%s", osVersion) - - if featureVersion.Feature.Name != "" && featureVersion.Version.String() != "" { - featureVersionParameters[featureVersion.Feature.Namespace.Name+":"+featureVersion.Feature.Name] = featureVersion - } else { - log.Warningf("could not determine a valid package from criterions: %v", criterions) - } - } - - var featureVersionParametersArray []database.FeatureVersion - - for _, fv := range featureVersionParameters { - featureVersionParametersArray = append(featureVersionParametersArray, fv) - } - - return featureVersionParametersArray +func (f *OpenSUSE_Info) DistMinVersion() float64 { + return 13.1 } -func getCriterions(node criteria) [][]criterion { - 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 (f *OpenSUSE_Info) DistName() string { + return "opensuse" } -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) - } +func (f *OpenSUSE_Info) ParseOsVersion(comment string) string { + systemMatch := f.CritSystem().FindStringSubmatch(comment) + if len(systemMatch) < 2 { + return "" + } + osVersion := systemMatch[1] + if len(systemMatch) == 4 && systemMatch[3] != "" { + sp:= systemMatch[3] + osVersion = fmt.Sprintf("%s.%s", osVersion, sp) + } - for _, possibilityGroup := range possibilitiesToCompose[1:] { - var newPossibilities [][]criterion + // log.Infof("got %s as system version from: %s", osVersion, c.Comment) + return osVersion - 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 description(def definition) (desc string) { - desc = strings.Replace(def.Description, "\n\n\n", " ", -1) - desc = strings.Replace(desc, "\n\n", " ", -1) - desc = strings.Replace(desc, "\n", " ", -1) +func (f *OpenSUSE_Info) ParsePackageNameVersion(comment string) (string, string) { + packageMatch := f.CritPackage().FindStringSubmatch(comment) - return + if len(packageMatch) != 3 { + return "", "" + } + name := packageMatch[1] + version:= packageMatch[2] + return name, version } -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 (f *OpenSUSE_Info) ParseFilenameDistVersion(line string) (string, float64) { + r := f.DistRegexp().FindStringSubmatch(line) + if len(r) == 2 { + distVersion, _ := strconv.ParseFloat(r[1], 32) + return r[0], distVersion + } + return "", 0 + } -func priority(def definition) types.Priority { - // The OVAL files provided by SUSE doesn't include any priority/severity yet. - return types.Unknown -} diff --git a/updater/fetchers/opensuse/opensuse_test.go b/updater/fetchers/opensuse/opensuse_test.go index 19a2c6cc39..b4e7f16efb 100644 --- a/updater/fetchers/opensuse/opensuse_test.go +++ b/updater/fetchers/opensuse/opensuse_test.go @@ -21,17 +21,19 @@ import ( "testing" "github.com/coreos/clair/database" + "github.com/coreos/clair/updater/fetchers/oval" "github.com/coreos/clair/utils/types" "github.com/stretchr/testify/assert" ) -func TestOvalParser(t *testing.T) { +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") - vulnerabilities, err := parseOval(testFile) + ov := &oval.OvalFetcher{ Os_info: &OpenSUSE_Info{}} + vulnerabilities, err := ov.ParseOval(testFile) 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) diff --git a/updater/fetchers/oval/oval.go b/updater/fetchers/oval/oval.go new file mode 100644 index 0000000000..459faa915b --- /dev/null +++ b/updater/fetchers/oval/oval.go @@ -0,0 +1,309 @@ +// Copyright 2015 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 oval + +import ( + "bufio" + "encoding/xml" + "fmt" + "io" + "net/http" + "strings" + + "github.com/coreos/clair/database" + "github.com/coreos/clair/updater" + cerrors "github.com/coreos/clair/utils/errors" + "github.com/coreos/clair/utils/types" + "github.com/coreos/pkg/capnslog" +) + +type oval struct { + 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"` +} + +// OvalFetcher implements updater.Fetcher. +type OvalFetcher struct { + Os_info OSInfo +} + +type OSInfo interface { + ParsePackageNameVersion(string) (string, string) + ParseOsVersion(string) string + ParseFilenameDistVersion(string) (string, float64) + OvalURI() string + UpdaterFlag() string + DistMinVersion() float64 + DistName() string +} + +var ( + + log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/sle") +) + +// FetchUpdate gets vulnerability updates from the OVAL definitions. +func (f *OvalFetcher) FetchUpdate(datastore database.Datastore) (resp updater.FetcherResponse, err error) { + log.Info("fetching %s vulnerabilities", f.Os_info.DistName()) + + // TODO: Skip already loaded records + + // flagValue, err := datastore.KeyValue(f.Os_info.UpdaterFlag) + + // if err != nil { + // return resp, err + // } + + r, err := http.Get(f.Os_info.OvalURI()) + + if err != nil { + log.Errorf("could not download %s's update list: %s", f.Os_info.DistName(), err) + return resp, cerrors.ErrCouldNotDownload + } + + var distList []string + scanner := bufio.NewScanner(r.Body) + + for scanner.Scan() { + line := scanner.Text() + filename, distVersion := f.Os_info.ParseFilenameDistVersion(line) + if distVersion >= f.Os_info.DistMinVersion() { + distList = append(distList, filename) + } + } + + for _, distFile := range distList { + r, err := http.Get(f.Os_info.OvalURI() + distFile) + + if err != nil { + log.Errorf("could not download %s's update file: %s", f.Os_info.DistName(), err) + return resp, cerrors.ErrCouldNotDownload + } + + vs, err := f.ParseOval(r.Body) + + if err != nil { + return resp, err + } + + for _, v := range vs { + resp.Vulnerabilities = append(resp.Vulnerabilities, v) + } + } + + return resp, nil +} + +// Clean deletes any allocated resources. +func (f *OvalFetcher) Clean() {} + +func (f *OvalFetcher) ToFeatureVersions(possibilities [][]criterion) []database.FeatureVersion { + featureVersionParameters := make(map[string]database.FeatureVersion) + + for _, criterions := range possibilities { + var ( + featureVersion database.FeatureVersion + osVersion string + ) + + for _, c := range criterions { + tmp_v := f.Os_info.ParseOsVersion(c.Comment) + if tmp_v != "" { + osVersion = tmp_v + continue + } + + tmp_p_name, tmp_p_version := f.Os_info.ParsePackageNameVersion(c.Comment) + if tmp_p_version != "" && tmp_p_name != "" { + featureVersion.Feature.Name = tmp_p_name + featureVersion.Version, _ = types.NewVersion(tmp_p_version) + continue + } + + + log.Warningf("could not parse criteria: '%s'.", c.Comment) + } + + if osVersion == "" { + log.Warning(criterions) + } + + featureVersion.Feature.Namespace.Name = fmt.Sprintf("%s:%s", f.Os_info.DistName(), osVersion) + + if featureVersion.Feature.Name != "" && featureVersion.Version.String() != "" { + featureVersionParameters[featureVersion.Feature.Namespace.Name+":"+featureVersion.Feature.Name] = featureVersion + } else { + log.Warningf("could not determine a valid package from criterions: %v", criterions) + } + } + + var featureVersionParametersArray []database.FeatureVersion + + for _, fv := range featureVersionParameters { + featureVersionParametersArray = append(featureVersionParametersArray, fv) + } + + return featureVersionParametersArray +} +func (f* OvalFetcher) ParseOval(ovalReader io.Reader) (vulnerabilities []database.Vulnerability, err error) { + var ov oval + err = xml.NewDecoder(ovalReader).Decode(&ov) + + if err != nil { + log.Errorf("could not decode %s's XML: %s", f.Os_info.DistName(), err) + return vulnerabilities, cerrors.ErrCouldNotParse + } + + for _, definition := range ov.Definitions { + pkgs := f.ToFeatureVersions(getPossibilities(definition.Criteria)) + + if len(pkgs) > 0 { + vulnerability := database.Vulnerability{ + Name: name(definition), + Link: link(definition), + Severity: priority(definition), + Description: description(definition), + } + + for _, p := range pkgs { + vulnerability.FixedIn = append(vulnerability.FixedIn, p) + } + + vulnerabilities = append(vulnerabilities, vulnerability) + } + } + + return +} + +func description(def definition) (desc string) { + 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 priority(def definition) types.Priority { + // The OVAL files provided by SUSE doesn't include any priority/severity yet. + return types.Unknown +} + +func getCriterions(node criteria) [][]criterion { + var criterions []criterion + + for _, c := range node.Criterions { + 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 +} + diff --git a/updater/fetchers/sle/sle.go b/updater/fetchers/sle/sle.go index f854cfd28b..6a6ebd25e0 100644 --- a/updater/fetchers/sle/sle.go +++ b/updater/fetchers/sle/sle.go @@ -15,335 +15,87 @@ package sle import ( - "bufio" - "encoding/xml" "fmt" - "io" - "net/http" "regexp" - "strconv" - "strings" - - "github.com/coreos/clair/database" + "strconv" "github.com/coreos/clair/updater" - cerrors "github.com/coreos/clair/utils/errors" - "github.com/coreos/clair/utils/types" + "github.com/coreos/clair/updater/fetchers/oval" "github.com/coreos/pkg/capnslog" ) -const ( - ovalURI = "http://ftp.suse.com/pub/projects/security/oval/" - updaterFlag = "sleUpdater" - distMinVersion = 11.4 -) - -var ( - ignoredCriterions = []string{} +var log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/sle") - critSystem = regexp.MustCompile(`SUSE Linux Enterprise Server [^0-9]*(\d+)\s*(SP(\d+)|) is installed`) - critPackage = regexp.MustCompile(`(.*)-(.*\-[\d\.]+) is installed`) - distRegexp = regexp.MustCompile(`suse.linux.enterprise.(\d+).xml`) - - log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/sle") -) +func init() { + sle_info := &SLE_Info{} -type oval struct { - Definitions []definition `xml:"definitions>definition"` + updater.RegisterFetcher(sle_info.DistName(), + &oval.OvalFetcher{Os_info: sle_info}) } -type definition struct { - Title string `xml:"metadata>title"` - Description string `xml:"metadata>description"` - References []reference `xml:"metadata>reference"` - Criteria criteria `xml:"criteria"` +type SLE_Info struct { } -type reference struct { - Source string `xml:"source,attr"` - URI string `xml:"ref_url,attr"` +func (f *SLE_Info) CritSystem() *regexp.Regexp { + return regexp.MustCompile(`SUSE Linux Enterprise Server [^0-9]*(\d+)\s*(SP(\d+)|) is installed`) } -type criteria struct { - Operator string `xml:"operator,attr"` - Criterias []*criteria `xml:"criteria"` - Criterions []criterion `xml:"criterion"` +func (f *SLE_Info) CritPackage() *regexp.Regexp { + return regexp.MustCompile(`(.*)-(.*\-[\d\.]+) is installed`) } -type criterion struct { - Comment string `xml:"comment,attr"` +func (f *SLE_Info) DistRegexp() *regexp.Regexp { + return regexp.MustCompile(`suse.linux.enterprise.(\d+).xml`) } -func init() { - updater.RegisterFetcher("sle", &SLEFetcher{}) +func (f *SLE_Info) OvalURI() string { + return "http://ftp.suse.com/pub/projects/security/oval/" } -// SLEFetcher implements updater.Fetcher. -type SLEFetcher struct{} - -// FetchUpdate gets vulnerability updates from the OVAL definitions. -func (f *SLEFetcher) FetchUpdate(datastore database.Datastore) (resp updater.FetcherResponse, err error) { - log.Info("fetching SLE vulnerabilities") - - // TODO: Skip already loaded records - - // flagValue, err := datastore.GetKeyValue(updaterFlag) - - // if err != nil { - // return resp, err - // } - - r, err := http.Get(ovalURI) - - if err != nil { - log.Errorf("could not download SLE's update list: %s", err) - return resp, cerrors.ErrCouldNotDownload - } - - var distList []string - scanner := bufio.NewScanner(r.Body) - - for scanner.Scan() { - line := scanner.Text() - r := distRegexp.FindStringSubmatch(line) - - if len(r) == 2 { - distVersion, _ := strconv.ParseFloat(r[1], 32) - if distVersion >= distMinVersion { - distList = append(distList, r[0]) - } - } - } - - for _, distFile := range distList { - r, err := http.Get(ovalURI + distFile) - - if err != nil { - log.Errorf("could not download SLE's update file: %s", err) - return resp, cerrors.ErrCouldNotDownload - } - - vs, err := parseOval(r.Body) - - if err != nil { - return resp, err - } - - for _, v := range vs { - resp.Vulnerabilities = append(resp.Vulnerabilities, v) - } - } - - return resp, nil +func (f *SLE_Info) UpdaterFlag() string { + return "sleUpdater" } -// Clean deletes any allocated resources. -func (f *SLEFetcher) Clean() {} - -func parseOval(ovalReader io.Reader) (vulnerabilities []database.Vulnerability, err error) { - var ov oval - err = xml.NewDecoder(ovalReader).Decode(&ov) - - if err != nil { - log.Errorf("could not decode SLE's XML: %s", err) - return vulnerabilities, cerrors.ErrCouldNotParse - } - - for _, definition := range ov.Definitions { - pkgs := toFeatureVersions(definition.Criteria) - - if len(pkgs) > 0 { - vulnerability := database.Vulnerability{ - Name: name(definition), - Link: link(definition), - Severity: priority(definition), - Description: description(definition), - } - - for _, p := range pkgs { - vulnerability.FixedIn = append(vulnerability.FixedIn, p) - } - - vulnerabilities = append(vulnerabilities, vulnerability) - } - } - - return +func (f *SLE_Info) DistMinVersion() float64 { + return 11.4 } -func toFeatureVersions(criteria criteria) []database.FeatureVersion { - featureVersionParameters := make(map[string]database.FeatureVersion) - possibilities := getPossibilities(criteria) - - for _, criterions := range possibilities { - var ( - featureVersion database.FeatureVersion - osVersion string - err error - ) - - for _, c := range criterions { - systemMatch := critSystem.FindStringSubmatch(c.Comment) - fmt.Println("eieie") - fmt.Println(systemMatch) - - if len(systemMatch) >= 2 { - osVersion = systemMatch[1] - if len(systemMatch) == 4 && systemMatch[3] != "" { - sp:= systemMatch[3] - osVersion = fmt.Sprintf("%s.%s", osVersion, sp) - } - - log.Infof("got %s as system version from: %s", osVersion, c.Comment) - - continue - } - - packageMatch := critPackage.FindStringSubmatch(c.Comment) - - if len(packageMatch) == 3 { - featureVersion.Feature.Name = packageMatch[1] - featureVersion.Version, err = types.NewVersion(packageMatch[2]) - - if err != nil { - log.Warningf("could not parse package version '%s': %s. skipping", packageMatch[2], err.Error()) - } - - // log.Infof("got %s = %s as pkg version from: %s", packageMatch[1], packageMatch[2], c.Comment) - - continue - } - - log.Warningf("could not parse criteria: '%s'.", c.Comment) - } - - if osVersion == "" { - log.Warning(criterions) - } - - featureVersion.Feature.Namespace.Name = fmt.Sprintf("sle:%s", osVersion) - - if featureVersion.Feature.Name != "" && featureVersion.Version.String() != "" { - featureVersionParameters[featureVersion.Feature.Namespace.Name+":"+featureVersion.Feature.Name] = featureVersion - } else { - log.Warningf("could not determine a valid package from criterions: %v", criterions) - } - } - - var featureVersionParametersArray []database.FeatureVersion - - for _, fv := range featureVersionParameters { - featureVersionParametersArray = append(featureVersionParametersArray, fv) - } - - return featureVersionParametersArray -} - -func getCriterions(node criteria) [][]criterion { - 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 (f *SLE_Info) DistName() string { + return "sle" } -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 +func (f *SLE_Info) ParseOsVersion(comment string) string { + systemMatch := f.CritSystem().FindStringSubmatch(comment) + if len(systemMatch) < 2 { + return "" + } + osVersion := systemMatch[1] + if len(systemMatch) == 4 && systemMatch[3] != "" { + sp:= systemMatch[3] + osVersion = fmt.Sprintf("%s.%s", osVersion, sp) + } - for _, possibility := range possibilities { - for _, possibilityInGroup := range possibilityGroup { - var p []criterion + // log.Infof("got %s as system version from: %s", osVersion, c.Comment) + return osVersion - 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 description(def definition) (desc string) { - desc = strings.Replace(def.Description, "\n\n\n", " ", -1) - desc = strings.Replace(desc, "\n\n", " ", -1) - desc = strings.Replace(desc, "\n", " ", -1) - - return -} +func (f *SLE_Info) ParsePackageNameVersion(comment string) (string, string) { + packageMatch := f.CritPackage().FindStringSubmatch(comment) -func name(def definition) string { - return strings.TrimSpace(def.Title) + if len(packageMatch) != 3 { + return "", "" + } + name := packageMatch[1] + version:= packageMatch[2] + return name, version } -func link(def definition) (link string) { - for _, reference := range def.References { - if reference.Source == "CVE" { - link = reference.URI - break - } - } - - return -} -func priority(def definition) types.Priority { - // The OVAL files provided by SUSE doesn't include any priority/severity yet. - return types.Unknown -} + func (f *SLE_Info) ParseFilenameDistVersion(line string) (string, float64) { + r := f.DistRegexp().FindStringSubmatch(line) + if len(r) == 2 { + distVersion, _ := strconv.ParseFloat(r[1], 32) + return r[0], distVersion + } + return "", 0 + } diff --git a/updater/fetchers/sle/sle_test.go b/updater/fetchers/sle/sle_test.go index f13878577a..6546bd992e 100644 --- a/updater/fetchers/sle/sle_test.go +++ b/updater/fetchers/sle/sle_test.go @@ -21,18 +21,20 @@ import ( "runtime" "testing" + "github.com/coreos/clair/updater/fetchers/oval" "github.com/coreos/clair/database" "github.com/coreos/clair/utils/types" "github.com/stretchr/testify/assert" ) -func TestOvalParser(t *testing.T) { +func TestSLEParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) path := filepath.Join(filepath.Dir(filename)) // Test parsing testdata/fetcher_sle_test.1.xml testFile, _ := os.Open(path + "/testdata/fetcher_sle_test.1.xml") - vulnerabilities, err := parseOval(testFile) + ov := &oval.OvalFetcher{ Os_info: &SLE_Info{}} + vulnerabilities, err := ov.ParseOval(testFile) 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)