Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updater: Refactor and merge fetcher responses #37

Merged
merged 1 commit into from
Dec 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions updater/fetchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@

package updater

import (
"github.com/coreos/clair/database"
"github.com/coreos/clair/utils/types"
)
import "github.com/coreos/clair/database"

var fetchers = make(map[string]Fetcher)

Expand All @@ -31,17 +28,8 @@ type FetcherResponse struct {
FlagName string
FlagValue string
Notes []string
Vulnerabilities []FetcherVulnerability
}

// FetcherVulnerability represents an individual vulnerability processed from
// an update.
type FetcherVulnerability struct {
ID string
Link string
Description string
Priority types.Priority
FixedIn []*database.Package
Vulnerabilities []*database.Vulnerability
Packages []*database.Package
}

// RegisterFetcher makes a Fetcher available by the provided name.
Expand Down
28 changes: 15 additions & 13 deletions updater/fetchers/debian.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
"strings"

"github.com/coreos/clair/database"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/updater"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/utils/types"
)

Expand Down Expand Up @@ -114,7 +114,8 @@ func buildResponse(jsonReader io.Reader, latestKnownHash string) (resp updater.F
}

// Extract vulnerability data from Debian's JSON schema.
vulnerabilities, unknownReleases := parseDebianJSON(&data)
var unknownReleases map[string]struct{}
resp.Vulnerabilities, resp.Packages, unknownReleases = parseDebianJSON(&data)

// Log unknown releases
for k := range unknownReleases {
Expand All @@ -123,16 +124,11 @@ func buildResponse(jsonReader io.Reader, latestKnownHash string) (resp updater.F
log.Warning(note)
}

// Convert the vulnerabilities map to a slice in the response
for _, v := range vulnerabilities {
resp.Vulnerabilities = append(resp.Vulnerabilities, v)
}

return resp, nil
}

func parseDebianJSON(data *jsonData) (vulnerabilities map[string]updater.FetcherVulnerability, unknownReleases map[string]struct{}) {
vulnerabilities = make(map[string]updater.FetcherVulnerability)
func parseDebianJSON(data *jsonData) (vulnerabilities []*database.Vulnerability, packages []*database.Package, unknownReleases map[string]struct{}) {
mvulnerabilities := make(map[string]*database.Vulnerability)
unknownReleases = make(map[string]struct{})

for pkgName, pkgNode := range *data {
Expand All @@ -150,9 +146,9 @@ func parseDebianJSON(data *jsonData) (vulnerabilities map[string]updater.Fetcher
}

// Get or create the vulnerability.
vulnerability, vulnerabilityAlreadyExists := vulnerabilities[vulnName]
vulnerability, vulnerabilityAlreadyExists := mvulnerabilities[vulnName]
if !vulnerabilityAlreadyExists {
vulnerability = updater.FetcherVulnerability{
vulnerability = &database.Vulnerability{
ID: vulnName,
Link: strings.Join([]string{cveURLPrefix, "/", vulnName}, ""),
Priority: types.Unknown,
Expand Down Expand Up @@ -191,14 +187,20 @@ func parseDebianJSON(data *jsonData) (vulnerabilities map[string]updater.Fetcher
Name: pkgName,
Version: version,
}
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
vulnerability.FixedInNodes = append(vulnerability.FixedInNodes, pkg.GetNode())
packages = append(packages, pkg)

// Store the vulnerability.
vulnerabilities[vulnName] = vulnerability
mvulnerabilities[vulnName] = vulnerability
}
}
}

// Convert the vulnerabilities map to a slice
for _, v := range mvulnerabilities {
vulnerabilities = append(vulnerabilities, v)
}

return
}

Expand Down
34 changes: 22 additions & 12 deletions updater/fetchers/debian_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,49 @@ func TestDebianParser(t *testing.T) {
assert.Equal(t, types.Low, vulnerability.Priority)
assert.Equal(t, "This vulnerability is not very dangerous.", vulnerability.Description)

if assert.Len(t, vulnerability.FixedIn, 2) {
assert.Contains(t, vulnerability.FixedIn, &database.Package{
expectedPackages := []*database.Package{
&database.Package{
OS: "debian:8",
Name: "aptdaemon",
Version: types.MaxVersion,
})
assert.Contains(t, vulnerability.FixedIn, &database.Package{
},
&database.Package{
OS: "debian:unstable",
Name: "aptdaemon",
Version: types.NewVersionUnsafe("1.1.1+bzr982-1"),
})
},
}

for _, expectedPackage := range expectedPackages {
assert.Contains(t, response.Packages, expectedPackage)
assert.Contains(t, vulnerability.FixedInNodes, expectedPackage.GetNode())
}
} else if vulnerability.ID == "CVE-2003-0779" {
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2003-0779", vulnerability.Link)
assert.Equal(t, types.High, vulnerability.Priority)
assert.Equal(t, "But this one is very dangerous.", vulnerability.Description)

if assert.Len(t, vulnerability.FixedIn, 3) {
assert.Contains(t, vulnerability.FixedIn, &database.Package{
expectedPackages := []*database.Package{
&database.Package{
OS: "debian:8",
Name: "aptdaemon",
Version: types.NewVersionUnsafe("0.7.0"),
})
assert.Contains(t, vulnerability.FixedIn, &database.Package{
},
&database.Package{
OS: "debian:unstable",
Name: "aptdaemon",
Version: types.NewVersionUnsafe("0.7.0"),
})
assert.Contains(t, vulnerability.FixedIn, &database.Package{
},
&database.Package{
OS: "debian:8",
Name: "asterisk",
Version: types.NewVersionUnsafe("0.5.56"),
})
},
}

for _, expectedPackage := range expectedPackages {
assert.Contains(t, response.Packages, expectedPackage)
assert.Contains(t, vulnerability.FixedInNodes, expectedPackage.GetNode())
}
} else {
assert.Fail(t, "Wrong vulnerability name: ", vulnerability.ID)
Expand Down
28 changes: 14 additions & 14 deletions updater/fetchers/rhel.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
"strings"

"github.com/coreos/clair/database"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/updater"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/utils/types"
)

Expand Down Expand Up @@ -128,17 +128,14 @@ func (f *RHELFetcher) FetchUpdate() (resp updater.FetcherResponse, err error) {
}

// Parse the XML.
vs, err := parseRHSA(r.Body)
vs, pkgs, err := parseRHSA(r.Body)
if err != nil {
return resp, err
}

// Collect vulnerabilities.
for _, v := range vs {
if len(v.FixedIn) > 0 {
resp.Vulnerabilities = append(resp.Vulnerabilities, v)
}
}
resp.Vulnerabilities = append(resp.Vulnerabilities, vs...)
resp.Packages = append(resp.Packages, pkgs...)
}

// Set the flag if we found anything.
Expand All @@ -152,7 +149,7 @@ func (f *RHELFetcher) FetchUpdate() (resp updater.FetcherResponse, err error) {
return resp, nil
}

func parseRHSA(ovalReader io.Reader) (vulnerabilities []updater.FetcherVulnerability, err error) {
func parseRHSA(ovalReader io.Reader) (vulnerabilities []*database.Vulnerability, packages []*database.Package, err error) {
// Decode the XML.
var ov oval
err = xml.NewDecoder(ovalReader).Decode(&ov)
Expand All @@ -163,18 +160,21 @@ func parseRHSA(ovalReader io.Reader) (vulnerabilities []updater.FetcherVulnerabi
}

// Iterate over the definitions and collect any vulnerabilities that affect
// more than one package.
// at least one package.
for _, definition := range ov.Definitions {
packages := toPackages(definition.Criteria)
if len(packages) > 0 {
vuln := updater.FetcherVulnerability{
pkgs := toPackages(definition.Criteria)
if len(pkgs) > 0 {
vulnerability := &database.Vulnerability{
ID: name(definition),
Link: link(definition),
Priority: priority(definition),
Description: description(definition),
FixedIn: packages,
}
vulnerabilities = append(vulnerabilities, vuln)
for _, p := range pkgs {
vulnerability.FixedInNodes = append(vulnerability.FixedInNodes, p.GetNode())
}
vulnerabilities = append(vulnerabilities, vulnerability)
packages = append(packages, pkgs...)
}
}

Expand Down
38 changes: 24 additions & 14 deletions updater/fetchers/rhel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,52 +31,62 @@ func TestRHELParser(t *testing.T) {

// Test parsing testdata/fetcher_rhel_test.1.xml
testFile, _ := os.Open(path + "/testdata/fetcher_rhel_test.1.xml")
vulnerabilities, err := parseRHSA(testFile)
vulnerabilities, packages, err := parseRHSA(testFile)
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
assert.Equal(t, "RHSA-2015:1193", vulnerabilities[0].ID)
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1193.html", vulnerabilities[0].Link)
assert.Equal(t, types.Medium, vulnerabilities[0].Priority)
assert.Equal(t, `Xerces-C is a validating XML parser written in a portable subset of C++. A flaw was found in the way the Xerces-C XML parser processed certain XML documents. A remote attacker could provide specially crafted XML input that, when parsed by an application using Xerces-C, would cause that application to crash.`, vulnerabilities[0].Description)

if assert.Len(t, vulnerabilities[0].FixedIn, 3) {
assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{
expectedPackages := []*database.Package{
&database.Package{
OS: "centos:7",
Name: "xerces-c",
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
})
assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{
},
&database.Package{
OS: "centos:7",
Name: "xerces-c-devel",
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
})
assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{
},
&database.Package{
OS: "centos:7",
Name: "xerces-c-doc",
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
})
},
}

for _, expectedPackage := range expectedPackages {
assert.Contains(t, packages, expectedPackage)
assert.Contains(t, vulnerabilities[0].FixedInNodes, expectedPackage.GetNode())
}
}

// Test parsing testdata/fetcher_rhel_test.2.xml
testFile, _ = os.Open(path + "/testdata/fetcher_rhel_test.2.xml")
vulnerabilities, err = parseRHSA(testFile)
vulnerabilities, packages, err = parseRHSA(testFile)
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
assert.Equal(t, "RHSA-2015:1207", vulnerabilities[0].ID)
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1207.html", vulnerabilities[0].Link)
assert.Equal(t, types.Critical, vulnerabilities[0].Priority)
assert.Equal(t, `Mozilla Firefox is an open source web browser. XULRunner provides the XUL Runtime environment for Mozilla Firefox. Several flaws were found in the processing of malformed web content. A web page containing malicious content could cause Firefox to crash or, potentially, execute arbitrary code with the privileges of the user running Firefox.`, vulnerabilities[0].Description)

if assert.Len(t, vulnerabilities[0].FixedIn, 2) {
assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{
expectedPackages := []*database.Package{
&database.Package{
OS: "centos:6",
Name: "firefox",
Version: types.NewVersionUnsafe("38.1.0-1.el6_6"),
})
assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{
},
&database.Package{
OS: "centos:7",
Name: "firefox",
Version: types.NewVersionUnsafe("38.1.0-1.el7_1"),
})
},
}

for _, expectedPackage := range expectedPackages {
assert.Contains(t, packages, expectedPackage)
assert.Contains(t, vulnerabilities[0].FixedInNodes, expectedPackage.GetNode())
}
}
}
16 changes: 12 additions & 4 deletions updater/fetchers/ubuntu.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,14 @@ func (fetcher *UbuntuFetcher) FetchUpdate() (resp updater.FetcherResponse, err e
}
defer file.Close()

v, unknownReleases, err := parseUbuntuCVE(file)
v, pkgs, unknownReleases, err := parseUbuntuCVE(file)
if err != nil {
return resp, err
}

if len(v.FixedIn) > 0 {
if len(v.FixedInNodes) > 0 {
resp.Vulnerabilities = append(resp.Vulnerabilities, v)
resp.Packages = append(resp.Packages, pkgs...)
}

// Log any unknown releases.
Expand Down Expand Up @@ -255,7 +256,8 @@ func getRevisionNumber(pathToRepo string) (int, error) {
return revno, nil
}

func parseUbuntuCVE(fileContent io.Reader) (vulnerability updater.FetcherVulnerability, unknownReleases map[string]struct{}, err error) {
func parseUbuntuCVE(fileContent io.Reader) (vulnerability *database.Vulnerability, packages []*database.Package, unknownReleases map[string]struct{}, err error) {
vulnerability = &database.Vulnerability{}
unknownReleases = make(map[string]struct{})
readingDescription := false
scanner := bufio.NewScanner(fileContent)
Expand Down Expand Up @@ -351,7 +353,13 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability updater.FetcherVulnera
}

// Create and add the new package.
vulnerability.FixedIn = append(vulnerability.FixedIn, &database.Package{OS: "ubuntu:" + database.UbuntuReleasesMapping[md["release"]], Name: md["package"], Version: version})
pkg := &database.Package{
OS: "ubuntu:" + database.UbuntuReleasesMapping[md["release"]],
Name: md["package"],
Version: version,
}
packages = append(packages, pkg)
vulnerability.FixedInNodes = append(vulnerability.FixedInNodes, pkg.GetNode())
}
}
}
Expand Down
21 changes: 13 additions & 8 deletions updater/fetchers/ubuntu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestUbuntuParser(t *testing.T) {
// Test parsing testdata/fetcher_
testData, _ := os.Open(path + "/testdata/fetcher_ubuntu_test.txt")
defer testData.Close()
vulnerability, unknownReleases, err := parseUbuntuCVE(testData)
vulnerability, packages, unknownReleases, err := parseUbuntuCVE(testData)
if assert.Nil(t, err) {
assert.Equal(t, "CVE-2015-4471", vulnerability.ID)
assert.Equal(t, types.Medium, vulnerability.Priority)
Expand All @@ -42,22 +42,27 @@ func TestUbuntuParser(t *testing.T) {
_, hasUnkownRelease := unknownReleases["unknown"]
assert.True(t, hasUnkownRelease)

if assert.Len(t, vulnerability.FixedIn, 3) {
assert.Contains(t, vulnerability.FixedIn, &database.Package{
expectedPackages := []*database.Package{
&database.Package{
OS: "ubuntu:14.04",
Name: "libmspack",
Version: types.MaxVersion,
})
assert.Contains(t, vulnerability.FixedIn, &database.Package{
},
&database.Package{
OS: "ubuntu:15.04",
Name: "libmspack",
Version: types.NewVersionUnsafe("0.4-3"),
})
assert.Contains(t, vulnerability.FixedIn, &database.Package{
},
&database.Package{
OS: "ubuntu:15.10",
Name: "libmspack-anotherpkg",
Version: types.NewVersionUnsafe("0.1"),
})
},
}

for _, expectedPackage := range expectedPackages {
assert.Contains(t, packages, expectedPackage)
assert.Contains(t, vulnerability.FixedInNodes, expectedPackage.GetNode())
}
}
}
Loading