diff --git a/api/v1/models.go b/api/v1/models.go index e3b1e1fbcd..93d048b97a 100644 --- a/api/v1/models.go +++ b/api/v1/models.go @@ -24,7 +24,6 @@ import ( "github.com/coreos/pkg/capnslog" "github.com/fernet/fernet-go" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" ) @@ -109,7 +108,7 @@ type Vulnerability struct { } func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) { - severity, err := clair.NewSeverity(v.Severity) + severity, err := database.NewSeverity(v.Severity) if err != nil { return database.Vulnerability{}, err } diff --git a/cmd/clair/main.go b/cmd/clair/main.go index 3fa21556b7..b342edc2ec 100644 --- a/cmd/clair/main.go +++ b/cmd/clair/main.go @@ -27,13 +27,13 @@ import ( "github.com/coreos/pkg/capnslog" + "github.com/coreos/clair" "github.com/coreos/clair/api" "github.com/coreos/clair/api/context" "github.com/coreos/clair/config" "github.com/coreos/clair/database" "github.com/coreos/clair/notifier" "github.com/coreos/clair/pkg/stopper" - "github.com/coreos/clair/updater" // Register database driver. _ "github.com/coreos/clair/database/pgsql" @@ -112,7 +112,7 @@ func Boot(config *config.Config) { // Start updater st.Begin() - go updater.Run(config.Updater, db, st) + go clair.RunUpdater(config.Updater, db, st) // Wait for interruption and shutdown gracefully. waitForSignals(syscall.SIGINT, syscall.SIGTERM) diff --git a/database/database.go b/database/database.go index 0e57921ff0..f8ce18ddee 100644 --- a/database/database.go +++ b/database/database.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package database defines the Clair's models and a common interface for database implementations. +// Package database defines the Clair's models and a common interface for +// database implementations. package database import ( @@ -144,7 +145,8 @@ type Datastore interface { // Vulnerability in the database. It can be used to store the fact that a // Vulnerability no longer affects the given Feature in any Version. // - // It has has to create a Notification that will contain the old and the updated Vulnerability. + // It has has to create a Notification that will contain the old and the + // updated Vulnerability. DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerabilityName, featureName string) error // GetAvailableNotification returns the Name, Created, Notified and Deleted diff --git a/database/models.go b/database/models.go index 86a1409636..2d645e6c3c 100644 --- a/database/models.go +++ b/database/models.go @@ -18,8 +18,6 @@ import ( "database/sql/driver" "encoding/json" "time" - - "github.com/coreos/clair" ) // ID is only meant to be used by database implementations and should never be used for anything else. @@ -70,7 +68,7 @@ type Vulnerability struct { Description string Link string - Severity clair.Severity + Severity Severity Metadata MetadataMap diff --git a/database/pgsql/complex_test.go b/database/pgsql/complex_test.go index 9e7e06e047..ed038b4e0f 100644 --- a/database/pgsql/complex_test.go +++ b/database/pgsql/complex_test.go @@ -26,7 +26,6 @@ import ( "github.com/pborman/uuid" "github.com/stretchr/testify/assert" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt/dpkg" ) @@ -92,7 +91,7 @@ func TestRaceAffects(t *testing.T) { Version: strconv.Itoa(version), }, }, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, } vulnerabilities[version] = append(vulnerabilities[version], vulnerability) diff --git a/database/pgsql/layer_test.go b/database/pgsql/layer_test.go index ad6ae1d737..72ede4d8b7 100644 --- a/database/pgsql/layer_test.go +++ b/database/pgsql/layer_test.go @@ -20,7 +20,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt/dpkg" "github.com/coreos/clair/pkg/commonerr" @@ -91,7 +90,7 @@ func TestFindLayer(t *testing.T) { if assert.Len(t, featureVersion.AffectedBy, 1) { assert.Equal(t, "debian:7", featureVersion.AffectedBy[0].Namespace.Name) assert.Equal(t, "CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Name) - assert.Equal(t, clair.High, featureVersion.AffectedBy[0].Severity) + assert.Equal(t, database.HighSeverity, featureVersion.AffectedBy[0].Severity) assert.Equal(t, "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0", featureVersion.AffectedBy[0].Description) assert.Equal(t, "http://google.com/#q=CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Link) assert.Equal(t, "2.0", featureVersion.AffectedBy[0].FixedBy) diff --git a/database/pgsql/notification_test.go b/database/pgsql/notification_test.go index 4e53e21781..24e7924644 100644 --- a/database/pgsql/notification_test.go +++ b/database/pgsql/notification_test.go @@ -20,7 +20,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -169,7 +168,7 @@ func TestNotification(t *testing.T) { // Update a vulnerability and ensure that the old/new vulnerabilities are correct. v1b := v1 - v1b.Severity = clair.High + v1b.Severity = database.HighSeverity v1b.FixedIn = []database.FeatureVersion{ { Feature: f1, diff --git a/database/pgsql/vulnerability_test.go b/database/pgsql/vulnerability_test.go index 53eaeaad08..61d835bbb5 100644 --- a/database/pgsql/vulnerability_test.go +++ b/database/pgsql/vulnerability_test.go @@ -20,7 +20,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -44,7 +43,7 @@ func TestFindVulnerability(t *testing.T) { Name: "CVE-OPENSSL-1-DEB7", Description: "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0", Link: "http://google.com/#q=CVE-OPENSSL-1-DEB7", - Severity: clair.High, + Severity: database.HighSeverity, Namespace: database.Namespace{ Name: "debian:7", VersionFormat: dpkg.ParserName, @@ -74,7 +73,7 @@ func TestFindVulnerability(t *testing.T) { Name: "debian:7", VersionFormat: dpkg.ParserName, }, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, } v2f, err := datastore.FindVulnerability("debian:7", "CVE-NOPE") @@ -180,13 +179,13 @@ func TestInsertVulnerability(t *testing.T) { Name: "", Namespace: n1, FixedIn: []database.FeatureVersion{f1}, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, }, { Name: "TestInsertVulnerability0", Namespace: database.Namespace{}, FixedIn: []database.FeatureVersion{f1}, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, }, { Name: "TestInsertVulnerability0-", @@ -197,7 +196,7 @@ func TestInsertVulnerability(t *testing.T) { Name: "TestInsertVulnerability0", Namespace: n1, FixedIn: []database.FeatureVersion{f2}, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, }, } { err := datastore.InsertVulnerabilities([]database.Vulnerability{vulnerability}, true) @@ -217,7 +216,7 @@ func TestInsertVulnerability(t *testing.T) { Name: "TestInsertVulnerability1", Namespace: n1, FixedIn: []database.FeatureVersion{f1, f3, f6, f7}, - Severity: clair.Low, + Severity: database.LowSeverity, Description: "TestInsertVulnerabilityDescription1", Link: "TestInsertVulnerabilityLink1", Metadata: v1meta, @@ -233,7 +232,7 @@ func TestInsertVulnerability(t *testing.T) { // Update vulnerability. v1.Description = "TestInsertVulnerabilityLink2" v1.Link = "TestInsertVulnerabilityLink2" - v1.Severity = clair.High + v1.Severity = database.HighSeverity // Update f3 in f4, add fixed in f5, add fixed in f6 which already exists, // removes fixed in f7 by adding f8 which is f7 but with MinVersion, and // add fixed by f5 a second time (duplicated). diff --git a/database/severity.go b/database/severity.go new file mode 100644 index 0000000000..58084d6459 --- /dev/null +++ b/database/severity.go @@ -0,0 +1,134 @@ +// 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 database + +import ( + "database/sql/driver" + "errors" + "strings" +) + +// ErrFailedToParseSeverity is the error returned when a severity could not +// be parsed from a string. +var ErrFailedToParseSeverity = errors.New("failed to parse Severity from input") + +// Severity defines a standard scale for measuring the severity of a +// vulnerability. +type Severity string + +const ( + // UnknownSeverity is either a security problem that has not been assigned to + // a priority yet or a priority that our system did not recognize. + UnknownSeverity Severity = "Unknown" + + // NegligibleSeverity is technically a security problem, but is only + // theoretical in nature, requires a very special situation, has almost no + // install base, or does no real damage. These tend not to get backport from + // upstreams, and will likely not be included in security updates unless + // there is an easy fix and some other issue causes an update. + NegligibleSeverity Severity = "Negligible" + + // LowSeverity is a security problem, but is hard to exploit due to + // environment, requires a user-assisted attack, a small install base, or + // does very little damage. These tend to be included in security updates + // only when higher priority issues require an update, or if many low + // priority issues have built up. + LowSeverity Severity = "Low" + + // MediumSeverity is a real security problem, and is exploitable for many + // people. Includes network daemon denial of service attacks, cross-site + // scripting, and gaining user privileges. Updates should be made soon for + // this priority of issue. + MediumSeverity Severity = "Medium" + + // HighSeverity is a real problem, exploitable for many people in a default + // installation. Includes serious remote denial of services, local root + // privilege escalations, or data loss. + HighSeverity Severity = "High" + + // CriticalSeverity is a world-burning problem, exploitable for nearly all + // people in a default installation of Linux. Includes remote root privilege + // escalations, or massive data loss. + CriticalSeverity Severity = "Critical" + + // Defcon1Severity is a Critical problem which has been manually highlighted + // by the team. It requires an immediate attention. + Defcon1Severity Severity = "Defcon1" +) + +// Severities lists all known severities, ordered from lowest to highest. +var Severities = []Severity{ + UnknownSeverity, + NegligibleSeverity, + LowSeverity, + MediumSeverity, + HighSeverity, + CriticalSeverity, + Defcon1Severity, +} + +// NewSeverity attempts to parse a string into a standard Severity value. +func NewSeverity(s string) (Severity, error) { + for _, ss := range Severities { + if strings.EqualFold(s, string(ss)) { + return ss, nil + } + } + + return UnknownSeverity, ErrFailedToParseSeverity +} + +// Compare determines the equality of two severities. +// +// If the severities are equal, returns 0. +// If the receiever is less, returns -1. +// If the receiver is greater, returns 1. +func (s Severity) Compare(s2 Severity) int { + var i1, i2 int + + for i1 = 0; i1 < len(Severities); i1 = i1 + 1 { + if s == Severities[i1] { + break + } + } + for i2 = 0; i2 < len(Severities); i2 = i2 + 1 { + if s2 == Severities[i2] { + break + } + } + + return i1 - i2 +} + +// Scan implements the database/sql.Scanner interface. +func (s *Severity) Scan(value interface{}) error { + val, ok := value.([]byte) + if !ok { + return errors.New("could not scan a Severity from a non-string input") + } + + var err error + *s, err = NewSeverity(string(val)) + if err != nil { + return err + } + + return nil +} + +// Value implements the database/sql/driver.Valuer interface. +func (s Severity) Value() (driver.Value, error) { + return string(s), nil +} diff --git a/severity_test.go b/database/severity_test.go similarity index 75% rename from severity_test.go rename to database/severity_test.go index 6bb180efcf..d255c3739d 100644 --- a/severity_test.go +++ b/database/severity_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package clair +package database import ( "testing" @@ -21,9 +21,9 @@ import ( ) func TestCompareSeverity(t *testing.T) { - assert.Equal(t, Medium.Compare(Medium), 0, "Severity comparison failed") - assert.True(t, Medium.Compare(High) < 0, "Severity comparison failed") - assert.True(t, Critical.Compare(Low) > 0, "Severity comparison failed") + assert.Equal(t, MediumSeverity.Compare(MediumSeverity), 0, "Severity comparison failed") + assert.True(t, MediumSeverity.Compare(HighSeverity) < 0, "Severity comparison failed") + assert.True(t, CriticalSeverity.Compare(LowSeverity) > 0, "Severity comparison failed") } func TestParseSeverity(t *testing.T) { diff --git a/ext/vulnmdsrc/driver.go b/ext/vulnmdsrc/driver.go index 4de7f35825..d1da7c3189 100644 --- a/ext/vulnmdsrc/driver.go +++ b/ext/vulnmdsrc/driver.go @@ -19,7 +19,6 @@ package vulnmdsrc import ( "sync" - "github.com/coreos/clair" "github.com/coreos/clair/database" ) @@ -29,7 +28,7 @@ var ( ) // AppendFunc is the type of a callback provided to an Appender. -type AppendFunc func(metadataKey string, metadata interface{}, severity clair.Severity) +type AppendFunc func(metadataKey string, metadata interface{}, severity database.Severity) // Appender represents anything that can fetch vulnerability metadata and // append it to a Vulnerability. diff --git a/ext/vulnmdsrc/nvd/nvd.go b/ext/vulnmdsrc/nvd/nvd.go index 1c6b755931..c8b7698e8d 100644 --- a/ext/vulnmdsrc/nvd/nvd.go +++ b/ext/vulnmdsrc/nvd/nvd.go @@ -32,7 +32,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/vulnmdsrc" "github.com/coreos/clair/pkg/commonerr" @@ -218,23 +217,25 @@ func getHashFromMetaURL(metaURL string) (string, error) { return "", errors.New("invalid .meta file format") } -// SeverityFromCVSS converts the CVSS Score (0.0 - 10.0) into a clair.Severity -// following the qualitative rating scale available in the CVSS v3.0 -// specification (https://www.first.org/cvss/specification-document), Table 14. +// SeverityFromCVSS converts the CVSS Score (0.0 - 10.0) into a +// database.Severity following the qualitative rating scale available in the +// CVSS v3.0 specification (https://www.first.org/cvss/specification-document), +// Table 14. +// // The Negligible level is set for CVSS scores between [0, 1), replacing the // specified None level, originally used for a score of 0. -func SeverityFromCVSS(score float64) clair.Severity { +func SeverityFromCVSS(score float64) database.Severity { switch { case score < 1.0: - return clair.Negligible + return database.NegligibleSeverity case score < 3.9: - return clair.Low + return database.LowSeverity case score < 6.9: - return clair.Medium + return database.MediumSeverity case score < 8.9: - return clair.High + return database.HighSeverity case score <= 10: - return clair.Critical + return database.CriticalSeverity } - return clair.Unknown + return database.UnknownSeverity } diff --git a/ext/vulnsrc/alpine/alpine.go b/ext/vulnsrc/alpine/alpine.go index b5391ae551..5f3acb22dd 100644 --- a/ext/vulnsrc/alpine/alpine.go +++ b/ext/vulnsrc/alpine/alpine.go @@ -28,7 +28,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -43,9 +42,7 @@ const ( nvdURLPrefix = "https://cve.mitre.org/cgi-bin/cvename.cgi?name=" ) -var ( - log = capnslog.NewPackageLogger("github.com/coreos/clair", "ext/vulnsrc/alpine") -) +var log = capnslog.NewPackageLogger("github.com/coreos/clair", "ext/vulnsrc/alpine") func init() { vulnsrc.RegisterUpdater("alpine", &updater{}) @@ -230,7 +227,7 @@ func parse33YAML(r io.Reader) (vulns []database.Vulnerability, err error) { vulns = append(vulns, database.Vulnerability{ Name: fix, - Severity: clair.Unknown, + Severity: database.UnknownSeverity, Link: nvdURLPrefix + fix, FixedIn: []database.FeatureVersion{ { @@ -284,7 +281,7 @@ func parse34YAML(r io.Reader) (vulns []database.Vulnerability, err error) { for _, vulnStr := range vulnStrs { var vuln database.Vulnerability - vuln.Severity = clair.Unknown + vuln.Severity = database.UnknownSeverity vuln.Name = vulnStr vuln.Link = nvdURLPrefix + vulnStr vuln.FixedIn = []database.FeatureVersion{ diff --git a/ext/vulnsrc/debian/debian.go b/ext/vulnsrc/debian/debian.go index 26860b583f..ec917fe311 100644 --- a/ext/vulnsrc/debian/debian.go +++ b/ext/vulnsrc/debian/debian.go @@ -27,7 +27,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -158,7 +157,7 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability, vulnerability = &database.Vulnerability{ Name: vulnName, Link: strings.Join([]string{cveURLPrefix, "/", vulnName}, ""), - Severity: clair.Unknown, + Severity: database.UnknownSeverity, Description: vulnNode.Description, } } @@ -220,40 +219,40 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability, } // SeverityFromUrgency converts the urgency scale used by the Debian Security -// Bug Tracker into a clair.Severity. -func SeverityFromUrgency(urgency string) clair.Severity { +// Bug Tracker into a database.Severity. +func SeverityFromUrgency(urgency string) database.Severity { switch urgency { case "not yet assigned": - return clair.Unknown + return database.UnknownSeverity case "end-of-life": fallthrough case "unimportant": - return clair.Negligible + return database.NegligibleSeverity case "low": fallthrough case "low*": fallthrough case "low**": - return clair.Low + return database.LowSeverity case "medium": fallthrough case "medium*": fallthrough case "medium**": - return clair.Medium + return database.MediumSeverity case "high": fallthrough case "high*": fallthrough case "high**": - return clair.High + return database.HighSeverity default: log.Warningf("could not determine vulnerability severity from: %s", urgency) - return clair.Unknown + return database.UnknownSeverity } } diff --git a/ext/vulnsrc/debian/debian_test.go b/ext/vulnsrc/debian/debian_test.go index 3a4c0d706c..1c62500c39 100644 --- a/ext/vulnsrc/debian/debian_test.go +++ b/ext/vulnsrc/debian/debian_test.go @@ -20,7 +20,6 @@ import ( "runtime" "testing" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -37,7 +36,7 @@ func TestDebianParser(t *testing.T) { for _, vulnerability := range response.Vulnerabilities { if vulnerability.Name == "CVE-2015-1323" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2015-1323", vulnerability.Link) - assert.Equal(t, clair.Low, vulnerability.Severity) + assert.Equal(t, database.LowSeverity, vulnerability.Severity) assert.Equal(t, "This vulnerability is not very dangerous.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ @@ -68,7 +67,7 @@ func TestDebianParser(t *testing.T) { } } else if vulnerability.Name == "CVE-2003-0779" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2003-0779", vulnerability.Link) - assert.Equal(t, clair.High, vulnerability.Severity) + assert.Equal(t, database.HighSeverity, vulnerability.Severity) assert.Equal(t, "But this one is very dangerous.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ @@ -109,7 +108,7 @@ func TestDebianParser(t *testing.T) { } } else if vulnerability.Name == "CVE-2013-2685" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2013-2685", vulnerability.Link) - assert.Equal(t, clair.Negligible, vulnerability.Severity) + assert.Equal(t, database.NegligibleSeverity, vulnerability.Severity) assert.Equal(t, "Un-affected packages.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ diff --git a/ext/vulnsrc/oracle/oracle.go b/ext/vulnsrc/oracle/oracle.go index e82d18f985..40704096f6 100644 --- a/ext/vulnsrc/oracle/oracle.go +++ b/ext/vulnsrc/oracle/oracle.go @@ -27,7 +27,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/rpm" @@ -337,20 +336,20 @@ func link(def definition) (link string) { return } -func severity(def definition) clair.Severity { +func severity(def definition) database.Severity { switch strings.ToLower(def.Severity) { case "n/a": - return clair.Negligible + return database.NegligibleSeverity case "low": - return clair.Low + return database.LowSeverity case "moderate": - return clair.Medium + return database.MediumSeverity case "important": - return clair.High + return database.HighSeverity case "critical": - return clair.Critical + return database.CriticalSeverity default: log.Warningf("could not determine vulnerability severity from: %s.", def.Severity) - return clair.Unknown + return database.UnknownSeverity } } diff --git a/ext/vulnsrc/oracle/oracle_test.go b/ext/vulnsrc/oracle/oracle_test.go index 2a33207cc9..63d3b7ee93 100644 --- a/ext/vulnsrc/oracle/oracle_test.go +++ b/ext/vulnsrc/oracle/oracle_test.go @@ -20,7 +20,6 @@ import ( "runtime" "testing" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt/rpm" "github.com/stretchr/testify/assert" @@ -38,7 +37,7 @@ func TestOracleParser(t *testing.T) { if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) { assert.Equal(t, "ELSA-2015-1193", vulnerabilities[0].Name) assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1193.html", vulnerabilities[0].Link) - assert.Equal(t, clair.Medium, vulnerabilities[0].Severity) + assert.Equal(t, database.MediumSeverity, vulnerabilities[0].Severity) assert.Equal(t, ` [3.1.1-7] Resolves: rhbz#1217104 CVE-2015-0252 `, vulnerabilities[0].Description) expectedFeatureVersions := []database.FeatureVersion{ @@ -86,7 +85,7 @@ func TestOracleParser(t *testing.T) { if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) { assert.Equal(t, "ELSA-2015-1207", vulnerabilities[0].Name) assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1207.html", vulnerabilities[0].Link) - assert.Equal(t, clair.Critical, vulnerabilities[0].Severity) + assert.Equal(t, database.CriticalSeverity, vulnerabilities[0].Severity) assert.Equal(t, ` [38.1.0-1.0.1.el7_1] - Add firefox-oracle-default-prefs.js and remove the corresponding Red Hat file [38.1.0-1] - Update to 38.1.0 ESR [38.0.1-2] - Fixed rhbz#1222807 by removing preun section `, vulnerabilities[0].Description) expectedFeatureVersions := []database.FeatureVersion{ { diff --git a/ext/vulnsrc/rhel/rhel.go b/ext/vulnsrc/rhel/rhel.go index 9dd66bcc82..a31aa86c65 100644 --- a/ext/vulnsrc/rhel/rhel.go +++ b/ext/vulnsrc/rhel/rhel.go @@ -27,7 +27,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/rpm" @@ -344,18 +343,18 @@ func link(def definition) (link string) { return } -func severity(def definition) clair.Severity { +func severity(def definition) database.Severity { switch strings.TrimSpace(def.Title[strings.LastIndex(def.Title, "(")+1 : len(def.Title)-1]) { case "Low": - return clair.Low + return database.LowSeverity case "Moderate": - return clair.Medium + return database.MediumSeverity case "Important": - return clair.High + return database.HighSeverity case "Critical": - return clair.Critical + return database.CriticalSeverity default: log.Warning("could not determine vulnerability severity from: %s.", def.Title) - return clair.Unknown + return database.UnknownSeverity } } diff --git a/ext/vulnsrc/rhel/rhel_test.go b/ext/vulnsrc/rhel/rhel_test.go index 7aa13938e2..db76261012 100644 --- a/ext/vulnsrc/rhel/rhel_test.go +++ b/ext/vulnsrc/rhel/rhel_test.go @@ -20,7 +20,6 @@ import ( "runtime" "testing" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt/rpm" "github.com/stretchr/testify/assert" @@ -36,7 +35,7 @@ func TestRHELParser(t *testing.T) { if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) { assert.Equal(t, "RHSA-2015:1193", vulnerabilities[0].Name) assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1193.html", vulnerabilities[0].Link) - assert.Equal(t, clair.Medium, vulnerabilities[0].Severity) + assert.Equal(t, database.MediumSeverity, vulnerabilities[0].Severity) 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) expectedFeatureVersions := []database.FeatureVersion{ @@ -83,7 +82,7 @@ func TestRHELParser(t *testing.T) { if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) { assert.Equal(t, "RHSA-2015:1207", vulnerabilities[0].Name) assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1207.html", vulnerabilities[0].Link) - assert.Equal(t, clair.Critical, vulnerabilities[0].Severity) + assert.Equal(t, database.CriticalSeverity, vulnerabilities[0].Severity) 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) expectedFeatureVersions := []database.FeatureVersion{ diff --git a/ext/vulnsrc/ubuntu/ubuntu.go b/ext/vulnsrc/ubuntu/ubuntu.go index 38e73ab160..70602e6c40 100644 --- a/ext/vulnsrc/ubuntu/ubuntu.go +++ b/ext/vulnsrc/ubuntu/ubuntu.go @@ -30,7 +30,6 @@ import ( "github.com/coreos/pkg/capnslog" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" "github.com/coreos/clair/ext/versionfmt/dpkg" @@ -398,30 +397,30 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability database.Vulnerability // If no priority has been provided (CVE-2007-0667 for instance), set the priority to Unknown if vulnerability.Severity == "" { - vulnerability.Severity = clair.Unknown + vulnerability.Severity = database.UnknownSeverity } return } // SeverityFromPriority converts an priority from the Ubuntu CVE Tracker into -// a clair.Severity. -func SeverityFromPriority(priority string) clair.Severity { +// a database.Severity. +func SeverityFromPriority(priority string) database.Severity { switch priority { case "untriaged": - return clair.Unknown + return database.UnknownSeverity case "negligible": - return clair.Negligible + return database.NegligibleSeverity case "low": - return clair.Low + return database.LowSeverity case "medium": - return clair.Medium + return database.MediumSeverity case "high": - return clair.High + return database.HighSeverity case "critical": - return clair.Critical + return database.CriticalSeverity default: log.Warning("could not determine a vulnerability severity from: %s", priority) - return clair.Unknown + return database.UnknownSeverity } } diff --git a/ext/vulnsrc/ubuntu/ubuntu_test.go b/ext/vulnsrc/ubuntu/ubuntu_test.go index 9ebeba7a9c..3b9fd2aec6 100644 --- a/ext/vulnsrc/ubuntu/ubuntu_test.go +++ b/ext/vulnsrc/ubuntu/ubuntu_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/assert" - "github.com/coreos/clair" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/versionfmt" ) @@ -37,7 +36,7 @@ func TestUbuntuParser(t *testing.T) { vulnerability, unknownReleases, err := parseUbuntuCVE(testData) if assert.Nil(t, err) { assert.Equal(t, "CVE-2015-4471", vulnerability.Name) - assert.Equal(t, clair.Medium, vulnerability.Severity) + assert.Equal(t, database.MediumSeverity, vulnerability.Severity) assert.Equal(t, "Off-by-one error in the lzxd_decompress function in lzxd.c in libmspack before 0.5 allows remote attackers to cause a denial of service (buffer under-read and application crash) via a crafted CAB archive.", vulnerability.Description) // Unknown release (line 28) diff --git a/severity.go b/severity.go deleted file mode 100644 index 46bb992784..0000000000 --- a/severity.go +++ /dev/null @@ -1,139 +0,0 @@ -// 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 clair - -import ( - "database/sql/driver" - "errors" - "strings" -) - -var ( - // ErrFailedToParseSeverity is the error returned when a severity could not - // be parsed from a string. - ErrFailedToParseSeverity = errors.New("failed to parse Severity from input") -) - -// Severity defines a standard scale for measuring the severity of a -// vulnerability. -type Severity string - -const ( - // Unknown is either a security problem that has not been - // assigned to a priority yet or a priority that our system - // did not recognize. - Unknown Severity = "Unknown" - - // Negligible is technically a security problem, but is - // only theoretical in nature, requires a very special - // situation, has almost no install base, or does no real - // damage. These tend not to get backport from upstreams, - // and will likely not be included in security updates unless - // there is an easy fix and some other issue causes an update. - Negligible Severity = "Negligible" - - // Low is a security problem, but is hard to - // exploit due to environment, requires a user-assisted - // attack, a small install base, or does very little damage. - // These tend to be included in security updates only when - // higher priority issues require an update, or if many - // low priority issues have built up. - Low Severity = "Low" - - // Medium is a real security problem, and is exploitable - // for many people. Includes network daemon denial of service - // attacks, cross-site scripting, and gaining user privileges. - // Updates should be made soon for this priority of issue. - Medium Severity = "Medium" - - // High is a real problem, exploitable for many people in a default - // installation. Includes serious remote denial of services, - // local root privilege escalations, or data loss. - High Severity = "High" - - // Critical is a world-burning problem, exploitable for nearly all people - // in a default installation of Linux. Includes remote root - // privilege escalations, or massive data loss. - Critical Severity = "Critical" - - // Defcon1 is a Critical problem which has been manually highlighted by - // the team. It requires an immediate attention. - Defcon1 Severity = "Defcon1" -) - -// Severities lists all known severities, ordered from lowest to highest. -var Severities = []Severity{ - Unknown, - Negligible, - Low, - Medium, - High, - Critical, - Defcon1, -} - -// NewSeverity attempts to parse a string into a standard Severity value. -func NewSeverity(s string) (Severity, error) { - for _, ss := range Severities { - if strings.EqualFold(s, string(ss)) { - return ss, nil - } - } - - return Unknown, ErrFailedToParseSeverity -} - -// Compare determines the equality of two severities. -// -// If the severities are equal, returns 0. -// If the receiever is less, returns -1. -// If the receiver is greater, returns 1. -func (s Severity) Compare(s2 Severity) int { - var i1, i2 int - - for i1 = 0; i1 < len(Severities); i1 = i1 + 1 { - if s == Severities[i1] { - break - } - } - for i2 = 0; i2 < len(Severities); i2 = i2 + 1 { - if s2 == Severities[i2] { - break - } - } - - return i1 - i2 -} - -// Scan implements the database/sql.Scanner interface. -func (s *Severity) Scan(value interface{}) error { - val, ok := value.([]byte) - if !ok { - return errors.New("could not scan a Severity from a non-string input") - } - - var err error - *s, err = NewSeverity(string(val)) - if err != nil { - return err - } - - return nil -} - -// Value implements the database/sql/driver.Valuer interface. -func (s Severity) Value() (driver.Value, error) { - return string(s), nil -} diff --git a/updater/updater.go b/updater.go similarity index 88% rename from updater/updater.go rename to updater.go index 267ea7fe89..41eb812815 100644 --- a/updater/updater.go +++ b/updater.go @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package updater updates the vulnerability database periodically using the -// registered vulnerability source updaters and vulnerability metadata -// appenders. -package updater +package clair import ( "math/rand" @@ -27,7 +24,6 @@ import ( "github.com/pborman/uuid" "github.com/prometheus/client_golang/prometheus" - "github.com/coreos/clair" "github.com/coreos/clair/config" "github.com/coreos/clair/database" "github.com/coreos/clair/ext/vulnmdsrc" @@ -36,16 +32,14 @@ import ( ) const ( - flagName = "updater/last" - notesFlagName = "updater/notes" - - lockName = "updater" - lockDuration = refreshLockDuration + time.Minute*2 - refreshLockDuration = time.Minute * 8 + updaterLastFlagName = "updater/last" + updaterLockName = "updater" + updaterLockDuration = updaterLockRefreshDuration + time.Minute*2 + updaterLockRefreshDuration = time.Minute * 8 ) var ( - log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater") + log = capnslog.NewPackageLogger("github.com/coreos/clair", "clair") promUpdaterErrorsTotal = prometheus.NewCounter(prometheus.CounterOpts{ Name: "clair_updater_errors_total", @@ -69,8 +63,9 @@ func init() { prometheus.MustRegister(promUpdaterNotesTotal) } -// Run updates the vulnerability database at regular intervals. -func Run(config *config.UpdaterConfig, datastore database.Datastore, st *stopper.Stopper) { +// RunUpdater begins a process that updates the vulnerability database at +// regular intervals. +func RunUpdater(config *config.UpdaterConfig, datastore database.Datastore, st *stopper.Stopper) { defer st.End() // Do not run the updater if there is no config or if the interval is 0. @@ -100,12 +95,12 @@ func Run(config *config.UpdaterConfig, datastore database.Datastore, st *stopper if nextUpdate.Before(time.Now().UTC()) { // Attempt to get a lock on the the update. log.Debug("attempting to obtain update lock") - hasLock, hasLockUntil := datastore.Lock(lockName, whoAmI, lockDuration, false) + hasLock, hasLockUntil := datastore.Lock(updaterLockName, whoAmI, updaterLockDuration, false) if hasLock { // Launch update in a new go routine. doneC := make(chan bool, 1) go func() { - Update(datastore, firstUpdate) + update(datastore, firstUpdate) doneC <- true }() @@ -113,23 +108,23 @@ func Run(config *config.UpdaterConfig, datastore database.Datastore, st *stopper select { case <-doneC: done = true - case <-time.After(refreshLockDuration): + case <-time.After(updaterLockRefreshDuration): // Refresh the lock until the update is done. - datastore.Lock(lockName, whoAmI, lockDuration, true) + datastore.Lock(updaterLockName, whoAmI, updaterLockDuration, true) case <-st.Chan(): stop = true } } // Unlock the update. - datastore.Unlock(lockName, whoAmI) + datastore.Unlock(updaterLockName, whoAmI) if stop { break } continue } else { - lockOwner, lockExpiration, err := datastore.FindLock(lockName) + lockOwner, lockExpiration, err := datastore.FindLock(updaterLockName) if err != nil { log.Debug("update lock is already taken") nextUpdate = hasLockUntil @@ -162,9 +157,9 @@ func Run(config *config.UpdaterConfig, datastore database.Datastore, st *stopper log.Info("updater service stopped") } -// Update fetches all the vulnerabilities from the registered fetchers, upserts +// update fetches all the vulnerabilities from the registered fetchers, upserts // them into the database and then sends notifications. -func Update(datastore database.Datastore, firstUpdate bool) { +func update(datastore database.Datastore, firstUpdate bool) { defer setUpdaterDuration(time.Now()) log.Info("updating vulnerabilities") @@ -195,7 +190,7 @@ func Update(datastore database.Datastore, firstUpdate bool) { // Update last successful update if every fetchers worked properly. if status { - datastore.InsertKeyValue(flagName, strconv.FormatInt(time.Now().UTC().Unix(), 10)) + datastore.InsertKeyValue(updaterLastFlagName, strconv.FormatInt(time.Now().UTC().Unix(), 10)) } log.Info("update finished") @@ -293,7 +288,7 @@ func addMetadata(datastore database.Datastore, vulnerabilities []database.Vulner } func getLastUpdate(datastore database.Datastore) (time.Time, bool, error) { - lastUpdateTSS, err := datastore.GetKeyValue(flagName) + lastUpdateTSS, err := datastore.GetKeyValue(updaterLastFlagName) if err != nil { return time.Time{}, false, err } @@ -316,7 +311,7 @@ type lockableVulnerability struct { sync.Mutex } -func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interface{}, severity clair.Severity) { +func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interface{}, severity database.Severity) { lv.Lock() defer lv.Unlock() @@ -329,7 +324,7 @@ func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interfa lv.Metadata[metadataKey] = metadata // If necessary, provide a severity for the vulnerability. - if lv.Severity == clair.Unknown { + if lv.Severity == database.UnknownSeverity { lv.Severity = severity } } diff --git a/updater/updater_test.go b/updater_test.go similarity index 97% rename from updater/updater_test.go rename to updater_test.go index c36725715b..380ff2779e 100644 --- a/updater/updater_test.go +++ b/updater_test.go @@ -1,4 +1,4 @@ -// Copyright 2016 clair authors +// 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. @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package updater +package clair import ( "fmt"