func TestInsertVulnerabilityNotifications(t *testing.T) { Open(&config.DatabaseConfig{Type: "memstore"}) defer Close() pkg1 := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")} pkg1b := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.2")} pkg2 := &Package{OS: "testOS", Name: "testpkg2", Version: types.NewVersionUnsafe("1.0")} InsertPackages([]*Package{pkg1, pkg1b, pkg2}) // NewVulnerabilityNotification vuln1 := &Vulnerability{ID: "test1", Link: "link1", Priority: types.Medium, Description: "testDescription1", FixedInNodes: []string{pkg1.Node}} vuln2 := &Vulnerability{ID: "test2", Link: "link2", Priority: types.High, Description: "testDescription2", FixedInNodes: []string{pkg1.Node, pkg2.Node}} vuln1b := &Vulnerability{ID: "test1", Priority: types.High, FixedInNodes: []string{"pkg3"}} notifications, err := InsertVulnerabilities([]*Vulnerability{vuln1, vuln2, vuln1b}) if assert.Nil(t, err) { // We should only have two NewVulnerabilityNotification notifications: one for test1 and one for test2 // We should not have a VulnerabilityPriorityIncreasedNotification or a VulnerabilityPackageChangedNotification // for test1 because it is in the same batch if assert.Len(t, notifications, 2) { for _, n := range notifications { _, ok := n.(*NewVulnerabilityNotification) assert.True(t, ok) } } } // VulnerabilityPriorityIncreasedNotification vuln1c := &Vulnerability{ID: "test1", Priority: types.Critical} notifications, err = InsertVulnerabilities([]*Vulnerability{vuln1c}) if assert.Nil(t, err) { if assert.Len(t, notifications, 1) { if nn, ok := notifications[0].(*VulnerabilityPriorityIncreasedNotification); assert.True(t, ok) { assert.Equal(t, vuln1b.Priority, nn.OldPriority) assert.Equal(t, vuln1c.Priority, nn.NewPriority) } } } notifications, err = InsertVulnerabilities([]*Vulnerability{&Vulnerability{ID: "test1", Priority: types.Low}}) assert.Nil(t, err) assert.Len(t, notifications, 0) // VulnerabilityPackageChangedNotification vuln1e := &Vulnerability{ID: "test1", FixedInNodes: []string{pkg1b.Node}} vuln1f := &Vulnerability{ID: "test1", FixedInNodes: []string{pkg2.Node}} notifications, err = InsertVulnerabilities([]*Vulnerability{vuln1e, vuln1f}) if assert.Nil(t, err) { if assert.Len(t, notifications, 1) { if nn, ok := notifications[0].(*VulnerabilityPackageChangedNotification); assert.True(t, ok) { // Here, we say that pkg1b fixes the vulnerability, but as pkg1b is in // the same branch as pkg1, pkg1 should be removed and pkg1b added // We also add pkg2 as fixed assert.Contains(t, nn.AddedFixedInNodes, pkg1b.Node) assert.Contains(t, nn.RemovedFixedInNodes, pkg1.Node) assert.Contains(t, nn.AddedFixedInNodes, pkg2.Node) } } } }
func TestDpkgFeatureDetection(t *testing.T) { testData := []feature.TestData{ // Test an Ubuntu dpkg status file { FeatureVersions: []database.FeatureVersion{ // Two packages from this source are installed, it should only appear one time { Feature: database.Feature{Name: "pam"}, Version: types.NewVersionUnsafe("1.1.8-3.1ubuntu3"), }, { Feature: database.Feature{Name: "makedev"}, // The source name and the package name are equals Version: types.NewVersionUnsafe("2.3.1-93ubuntu1"), // The version comes from the "Version:" line }, { Feature: database.Feature{Name: "gcc-5"}, Version: types.NewVersionUnsafe("5.1.1-12ubuntu1"), // The version comes from the "Source:" line }, }, Data: map[string][]byte{ "var/lib/dpkg/status": feature.LoadFileForTest("dpkg/testdata/status"), }, }, } feature.TestDetector(t, &DpkgFeaturesDetector{}, testData) }
func TestRpmFeatureDetection(t *testing.T) { testData := []feature.TestData{ // Test a CentOS 7 RPM database // Memo: Use the following command on a RPM-based system to shrink a database: rpm -qa --qf "%{NAME}\n" |tail -n +3| xargs rpm -e --justdb { FeatureVersions: []database.FeatureVersion{ // Two packages from this source are installed, it should only appear once { Feature: database.Feature{Name: "centos-release"}, Version: types.NewVersionUnsafe("7-1.1503.el7.centos.2.8"), }, // Two packages from this source are installed, it should only appear once { Feature: database.Feature{Name: "filesystem"}, Version: types.NewVersionUnsafe("3.2-18.el7"), }, }, Data: map[string][]byte{ "var/lib/rpm/Packages": feature.LoadFileForTest("rpm/testdata/Packages"), }, }, } feature.TestDetector(t, &RpmFeaturesDetector{}, testData) }
func TestRHELParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) path := path.Join(path.Dir(filename)) // Test parsing testdata/fetcher_rhel_test.1.xml testFile, _ := os.Open(path + "/testdata/fetcher_rhel_test.1.xml") vulnerabilities, 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{ OS: "centos:7", Name: "xerces-c", Version: types.NewVersionUnsafe("3.1.1-7.el7_1"), }) assert.Contains(t, vulnerabilities[0].FixedIn, &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{ OS: "centos:7", Name: "xerces-c-doc", Version: types.NewVersionUnsafe("3.1.1-7.el7_1"), }) } } // Test parsing testdata/fetcher_rhel_test.2.xml testFile, _ = os.Open(path + "/testdata/fetcher_rhel_test.2.xml") vulnerabilities, 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{ OS: "centos:6", Name: "firefox", Version: types.NewVersionUnsafe("38.1.0-1.el6_6"), }) assert.Contains(t, vulnerabilities[0].FixedIn, &database.Package{ OS: "centos:7", Name: "firefox", Version: types.NewVersionUnsafe("38.1.0-1.el7_1"), }) } } }
func TestDebianParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) // Test parsing testdata/fetcher_debian_test.json testFile, _ := os.Open(path.Join(path.Dir(filename)) + "/testdata/fetcher_debian_test.json") response, err := buildResponse(testFile, "") if assert.Nil(t, err) && assert.Len(t, response.Vulnerabilities, 2) { for _, vulnerability := range response.Vulnerabilities { if vulnerability.ID == "CVE-2015-1323" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2015-1323", vulnerability.Link) 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{ OS: "debian:8", Name: "aptdaemon", Version: types.MaxVersion, }) assert.Contains(t, vulnerability.FixedIn, &database.Package{ OS: "debian:unstable", Name: "aptdaemon", Version: types.NewVersionUnsafe("1.1.1+bzr982-1"), }) } } 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{ OS: "debian:8", Name: "aptdaemon", Version: types.NewVersionUnsafe("0.7.0"), }) assert.Contains(t, vulnerability.FixedIn, &database.Package{ OS: "debian:unstable", Name: "aptdaemon", Version: types.NewVersionUnsafe("0.7.0"), }) assert.Contains(t, vulnerability.FixedIn, &database.Package{ OS: "debian:8", Name: "asterisk", Version: types.NewVersionUnsafe("0.5.56"), }) } } else { assert.Fail(t, "Wrong vulnerability name: ", vulnerability.ID) } } } }
func TestFindVulnerability(t *testing.T) { datastore, err := openDatabaseForTest("FindVulnerability", true) if err != nil { t.Error(err) return } defer datastore.Close() // Find a vulnerability that does not exist. _, err = datastore.FindVulnerability("", "") assert.Equal(t, cerrors.ErrNotFound, err) // Find a normal vulnerability. v1 := database.Vulnerability{ 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: types.High, Namespace: database.Namespace{Name: "debian:7"}, FixedIn: []database.FeatureVersion{ { Feature: database.Feature{Name: "openssl"}, Version: types.NewVersionUnsafe("2.0"), }, { Feature: database.Feature{Name: "libssl"}, Version: types.NewVersionUnsafe("1.9-abc"), }, }, } v1f, err := datastore.FindVulnerability("debian:7", "CVE-OPENSSL-1-DEB7") if assert.Nil(t, err) { equalsVuln(t, &v1, &v1f) } // Find a vulnerability that has no link, no severity and no FixedIn. v2 := database.Vulnerability{ Name: "CVE-NOPE", Description: "A vulnerability affecting nothing", Namespace: database.Namespace{Name: "debian:7"}, Severity: types.Unknown, } v2f, err := datastore.FindVulnerability("debian:7", "CVE-NOPE") if assert.Nil(t, err) { equalsVuln(t, &v2, &v2f) } }
func TestDistUpgrade(t *testing.T) { database.Open(&config.DatabaseConfig{Type: "memstore"}) defer database.Close() _, f, _, _ := runtime.Caller(0) path := path.Join(path.Dir(f)) + "/testdata/DistUpgrade/" // blank.tar: MAINTAINER Quentin MACHU <quentin.machu.fr> // wheezy.tar: FROM debian:wheezy // jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update && apt-get -y dist-upgrade assert.Nil(t, Process("blank", "", path+"blank.tar.gz", "Docker")) assert.Nil(t, Process("wheezy", "blank", path+"wheezy.tar.gz", "Docker")) assert.Nil(t, Process("jessie", "wheezy", path+"jessie.tar.gz", "Docker")) err := Process("blank", "", path+"blank.tar.gz", "") assert.Error(t, err, "could not process a layer which does not have a specified format") err = Process("blank", "", path+"blank.tar.gz", "invalid") assert.Error(t, err, "could not process a layer which does not have a supported format") wheezy, err := database.FindOneLayerByID("wheezy", database.FieldLayerAll) if assert.Nil(t, err) { assert.Equal(t, "debian:7", wheezy.OS) assert.Len(t, wheezy.InstalledPackagesNodes, 52) assert.Len(t, wheezy.RemovedPackagesNodes, 0) jessie, err := database.FindOneLayerByID("jessie", database.FieldLayerAll) if assert.Nil(t, err) { assert.Equal(t, "debian:8", jessie.OS) assert.Len(t, jessie.InstalledPackagesNodes, 66) assert.Len(t, jessie.RemovedPackagesNodes, 44) packageNodes, err := jessie.AllPackages() if assert.Nil(t, err) { // These packages haven't been upgraded nonUpgradedPackages := []database.Package{ database.Package{Name: "libtext-wrapi18n-perl", Version: types.NewVersionUnsafe("0.06-7")}, database.Package{Name: "libtext-charwidth-perl", Version: types.NewVersionUnsafe("0.04-7")}, database.Package{Name: "libtext-iconv-perl", Version: types.NewVersionUnsafe("1.7-5")}, database.Package{Name: "mawk", Version: types.NewVersionUnsafe("1.3.3-17")}, database.Package{Name: "insserv", Version: types.NewVersionUnsafe("1.14.0-5")}, database.Package{Name: "db", Version: types.NewVersionUnsafe("5.1.29-5")}, database.Package{Name: "ustr", Version: types.NewVersionUnsafe("1.0.4-3")}, database.Package{Name: "xz-utils", Version: types.NewVersionUnsafe("5.1.1alpha+20120614-2")}, } for _, p := range nonUpgradedPackages { p.OS = "debian:7" assert.Contains(t, packageNodes, p.GetNode(), "Jessie layer doesn't have %s but it should.", p) } for _, p := range nonUpgradedPackages { p.OS = "debian:8" assert.NotContains(t, packageNodes, p.GetNode(), "Jessie layer has %s but it shouldn't.", p) } } } } }
func TestUbuntuParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) path := filepath.Join(filepath.Dir(filename)) // Test parsing testdata/fetcher_ testData, _ := os.Open(path + "/testdata/fetcher_ubuntu_test.txt") defer testData.Close() vulnerability, unknownReleases, err := parseUbuntuCVE(testData) if assert.Nil(t, err) { assert.Equal(t, "CVE-2015-4471", vulnerability.Name) assert.Equal(t, types.Medium, 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) _, hasUnkownRelease := unknownReleases["unknown"] assert.True(t, hasUnkownRelease) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "ubuntu:14.04"}, Name: "libmspack", }, Version: types.MaxVersion, }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "ubuntu:15.04"}, Name: "libmspack", }, Version: types.NewVersionUnsafe("0.4-3"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "ubuntu:15.10"}, Name: "libmspack-anotherpkg", }, Version: types.NewVersionUnsafe("0.1"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerability.FixedIn, expectedFeatureVersion) } } }
func TestDoVulnerabilitiesNamespacing(t *testing.T) { fv1 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "Namespace1"}, Name: "Feature1", }, Version: types.NewVersionUnsafe("0.1"), } fv2 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "Namespace2"}, Name: "Feature1", }, Version: types.NewVersionUnsafe("0.2"), } fv3 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "Namespace2"}, Name: "Feature2", }, Version: types.NewVersionUnsafe("0.3"), } vulnerability := database.Vulnerability{ Name: "DoVulnerabilityNamespacing", FixedIn: []database.FeatureVersion{fv1, fv2, fv3}, } vulnerabilities := doVulnerabilitiesNamespacing([]database.Vulnerability{vulnerability}) for _, vulnerability := range vulnerabilities { switch vulnerability.Namespace.Name { case fv1.Feature.Namespace.Name: assert.Len(t, vulnerability.FixedIn, 1) assert.Contains(t, vulnerability.FixedIn, fv1) case fv2.Feature.Namespace.Name: assert.Len(t, vulnerability.FixedIn, 2) assert.Contains(t, vulnerability.FixedIn, fv2) assert.Contains(t, vulnerability.FixedIn, fv3) default: t.Errorf("Should not have a Vulnerability with '%s' as its Namespace.", vulnerability.Namespace.Name) fmt.Printf("%#v\n", vulnerability) } } }
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") ov := &oval.OvalFetcher{OsInfo: &SLEInfo{}} 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) // Severity is not defined for SLE assert.Equal(t, types.Unknown, vulnerabilities[0].Severity) 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) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "sle:12"}, Name: "xfsprogs", }, Version: types.NewVersionUnsafe("3.2.1-3.5"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "sle:12.1"}, Name: "xfsprogs", }, Version: types.NewVersionUnsafe("3.2.1-3.5"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerabilities[0].FixedIn, expectedFeatureVersion) } } }
func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) { f7 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace3"}, Name: "TestInsertLayerFeature7", }, Version: types.NewVersionUnsafe("0.01"), } l3, _ := datastore.FindLayer("TestInsertLayer3", true, false) l3u := database.Layer{ Name: l3.Name, Parent: l3.Parent, Namespace: &database.Namespace{Name: "TestInsertLayerNamespaceUpdated1"}, Features: []database.FeatureVersion{f7}, } l4u := database.Layer{ Name: "TestInsertLayer4", Parent: &database.Layer{Name: "TestInsertLayer3"}, Features: []database.FeatureVersion{f7}, EngineVersion: 2, } // Try to re-insert without increasing the EngineVersion. err := datastore.InsertLayer(l3u) assert.Nil(t, err) l3uf, err := datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3.Namespace.Name, l3uf.Namespace.Name) assert.Equal(t, l3.EngineVersion, l3uf.EngineVersion) assert.Len(t, l3uf.Features, len(l3.Features)) } // Update layer l3. // Verify that the Namespace, EngineVersion and FeatureVersions got updated. l3u.EngineVersion = 2 err = datastore.InsertLayer(l3u) assert.Nil(t, err) l3uf, err = datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3u.Namespace.Name, l3uf.Namespace.Name) assert.Equal(t, l3u.EngineVersion, l3uf.EngineVersion) if assert.Len(t, l3uf.Features, 1) { assert.True(t, cmpFV(l3uf.Features[0], f7), "Updated layer should have %#v but actually have %#v", f7, l3uf.Features[0]) } } // Update layer l4. // Verify that the Namespace got updated from its new Parent's, and also verify the // EnginVersion and FeatureVersions. l4u.Parent = &l3uf err = datastore.InsertLayer(l4u) assert.Nil(t, err) l4uf, err := datastore.FindLayer(l3u.Name, true, false) if assert.Nil(t, err) { assert.Equal(t, l3u.Namespace.Name, l4uf.Namespace.Name) assert.Equal(t, l4u.EngineVersion, l4uf.EngineVersion) if assert.Len(t, l4uf.Features, 1) { assert.True(t, cmpFV(l3uf.Features[0], f7), "Updated layer should have %#v but actually have %#v", f7, l4uf.Features[0]) } } }
func TestProcessWithDistUpgrade(t *testing.T) { // TODO(Quentin-M): This should not be bound to a single database implementation. datastore, err := pgsql.OpenForTest("ProcessWithDistUpgrade", false) if err != nil { t.Error(err) return } defer datastore.Close() _, f, _, _ := runtime.Caller(0) path := path.Join(path.Dir(f)) + "/testdata/DistUpgrade/" // blank.tar: MAINTAINER Quentin MACHU <quentin.machu.fr> // wheezy.tar: FROM debian:wheezy // jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update && // apt-get -y dist-upgrade assert.Nil(t, Process(datastore, "Docker", "blank", "", path+"blank.tar.gz", nil)) assert.Nil(t, Process(datastore, "Docker", "wheezy", "blank", path+"wheezy.tar.gz", nil)) assert.Nil(t, Process(datastore, "Docker", "jessie", "wheezy", path+"jessie.tar.gz", nil)) wheezy, err := datastore.FindLayer("wheezy", true, false) if assert.Nil(t, err) { assert.Equal(t, "debian:7", wheezy.Namespace.Name) assert.Len(t, wheezy.Features, 52) jessie, err := datastore.FindLayer("jessie", true, false) if assert.Nil(t, err) { assert.Equal(t, "debian:8", jessie.Namespace.Name) assert.Len(t, jessie.Features, 74) // These FeatureVersions haven't been upgraded. nonUpgradedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{Name: "libtext-wrapi18n-perl"}, Version: types.NewVersionUnsafe("0.06-7"), }, { Feature: database.Feature{Name: "libtext-charwidth-perl"}, Version: types.NewVersionUnsafe("0.04-7"), }, { Feature: database.Feature{Name: "libtext-iconv-perl"}, Version: types.NewVersionUnsafe("1.7-5"), }, { Feature: database.Feature{Name: "mawk"}, Version: types.NewVersionUnsafe("1.3.3-17"), }, { Feature: database.Feature{Name: "insserv"}, Version: types.NewVersionUnsafe("1.14.0-5"), }, { Feature: database.Feature{Name: "db"}, Version: types.NewVersionUnsafe("5.1.29-5"), }, { Feature: database.Feature{Name: "ustr"}, Version: types.NewVersionUnsafe("1.0.4-3"), }, { Feature: database.Feature{Name: "xz-utils"}, Version: types.NewVersionUnsafe("5.1.1alpha+20120614-2"), }, } for _, nufv := range nonUpgradedFeatureVersions { nufv.Feature.Namespace.Name = "debian:7" found := false for _, fv := range jessie.Features { if fv.Feature.Name == nufv.Feature.Name && fv.Feature.Namespace.Name == nufv.Feature.Namespace.Name && fv.Version == nufv.Version { found = true break } } assert.Equal(t, true, found, "Jessie layer doesn't have %#v but it should.", nufv) } for _, nufv := range nonUpgradedFeatureVersions { nufv.Feature.Namespace.Name = "debian:8" found := false for _, fv := range jessie.Features { if fv.Feature.Name == nufv.Feature.Name && fv.Feature.Namespace.Name == nufv.Feature.Namespace.Name && fv.Version == nufv.Version { found = true break } } assert.Equal(t, false, found, "Jessie layer has %#v but it shouldn't.", nufv) } } } }
func TestNotification(t *testing.T) { datastore, err := openDatabaseForTest("Notification", false) if err != nil { t.Error(err) return } defer datastore.Close() // Try to get a notification when there is none. _, err = datastore.GetAvailableNotification(time.Second) assert.Equal(t, cerrors.ErrNotFound, err) // Create some data. f1 := database.Feature{ Name: "TestNotificationFeature1", Namespace: database.Namespace{Name: "TestNotificationNamespace1"}, } f2 := database.Feature{ Name: "TestNotificationFeature2", Namespace: database.Namespace{Name: "TestNotificationNamespace1"}, } l1 := database.Layer{ Name: "TestNotificationLayer1", Features: []database.FeatureVersion{ { Feature: f1, Version: types.NewVersionUnsafe("0.1"), }, }, } l2 := database.Layer{ Name: "TestNotificationLayer2", Features: []database.FeatureVersion{ { Feature: f1, Version: types.NewVersionUnsafe("0.2"), }, }, } l3 := database.Layer{ Name: "TestNotificationLayer3", Features: []database.FeatureVersion{ { Feature: f1, Version: types.NewVersionUnsafe("0.3"), }, }, } l4 := database.Layer{ Name: "TestNotificationLayer4", Features: []database.FeatureVersion{ { Feature: f2, Version: types.NewVersionUnsafe("0.1"), }, }, } if !assert.Nil(t, datastore.InsertLayer(l1)) || !assert.Nil(t, datastore.InsertLayer(l2)) || !assert.Nil(t, datastore.InsertLayer(l3)) || !assert.Nil(t, datastore.InsertLayer(l4)) { return } // Insert a new vulnerability that is introduced by three layers. v1 := database.Vulnerability{ Name: "TestNotificationVulnerability1", Namespace: f1.Namespace, Description: "TestNotificationDescription1", Link: "TestNotificationLink1", Severity: "Unknown", FixedIn: []database.FeatureVersion{ { Feature: f1, Version: types.NewVersionUnsafe("1.0"), }, }, } assert.Nil(t, datastore.insertVulnerability(v1, false, true)) // Get the notification associated to the previously inserted vulnerability. notification, err := datastore.GetAvailableNotification(time.Second) if assert.Nil(t, err) && assert.NotEmpty(t, notification.Name) { // Verify the renotify behaviour. if assert.Nil(t, datastore.SetNotificationNotified(notification.Name)) { _, err := datastore.GetAvailableNotification(time.Second) assert.Equal(t, cerrors.ErrNotFound, err) time.Sleep(50 * time.Millisecond) notificationB, err := datastore.GetAvailableNotification(20 * time.Millisecond) assert.Nil(t, err) assert.Equal(t, notification.Name, notificationB.Name) datastore.SetNotificationNotified(notification.Name) } // Get notification. filledNotification, nextPage, err := datastore.GetNotification(notification.Name, 2, database.VulnerabilityNotificationFirstPage) if assert.Nil(t, err) { assert.NotEqual(t, database.NoVulnerabilityNotificationPage, nextPage) assert.Nil(t, filledNotification.OldVulnerability) if assert.NotNil(t, filledNotification.NewVulnerability) { assert.Equal(t, v1.Name, filledNotification.NewVulnerability.Name) assert.Len(t, filledNotification.NewVulnerability.LayersIntroducingVulnerability, 2) } } // Get second page. filledNotification, nextPage, err = datastore.GetNotification(notification.Name, 2, nextPage) if assert.Nil(t, err) { assert.Equal(t, database.NoVulnerabilityNotificationPage, nextPage) assert.Nil(t, filledNotification.OldVulnerability) if assert.NotNil(t, filledNotification.NewVulnerability) { assert.Equal(t, v1.Name, filledNotification.NewVulnerability.Name) assert.Len(t, filledNotification.NewVulnerability.LayersIntroducingVulnerability, 1) } } // Delete notification. assert.Nil(t, datastore.DeleteNotification(notification.Name)) _, err = datastore.GetAvailableNotification(time.Millisecond) assert.Equal(t, cerrors.ErrNotFound, err) } // Update a vulnerability and ensure that the old/new vulnerabilities are correct. v1b := v1 v1b.Severity = types.High v1b.FixedIn = []database.FeatureVersion{ { Feature: f1, Version: types.MinVersion, }, { Feature: f2, Version: types.MaxVersion, }, } if assert.Nil(t, datastore.insertVulnerability(v1b, false, true)) { notification, err = datastore.GetAvailableNotification(time.Second) assert.Nil(t, err) assert.NotEmpty(t, notification.Name) if assert.Nil(t, err) && assert.NotEmpty(t, notification.Name) { filledNotification, nextPage, err := datastore.GetNotification(notification.Name, 2, database.VulnerabilityNotificationFirstPage) if assert.Nil(t, err) { if assert.NotNil(t, filledNotification.OldVulnerability) { assert.Equal(t, v1.Name, filledNotification.OldVulnerability.Name) assert.Equal(t, v1.Severity, filledNotification.OldVulnerability.Severity) assert.Len(t, filledNotification.OldVulnerability.LayersIntroducingVulnerability, 2) } if assert.NotNil(t, filledNotification.NewVulnerability) { assert.Equal(t, v1b.Name, filledNotification.NewVulnerability.Name) assert.Equal(t, v1b.Severity, filledNotification.NewVulnerability.Severity) assert.Len(t, filledNotification.NewVulnerability.LayersIntroducingVulnerability, 1) } assert.Equal(t, -1, nextPage.NewVulnerability) } assert.Nil(t, datastore.DeleteNotification(notification.Name)) } } // Delete a vulnerability and verify the notification. if assert.Nil(t, datastore.DeleteVulnerability(v1b.Namespace.Name, v1b.Name)) { notification, err = datastore.GetAvailableNotification(time.Second) assert.Nil(t, err) assert.NotEmpty(t, notification.Name) if assert.Nil(t, err) && assert.NotEmpty(t, notification.Name) { filledNotification, _, err := datastore.GetNotification(notification.Name, 2, database.VulnerabilityNotificationFirstPage) if assert.Nil(t, err) { assert.Nil(t, filledNotification.NewVulnerability) if assert.NotNil(t, filledNotification.OldVulnerability) { assert.Equal(t, v1b.Name, filledNotification.OldVulnerability.Name) assert.Equal(t, v1b.Severity, filledNotification.OldVulnerability.Severity) assert.Len(t, filledNotification.OldVulnerability.LayersIntroducingVulnerability, 1) } } assert.Nil(t, datastore.DeleteNotification(notification.Name)) } } }
func TestPackage(t *testing.T) { Open("memstore", "") defer Close() // Try to insert invalid packages for _, invalidPkg := range []*Package{ &Package{OS: "", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")}, &Package{OS: "testOS", Name: "", Version: types.NewVersionUnsafe("1.0")}, &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("")}, &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("bad version")}, &Package{OS: "", Name: "", Version: types.NewVersionUnsafe("")}, } { err := InsertPackages([]*Package{invalidPkg}) assert.Error(t, err) } // Insert a package pkg1 := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")} err := InsertPackages([]*Package{pkg1}) if assert.Nil(t, err) { // Find the inserted package and verify its content pkg1b, err := FindOnePackage(pkg1.OS, pkg1.Name, pkg1.Version, FieldPackageAll) if assert.Nil(t, err) && assert.NotNil(t, pkg1b) { assert.Equal(t, pkg1.Node, pkg1b.Node) assert.Equal(t, pkg1.OS, pkg1b.OS) assert.Equal(t, pkg1.Name, pkg1b.Name) assert.Equal(t, pkg1.Version, pkg1b.Version) } // Find packages from the inserted branch and verify their content // (the first one should be a start package, the second one the inserted one and the third one the end package) pkgs1c, err := FindAllPackagesByBranch(pkg1.OS, pkg1.Name, FieldPackageAll) if assert.Nil(t, err) && assert.Equal(t, 3, len(pkgs1c)) { sort.Sort(ByVersion(pkgs1c)) assert.Equal(t, pkg1.OS, pkgs1c[0].OS) assert.Equal(t, pkg1.Name, pkgs1c[0].Name) assert.Equal(t, types.MinVersion, pkgs1c[0].Version) assert.Equal(t, pkg1.OS, pkgs1c[1].OS) assert.Equal(t, pkg1.Name, pkgs1c[1].Name) assert.Equal(t, pkg1.Version, pkgs1c[1].Version) assert.Equal(t, pkg1.OS, pkgs1c[2].OS) assert.Equal(t, pkg1.Name, pkgs1c[2].Name) assert.Equal(t, types.MaxVersion, pkgs1c[2].Version) } } // Insert multiple packages in the same branch, one in another branch, insert local duplicates and database duplicates as well pkg2 := []*Package{ &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("0.8")}, &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("0.9")}, &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")}, // Already present in the database &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.1")}, &Package{OS: "testOS", Name: "testpkg2", Version: types.NewVersionUnsafe("1.0")}, // Another branch &Package{OS: "testOS", Name: "testpkg2", Version: types.NewVersionUnsafe("1.0")}, // Local duplicates } nbInSameBranch := 4 + 2 // (start/end packages) err = InsertPackages(shuffle(pkg2)) if assert.Nil(t, err) { // Find packages from the inserted branch, verify their order and NextVersion / PreviousVersion pkgs2b, err := FindAllPackagesByBranch("testOS", "testpkg1", FieldPackageAll) if assert.Nil(t, err) && assert.Equal(t, nbInSameBranch, len(pkgs2b)) { sort.Sort(ByVersion(pkgs2b)) for i := 0; i < nbInSameBranch; i = i + 1 { if i == 0 { assert.Equal(t, types.MinVersion, pkgs2b[0].Version) } else if i < nbInSameBranch-2 { assert.Equal(t, pkg2[i].Version, pkgs2b[i+1].Version) nv, err := pkgs2b[i+1].NextVersion(FieldPackageAll) assert.Nil(t, err) assert.Equal(t, pkgs2b[i+2], nv) if i > 0 { pv, err := pkgs2b[i].PreviousVersion(FieldPackageAll) assert.Nil(t, err) assert.Equal(t, pkgs2b[i-1], pv) } else { pv, err := pkgs2b[i].PreviousVersion(FieldPackageAll) assert.Nil(t, err) assert.Nil(t, pv) } } else { assert.Equal(t, types.MaxVersion, pkgs2b[nbInSameBranch-1].Version) nv, err := pkgs2b[nbInSameBranch-1].NextVersion(FieldPackageAll) assert.Nil(t, err) assert.Nil(t, nv) pv, err := pkgs2b[i].PreviousVersion(FieldPackageAll) assert.Nil(t, err) assert.Equal(t, pkgs2b[i-1], pv) } } // NextVersions nv, err := pkgs2b[0].NextVersions(FieldPackageAll) if assert.Nil(t, err) && assert.Len(t, nv, nbInSameBranch-1) { for i := 0; i < nbInSameBranch-1; i = i + 1 { if i < nbInSameBranch-2 { assert.Equal(t, pkg2[i].Version, nv[i].Version) } else { assert.Equal(t, types.MaxVersion, nv[i].Version) } } } // PreviousVersions pv, err := pkgs2b[nbInSameBranch-1].PreviousVersions(FieldPackageAll) if assert.Nil(t, err) && assert.Len(t, pv, nbInSameBranch-1) { for i := 0; i < len(pv); i = i + 1 { assert.Equal(t, pkgs2b[len(pkgs2b)-i-2], pv[i]) } } } // Verify that the one we added which was already present in the database has the same node value (meaning that we just fetched it actually) assert.Contains(t, pkg2, pkg1) } // Insert duplicated latest packages directly, ensure only one is actually inserted. Then insert another package in the branch and ensure that its next version is the latest one pkg3a := &Package{OS: "testOS", Name: "testpkg3", Version: types.MaxVersion} pkg3b := &Package{OS: "testOS", Name: "testpkg3", Version: types.MaxVersion} pkg3c := &Package{OS: "testOS", Name: "testpkg3", Version: types.MaxVersion} err1 := InsertPackages([]*Package{pkg3a, pkg3b}) err2 := InsertPackages([]*Package{pkg3c}) if assert.Nil(t, err1) && assert.Nil(t, err2) { assert.Equal(t, pkg3a, pkg3b) assert.Equal(t, pkg3b, pkg3c) } pkg4 := Package{OS: "testOS", Name: "testpkg3", Version: types.NewVersionUnsafe("1.0")} InsertPackages([]*Package{&pkg4}) pkgs34, _ := FindAllPackagesByBranch("testOS", "testpkg3", FieldPackageAll) if assert.Len(t, pkgs34, 3) { sort.Sort(ByVersion(pkgs34)) assert.Equal(t, pkg4.Node, pkgs34[1].Node) assert.Equal(t, pkg3a.Node, pkgs34[2].Node) assert.Equal(t, pkg3a.Node, pkgs34[1].NextVersionNode) } // Insert two identical packages but with "different" versions // The second version should be simplified to the first one // Therefore, we should just have three packages (the inserted one and the start/end packages of the branch) InsertPackages([]*Package{&Package{OS: "testOS", Name: "testdirtypkg", Version: types.NewVersionUnsafe("0.1")}}) InsertPackages([]*Package{&Package{OS: "testOS", Name: "testdirtypkg", Version: types.NewVersionUnsafe("0:0.1")}}) dirtypkgs, err := FindAllPackagesByBranch("testOS", "testdirtypkg", FieldPackageAll) assert.Nil(t, err) assert.Len(t, dirtypkgs, 3) }
func TestInsertFeature(t *testing.T) { datastore, err := OpenForTest("InsertFeature", false) if err != nil { t.Error(err) return } defer datastore.Close() // Invalid Feature. id0, err := datastore.insertFeature(database.Feature{}) assert.NotNil(t, err) assert.Zero(t, id0) id0, err = datastore.insertFeature(database.Feature{ Namespace: database.Namespace{}, Name: "TestInsertFeature0", }) assert.NotNil(t, err) assert.Zero(t, id0) // Insert Feature and ensure we can find it. feature := database.Feature{ Namespace: database.Namespace{Name: "TestInsertFeatureNamespace1"}, Name: "TestInsertFeature1", } id1, err := datastore.insertFeature(feature) assert.Nil(t, err) id2, err := datastore.insertFeature(feature) assert.Nil(t, err) assert.Equal(t, id1, id2) // Insert invalid FeatureVersion. for _, invalidFeatureVersion := range []database.FeatureVersion{ { Feature: database.Feature{}, Version: types.NewVersionUnsafe("1.0"), }, { Feature: database.Feature{ Namespace: database.Namespace{}, Name: "TestInsertFeature2", }, Version: types.NewVersionUnsafe("1.0"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertFeatureNamespace2"}, Name: "TestInsertFeature2", }, Version: types.NewVersionUnsafe(""), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertFeatureNamespace2"}, Name: "TestInsertFeature2", }, Version: types.NewVersionUnsafe("bad version"), }, } { id3, err := datastore.insertFeatureVersion(invalidFeatureVersion) assert.Error(t, err) assert.Zero(t, id3) } // Insert FeatureVersion and ensure we can find it. featureVersion := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertFeatureNamespace1"}, Name: "TestInsertFeature1", }, Version: types.NewVersionUnsafe("2:3.0-imba"), } id4, err := datastore.insertFeatureVersion(featureVersion) assert.Nil(t, err) id5, err := datastore.insertFeatureVersion(featureVersion) assert.Nil(t, err) assert.Equal(t, id4, id5) }
func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.Row) (database.Vulnerability, error) { var vulnerability database.Vulnerability err := vulnerabilityRow.Scan( &vulnerability.ID, &vulnerability.Name, &vulnerability.Namespace.ID, &vulnerability.Namespace.Name, &vulnerability.Description, &vulnerability.Link, &vulnerability.Severity, &vulnerability.Metadata, ) if err != nil { return vulnerability, handleError(queryName+".Scan()", err) } if vulnerability.ID == 0 { return vulnerability, cerrors.ErrNotFound } // Query the FixedIn FeatureVersion now. rows, err := queryer.Query(searchVulnerabilityFixedIn, vulnerability.ID) if err != nil { return vulnerability, handleError("searchVulnerabilityFixedIn.Scan()", err) } defer rows.Close() for rows.Next() { var featureVersionID zero.Int var featureVersionVersion zero.String var featureVersionFeatureName zero.String err := rows.Scan( &featureVersionVersion, &featureVersionID, &featureVersionFeatureName, ) if err != nil { return vulnerability, handleError("searchVulnerabilityFixedIn.Scan()", err) } if !featureVersionID.IsZero() { // Note that the ID we fill in featureVersion is actually a Feature ID, and not // a FeatureVersion ID. featureVersion := database.FeatureVersion{ Model: database.Model{ID: int(featureVersionID.Int64)}, Feature: database.Feature{ Model: database.Model{ID: int(featureVersionID.Int64)}, Namespace: vulnerability.Namespace, Name: featureVersionFeatureName.String, }, Version: types.NewVersionUnsafe(featureVersionVersion.String), } vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion) } } if err := rows.Err(); err != nil { return vulnerability, handleError("searchVulnerabilityFixedIn.Rows()", err) } return vulnerability, nil }
"testing" "github.com/coreos/clair/database" "github.com/coreos/clair/utils/types" "github.com/coreos/clair/worker/detectors/feature" ) var rpmPackagesTests = []feature.FeatureVersionTest{ // Test a CentOS 7 RPM database // Memo: Use the following command on a RPM-based system to shrink a database: rpm -qa --qf "%{NAME}\n" |tail -n +3| xargs rpm -e --justdb { FeatureVersions: []database.FeatureVersion{ // Two packages from this source are installed, it should only appear once { Feature: database.Feature{Name: "centos-release"}, Version: types.NewVersionUnsafe("7-1.1503.el7.centos.2.8"), }, // Two packages from this source are installed, it should only appear once { Feature: database.Feature{Name: "filesystem"}, Version: types.NewVersionUnsafe("3.2-18.el7"), }, }, Data: map[string][]byte{ "var/lib/rpm/Packages": feature.LoadFileForTest("rpm/testdata/Packages"), }, }, } func TestRpmFeaturesDetector(t *testing.T) { feature.TestFeaturesDetector(t, &RpmFeaturesDetector{}, rpmPackagesTests)
func TestDebianParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) // Test parsing testdata/fetcher_debian_test.json testFile, _ := os.Open(filepath.Join(filepath.Dir(filename)) + "/testdata/fetcher_debian_test.json") response, err := buildResponse(testFile, "") if assert.Nil(t, err) && assert.Len(t, response.Vulnerabilities, 3) { 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, types.Low, vulnerability.Severity) assert.Equal(t, "This vulnerability is not very dangerous.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:8"}, Name: "aptdaemon", }, Version: types.MaxVersion, }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:unstable"}, Name: "aptdaemon", }, Version: types.NewVersionUnsafe("1.1.1+bzr982-1"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerability.FixedIn, expectedFeatureVersion) } } else if vulnerability.Name == "CVE-2003-0779" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2003-0779", vulnerability.Link) assert.Equal(t, types.High, vulnerability.Severity) assert.Equal(t, "But this one is very dangerous.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:8"}, Name: "aptdaemon", }, Version: types.NewVersionUnsafe("0.7.0"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:unstable"}, Name: "aptdaemon", }, Version: types.NewVersionUnsafe("0.7.0"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:8"}, Name: "asterisk", }, Version: types.NewVersionUnsafe("0.5.56"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerability.FixedIn, expectedFeatureVersion) } } else if vulnerability.Name == "CVE-2013-2685" { assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2013-2685", vulnerability.Link) assert.Equal(t, types.Negligible, vulnerability.Severity) assert.Equal(t, "Un-affected packages.", vulnerability.Description) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "debian:8"}, Name: "asterisk", }, Version: types.MinVersion, }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerability.FixedIn, expectedFeatureVersion) } } else { assert.Fail(t, "Wrong vulnerability name: ", vulnerability.ID) } } } }
func TestAPKFeatureDetection(t *testing.T) { testData := []feature.TestData{ { FeatureVersions: []database.FeatureVersion{ { Feature: database.Feature{Name: "musl"}, Version: types.NewVersionUnsafe("1.1.14-r10"), }, { Feature: database.Feature{Name: "busybox"}, Version: types.NewVersionUnsafe("1.24.2-r9"), }, { Feature: database.Feature{Name: "alpine-baselayout"}, Version: types.NewVersionUnsafe("3.0.3-r0"), }, { Feature: database.Feature{Name: "alpine-keys"}, Version: types.NewVersionUnsafe("1.1-r0"), }, { Feature: database.Feature{Name: "zlib"}, Version: types.NewVersionUnsafe("1.2.8-r2"), }, { Feature: database.Feature{Name: "libcrypto1.0"}, Version: types.NewVersionUnsafe("1.0.2h-r1"), }, { Feature: database.Feature{Name: "libssl1.0"}, Version: types.NewVersionUnsafe("1.0.2h-r1"), }, { Feature: database.Feature{Name: "apk-tools"}, Version: types.NewVersionUnsafe("2.6.7-r0"), }, { Feature: database.Feature{Name: "scanelf"}, Version: types.NewVersionUnsafe("1.1.6-r0"), }, { Feature: database.Feature{Name: "musl-utils"}, Version: types.NewVersionUnsafe("1.1.14-r10"), }, { Feature: database.Feature{Name: "libc-utils"}, Version: types.NewVersionUnsafe("0.7-r0"), }, }, Data: map[string][]byte{ "lib/apk/db/installed": feature.LoadFileForTest("apk/testdata/installed"), }, }, } feature.TestDetector(t, &detector{}, testData) }
func TestVulnerability(t *testing.T) { Open(&config.DatabaseConfig{Type: "memstore"}) defer Close() // Insert invalid vulnerabilities for _, vulnerability := range []Vulnerability{ Vulnerability{ID: "", Link: "link1", Priority: types.Medium, FixedInNodes: []string{"pkg1"}}, Vulnerability{ID: "test1", Link: "", Priority: types.Medium, FixedInNodes: []string{"pkg1"}}, Vulnerability{ID: "test1", Link: "link1", Priority: "InvalidPriority", FixedInNodes: []string{"pkg1"}}, Vulnerability{ID: "test1", Link: "link1", Priority: types.Medium, FixedInNodes: []string{}}, } { _, err := InsertVulnerabilities([]*Vulnerability{&vulnerability}) assert.Error(t, err) } // Some data vuln1 := &Vulnerability{ID: "test1", Link: "link1", Priority: types.Medium, Description: "testDescription1", FixedInNodes: []string{"pkg1"}} vuln2 := &Vulnerability{ID: "test2", Link: "link2", Priority: types.High, Description: "testDescription2", FixedInNodes: []string{"pkg1", "pkg2"}} vuln3 := &Vulnerability{ID: "test3", Link: "link3", Priority: types.High, FixedInNodes: []string{"pkg3"}} // Empty description // Insert some vulnerabilities _, err := InsertVulnerabilities([]*Vulnerability{vuln1, vuln2, vuln3}) if assert.Nil(t, err) { // Find one of the vulnerabilities we just inserted and verify its content v1, err := FindOneVulnerability(vuln1.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v1) { assert.Equal(t, vuln1.ID, v1.ID) assert.Equal(t, vuln1.Link, v1.Link) assert.Equal(t, vuln1.Priority, v1.Priority) assert.Equal(t, vuln1.Description, v1.Description) if assert.Len(t, v1.FixedInNodes, 1) { assert.Equal(t, vuln1.FixedInNodes[0], v1.FixedInNodes[0]) } } // Ensure that vulnerabilities with empty descriptions work as well v3, err := FindOneVulnerability(vuln3.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v3) { assert.Equal(t, vuln3.Description, v3.Description) } // Find vulnerabilities by fixed packages vulnsFixedInPkg1AndPkg3, err := FindAllVulnerabilitiesByFixedIn([]string{"pkg2", "pkg3"}, FieldVulnerabilityAll) assert.Nil(t, err) assert.Len(t, vulnsFixedInPkg1AndPkg3, 2) // Delete vulnerability if assert.Nil(t, DeleteVulnerability(vuln1.ID)) { v1, err := FindOneVulnerability(vuln1.ID, FieldVulnerabilityAll) assert.Equal(t, cerrors.ErrNotFound, err) assert.Nil(t, v1) } } // Update a vulnerability and verify its new content pkg1 := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")} InsertPackages([]*Package{pkg1}) vuln5 := &Vulnerability{ID: "test5", Link: "link5", Priority: types.Medium, Description: "testDescription5", FixedInNodes: []string{pkg1.Node}} _, err = InsertVulnerabilities([]*Vulnerability{vuln5}) if assert.Nil(t, err) { // Partial updates // # Just a field update vuln5b := &Vulnerability{ID: "test5", Priority: types.High} _, err := InsertVulnerabilities([]*Vulnerability{vuln5b}) if assert.Nil(t, err) { v5b, err := FindOneVulnerability(vuln5b.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v5b) { assert.Equal(t, vuln5b.ID, v5b.ID) assert.Equal(t, vuln5b.Priority, v5b.Priority) if assert.Len(t, v5b.FixedInNodes, 1) { assert.Contains(t, v5b.FixedInNodes, pkg1.Node) } } } // # Just a field update, twice in the same transaction vuln5b1 := &Vulnerability{ID: "test5", Link: "http://foo.bar"} vuln5b2 := &Vulnerability{ID: "test5", Link: "http://bar.foo"} _, err = InsertVulnerabilities([]*Vulnerability{vuln5b1, vuln5b2}) if assert.Nil(t, err) { v5b2, err := FindOneVulnerability(vuln5b2.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v5b2) { assert.Equal(t, vuln5b2.Link, v5b2.Link) } } // # All fields except fixedIn update vuln5c := &Vulnerability{ID: "test5", Link: "link5c", Priority: types.Critical, Description: "testDescription5c"} _, err = InsertVulnerabilities([]*Vulnerability{vuln5c}) if assert.Nil(t, err) { v5c, err := FindOneVulnerability(vuln5c.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v5c) { assert.Equal(t, vuln5c.ID, v5c.ID) assert.Equal(t, vuln5c.Link, v5c.Link) assert.Equal(t, vuln5c.Priority, v5c.Priority) assert.Equal(t, vuln5c.Description, v5c.Description) if assert.Len(t, v5c.FixedInNodes, 1) { assert.Contains(t, v5c.FixedInNodes, pkg1.Node) } } } // Complete update pkg2 := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.1")} pkg3 := &Package{OS: "testOS", Name: "testpkg2", Version: types.NewVersionUnsafe("1.0")} InsertPackages([]*Package{pkg2, pkg3}) vuln5d := &Vulnerability{ID: "test5", Link: "link5d", Priority: types.Low, Description: "testDescription5d", FixedInNodes: []string{pkg2.Node, pkg3.Node}} _, err = InsertVulnerabilities([]*Vulnerability{vuln5d}) if assert.Nil(t, err) { v5d, err := FindOneVulnerability(vuln5d.ID, FieldVulnerabilityAll) if assert.Nil(t, err) && assert.NotNil(t, v5d) { assert.Equal(t, vuln5d.ID, v5d.ID) assert.Equal(t, vuln5d.Link, v5d.Link) assert.Equal(t, vuln5d.Priority, v5d.Priority) assert.Equal(t, vuln5d.Description, v5d.Description) // Here, we ensure that a vulnerability can only be fixed by one package of a given branch at a given time // And that we can add new fixed packages as well if assert.Len(t, v5d.FixedInNodes, 2) { assert.NotContains(t, v5d.FixedInNodes, pkg1.Node) } } } } // Create and update a vulnerability's packages (and from the same branch) in the same batch pkg1 = &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")} pkg1b := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.1")} InsertPackages([]*Package{pkg1, pkg1b}) // # Two updates of the same vulnerability in the same batch with packages of the same branch pkg0 := &Package{OS: "testOS", Name: "testpkg0", Version: types.NewVersionUnsafe("1.0")} InsertPackages([]*Package{pkg0}) _, err = InsertVulnerabilities([]*Vulnerability{&Vulnerability{ID: "test7", Link: "link7", Priority: types.Medium, Description: "testDescription7", FixedInNodes: []string{pkg0.Node}}}) if assert.Nil(t, err) { vuln7b := &Vulnerability{ID: "test7", FixedInNodes: []string{pkg1.Node}} vuln7c := &Vulnerability{ID: "test7", FixedInNodes: []string{pkg1b.Node}} _, err = InsertVulnerabilities([]*Vulnerability{vuln7b, vuln7c}) if assert.Nil(t, err) { v7, err := FindOneVulnerability("test7", FieldVulnerabilityAll) if assert.Nil(t, err) && assert.Len(t, v7.FixedInNodes, 2) { assert.Contains(t, v7.FixedInNodes, pkg0.Node) assert.NotContains(t, v7.FixedInNodes, pkg1.Node) assert.Contains(t, v7.FixedInNodes, pkg1b.Node) } } } }
func TestOracleParser(t *testing.T) { _, filename, _, _ := runtime.Caller(0) path := filepath.Join(filepath.Dir(filename)) // Test parsing testdata/fetcher_oracle_test.1.xml testFile, _ := os.Open(path + "/testdata/fetcher_oracle_test.1.xml") defer testFile.Close() vulnerabilities, err := parseELSA(testFile) 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, types.Medium, vulnerabilities[0].Severity) assert.Equal(t, ` [3.1.1-7] Resolves: rhbz#1217104 CVE-2015-0252 `, vulnerabilities[0].Description) expectedFeatureVersions := []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "oracle:7"}, Name: "xerces-c", }, Version: types.NewVersionUnsafe("3.1.1-7.el7_1"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "oracle:7"}, Name: "xerces-c-devel", }, Version: types.NewVersionUnsafe("3.1.1-7.el7_1"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "oracle:7"}, Name: "xerces-c-doc", }, Version: types.NewVersionUnsafe("3.1.1-7.el7_1"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerabilities[0].FixedIn, expectedFeatureVersion) } } testFile2, _ := os.Open(path + "/testdata/fetcher_oracle_test.2.xml") defer testFile2.Close() vulnerabilities, err = parseELSA(testFile2) 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, types.Critical, 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{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "oracle:6"}, Name: "firefox", }, Version: types.NewVersionUnsafe("38.1.0-1.0.1.el6_6"), }, { Feature: database.Feature{ Namespace: database.Namespace{Name: "oracle:7"}, Name: "firefox", }, Version: types.NewVersionUnsafe("38.1.0-1.0.1.el7_1"), }, } for _, expectedFeatureVersion := range expectedFeatureVersions { assert.Contains(t, vulnerabilities[0].FixedIn, expectedFeatureVersion) } } }
func TestProcessWithDistUpgrade(t *testing.T) { _, f, _, _ := runtime.Caller(0) testDataPath := filepath.Join(filepath.Dir(f)) + "/testdata/DistUpgrade/" // Create a mock datastore. datastore := newMockDatastore() datastore.FctInsertLayer = func(layer database.Layer) error { datastore.layers[layer.Name] = layer return nil } datastore.FctFindLayer = func(name string, withFeatures, withVulnerabilities bool) (database.Layer, error) { if layer, exists := datastore.layers[name]; exists { return layer, nil } return database.Layer{}, cerrors.ErrNotFound } // Create the list of FeatureVersions that should not been upgraded from one layer to another. nonUpgradedFeatureVersions := []database.FeatureVersion{ {Feature: database.Feature{Name: "libtext-wrapi18n-perl"}, Version: types.NewVersionUnsafe("0.06-7")}, {Feature: database.Feature{Name: "libtext-charwidth-perl"}, Version: types.NewVersionUnsafe("0.04-7")}, {Feature: database.Feature{Name: "libtext-iconv-perl"}, Version: types.NewVersionUnsafe("1.7-5")}, {Feature: database.Feature{Name: "mawk"}, Version: types.NewVersionUnsafe("1.3.3-17")}, {Feature: database.Feature{Name: "insserv"}, Version: types.NewVersionUnsafe("1.14.0-5")}, {Feature: database.Feature{Name: "db"}, Version: types.NewVersionUnsafe("5.1.29-5")}, {Feature: database.Feature{Name: "ustr"}, Version: types.NewVersionUnsafe("1.0.4-3")}, {Feature: database.Feature{Name: "xz-utils"}, Version: types.NewVersionUnsafe("5.1.1alpha+20120614-2")}, } // Process test layers. // // blank.tar: MAINTAINER Quentin MACHU <quentin.machu.fr> // wheezy.tar: FROM debian:wheezy // jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update && // apt-get -y dist-upgrade assert.Nil(t, Process(datastore, "Docker", "blank", "", testDataPath+"blank.tar.gz", nil)) assert.Nil(t, Process(datastore, "Docker", "wheezy", "blank", testDataPath+"wheezy.tar.gz", nil)) assert.Nil(t, Process(datastore, "Docker", "jessie", "wheezy", testDataPath+"jessie.tar.gz", nil)) // Ensure that the 'wheezy' layer has the expected namespace and features. wheezy, ok := datastore.layers["wheezy"] if assert.True(t, ok, "layer 'wheezy' not processed") { assert.Equal(t, "debian:7", wheezy.Namespace.Name) assert.Len(t, wheezy.Features, 52) for _, nufv := range nonUpgradedFeatureVersions { nufv.Feature.Namespace.Name = "debian:7" assert.Contains(t, wheezy.Features, nufv) } } // Ensure that the 'wheezy' layer has the expected namespace and non-upgraded features. jessie, ok := datastore.layers["jessie"] if assert.True(t, ok, "layer 'jessie' not processed") { assert.Equal(t, "debian:8", jessie.Namespace.Name) assert.Len(t, jessie.Features, 74) for _, nufv := range nonUpgradedFeatureVersions { nufv.Feature.Namespace.Name = "debian:7" assert.Contains(t, jessie.Features, nufv) } for _, nufv := range nonUpgradedFeatureVersions { nufv.Feature.Namespace.Name = "debian:8" assert.NotContains(t, jessie.Features, nufv) } } }
func testInsertLayerTree(t *testing.T, datastore database.Datastore) { f1 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace2"}, Name: "TestInsertLayerFeature1", }, Version: types.NewVersionUnsafe("1.0"), } f2 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace2"}, Name: "TestInsertLayerFeature2", }, Version: types.NewVersionUnsafe("0.34"), } f3 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace2"}, Name: "TestInsertLayerFeature3", }, Version: types.NewVersionUnsafe("0.56"), } f4 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace3"}, Name: "TestInsertLayerFeature2", }, Version: types.NewVersionUnsafe("0.34"), } f5 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace3"}, Name: "TestInsertLayerFeature3", }, Version: types.NewVersionUnsafe("0.56"), } f6 := database.FeatureVersion{ Feature: database.Feature{ Namespace: database.Namespace{Name: "TestInsertLayerNamespace3"}, Name: "TestInsertLayerFeature4", }, Version: types.NewVersionUnsafe("0.666"), } layers := []database.Layer{ { Name: "TestInsertLayer1", }, { Name: "TestInsertLayer2", Parent: &database.Layer{Name: "TestInsertLayer1"}, Namespace: &database.Namespace{Name: "TestInsertLayerNamespace1"}, }, // This layer changes the namespace and adds Features. { Name: "TestInsertLayer3", Parent: &database.Layer{Name: "TestInsertLayer2"}, Namespace: &database.Namespace{Name: "TestInsertLayerNamespace2"}, Features: []database.FeatureVersion{f1, f2, f3}, }, // This layer covers the case where the last layer doesn't provide any new Feature. { Name: "TestInsertLayer4a", Parent: &database.Layer{Name: "TestInsertLayer3"}, Features: []database.FeatureVersion{f1, f2, f3}, }, // This layer covers the case where the last layer provides Features. // It also modifies the Namespace ("upgrade") but keeps some Features not upgraded, their // Namespaces should then remain unchanged. { Name: "TestInsertLayer4b", Parent: &database.Layer{Name: "TestInsertLayer3"}, Namespace: &database.Namespace{Name: "TestInsertLayerNamespace3"}, Features: []database.FeatureVersion{ // Deletes TestInsertLayerFeature1. // Keep TestInsertLayerFeature2 (old Namespace should be kept): f4, // Upgrades TestInsertLayerFeature3 (with new Namespace): f5, // Adds TestInsertLayerFeature4: f6, }, }, } var err error retrievedLayers := make(map[string]database.Layer) for _, layer := range layers { if layer.Parent != nil { // Retrieve from database its parent and assign. parent := retrievedLayers[layer.Parent.Name] layer.Parent = &parent } err = datastore.InsertLayer(layer) assert.Nil(t, err) retrievedLayers[layer.Name], err = datastore.FindLayer(layer.Name, true, false) assert.Nil(t, err) } l4a := retrievedLayers["TestInsertLayer4a"] if assert.NotNil(t, l4a.Namespace) { assert.Equal(t, "TestInsertLayerNamespace2", l4a.Namespace.Name) } assert.Len(t, l4a.Features, 3) for _, featureVersion := range l4a.Features { if cmpFV(featureVersion, f1) && cmpFV(featureVersion, f2) && cmpFV(featureVersion, f3) { assert.Error(t, fmt.Errorf("TestInsertLayer4a contains an unexpected package: %#v. Should contain %#v and %#v and %#v.", featureVersion, f1, f2, f3)) } } l4b := retrievedLayers["TestInsertLayer4b"] if assert.NotNil(t, l4b.Namespace) { assert.Equal(t, "TestInsertLayerNamespace3", l4b.Namespace.Name) } assert.Len(t, l4b.Features, 3) for _, featureVersion := range l4b.Features { if cmpFV(featureVersion, f2) && cmpFV(featureVersion, f5) && cmpFV(featureVersion, f6) { assert.Error(t, fmt.Errorf("TestInsertLayer4a contains an unexpected package: %#v. Should contain %#v and %#v and %#v.", featureVersion, f2, f4, f6)) } } }
package packages import ( "testing" "github.com/coreos/clair/database" "github.com/coreos/clair/utils/types" ) var dpkgPackagesTests = []packagesTest{ // Test an Ubuntu dpkg status file packagesTest{ packages: []*database.Package{ &database.Package{ Name: "pam", // Two packages from this source are installed, it should only appear one time Version: types.NewVersionUnsafe("1.1.8-3.1ubuntu3"), }, &database.Package{ Name: "makedev", // The source name and the package name are equals Version: types.NewVersionUnsafe("2.3.1-93ubuntu1"), // The version comes from the "Version:" line }, &database.Package{ Name: "gcc-5", Version: types.NewVersionUnsafe("5.1.1-12ubuntu1"), // The version comes from the "Source:" line }, }, data: map[string][]byte{ "var/lib/dpkg/status": loadFileForTest("testdata/dpkg_status"), }, }, }
func TestInsertVulnerability(t *testing.T) { datastore, err := openDatabaseForTest("InsertVulnerability", false) if err != nil { t.Error(err) return } defer datastore.Close() // Create some data. n1 := database.Namespace{Name: "TestInsertVulnerabilityNamespace1"} n2 := database.Namespace{Name: "TestInsertVulnerabilityNamespace2"} f1 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion1", Namespace: n1, }, Version: types.NewVersionUnsafe("1.0"), } f2 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion1", Namespace: n2, }, Version: types.NewVersionUnsafe("1.0"), } f3 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion2", }, Version: types.MaxVersion, } f4 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion2", }, Version: types.NewVersionUnsafe("1.4"), } f5 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion3", }, Version: types.NewVersionUnsafe("1.5"), } f6 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion4", }, Version: types.NewVersionUnsafe("0.1"), } f7 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion5", }, Version: types.MaxVersion, } f8 := database.FeatureVersion{ Feature: database.Feature{ Name: "TestInsertVulnerabilityFeatureVersion5", }, Version: types.MinVersion, } // Insert invalid vulnerabilities. for _, vulnerability := range []database.Vulnerability{ { Name: "", Namespace: n1, FixedIn: []database.FeatureVersion{f1}, Severity: types.Unknown, }, { Name: "TestInsertVulnerability0", Namespace: database.Namespace{}, FixedIn: []database.FeatureVersion{f1}, Severity: types.Unknown, }, { Name: "TestInsertVulnerability0-", Namespace: database.Namespace{}, FixedIn: []database.FeatureVersion{f1}, }, { Name: "TestInsertVulnerability0", Namespace: n1, FixedIn: []database.FeatureVersion{f1}, Severity: types.Priority(""), }, { Name: "TestInsertVulnerability0", Namespace: n1, FixedIn: []database.FeatureVersion{f2}, Severity: types.Unknown, }, } { err := datastore.InsertVulnerabilities([]database.Vulnerability{vulnerability}, true) assert.Error(t, err) } // Insert a simple vulnerability and find it. v1meta := make(map[string]interface{}) v1meta["TestInsertVulnerabilityMetadata1"] = "TestInsertVulnerabilityMetadataValue1" v1meta["TestInsertVulnerabilityMetadata2"] = struct { Test string }{ Test: "TestInsertVulnerabilityMetadataValue1", } v1 := database.Vulnerability{ Name: "TestInsertVulnerability1", Namespace: n1, FixedIn: []database.FeatureVersion{f1, f3, f6, f7}, Severity: types.Low, Description: "TestInsertVulnerabilityDescription1", Link: "TestInsertVulnerabilityLink1", Metadata: v1meta, } err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true) if assert.Nil(t, err) { v1f, err := datastore.FindVulnerability(n1.Name, v1.Name) if assert.Nil(t, err) { equalsVuln(t, &v1, &v1f) } } // Update vulnerability. v1.Description = "TestInsertVulnerabilityLink2" v1.Link = "TestInsertVulnerabilityLink2" v1.Severity = types.High // 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. v1.FixedIn = []database.FeatureVersion{f4, f5, f6, f8} err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true) if assert.Nil(t, err) { v1f, err := datastore.FindVulnerability(n1.Name, v1.Name) if assert.Nil(t, err) { // We already had f1 before the update. // Add it to the struct for comparison. v1.FixedIn = append(v1.FixedIn, f1) // Removes f8 from the struct for comparison as it was just here to cancel f7. for i := 0; i < len(v1.FixedIn); i++ { if v1.FixedIn[i].Feature.Name == f8.Feature.Name { v1.FixedIn = append(v1.FixedIn[:i], v1.FixedIn[i+1:]...) } } equalsVuln(t, &v1, &v1f) } } }
func TestFindLayer(t *testing.T) { datastore, err := openDatabaseForTest("FindLayer", true) if err != nil { t.Error(err) return } defer datastore.Close() // Layer-0: no parent, no namespace, no feature, no vulnerability layer, err := datastore.FindLayer("layer-0", false, false) if assert.Nil(t, err) && assert.NotNil(t, layer) { assert.Equal(t, "layer-0", layer.Name) assert.Nil(t, layer.Namespace) assert.Nil(t, layer.Parent) assert.Equal(t, 1, layer.EngineVersion) assert.Len(t, layer.Features, 0) } layer, err = datastore.FindLayer("layer-0", true, false) if assert.Nil(t, err) && assert.NotNil(t, layer) { assert.Len(t, layer.Features, 0) } // Layer-1: one parent, adds two features, one vulnerability layer, err = datastore.FindLayer("layer-1", false, false) if assert.Nil(t, err) && assert.NotNil(t, layer) { assert.Equal(t, layer.Name, "layer-1") assert.Equal(t, "debian:7", layer.Namespace.Name) if assert.NotNil(t, layer.Parent) { assert.Equal(t, "layer-0", layer.Parent.Name) } assert.Equal(t, 1, layer.EngineVersion) assert.Len(t, layer.Features, 0) } layer, err = datastore.FindLayer("layer-1", true, false) if assert.Nil(t, err) && assert.NotNil(t, layer) && assert.Len(t, layer.Features, 2) { for _, featureVersion := range layer.Features { assert.Equal(t, "debian:7", featureVersion.Feature.Namespace.Name) switch featureVersion.Feature.Name { case "wechat": assert.Equal(t, types.NewVersionUnsafe("0.5"), featureVersion.Version) case "openssl": assert.Equal(t, types.NewVersionUnsafe("1.0"), featureVersion.Version) default: t.Errorf("unexpected package %s for layer-1", featureVersion.Feature.Name) } } } layer, err = datastore.FindLayer("layer-1", true, true) if assert.Nil(t, err) && assert.NotNil(t, layer) && assert.Len(t, layer.Features, 2) { for _, featureVersion := range layer.Features { assert.Equal(t, "debian:7", featureVersion.Feature.Namespace.Name) switch featureVersion.Feature.Name { case "wechat": assert.Equal(t, types.NewVersionUnsafe("0.5"), featureVersion.Version) case "openssl": assert.Equal(t, types.NewVersionUnsafe("1.0"), featureVersion.Version) 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, types.High, 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, types.NewVersionUnsafe("2.0"), featureVersion.AffectedBy[0].FixedBy) } default: t.Errorf("unexpected package %s for layer-1", featureVersion.Feature.Name) } } } }
func TestRaceAffects(t *testing.T) { datastore, err := openDatabaseForTest("RaceAffects", false) if err != nil { t.Error(err) return } defer datastore.Close() // Insert the Feature on which we'll work. feature := database.Feature{ Namespace: database.Namespace{Name: "TestRaceAffectsFeatureNamespace1"}, Name: "TestRaceAffecturesFeature1", } _, err = datastore.insertFeature(feature) if err != nil { t.Error(err) return } // Initialize random generator and enforce max procs. rand.Seed(time.Now().UnixNano()) runtime.GOMAXPROCS(runtime.NumCPU()) // Generate FeatureVersions. featureVersions := make([]database.FeatureVersion, numFeatureVersions) for i := 0; i < numFeatureVersions; i++ { version := rand.Intn(numFeatureVersions) featureVersions[i] = database.FeatureVersion{ Feature: feature, Version: types.NewVersionUnsafe(strconv.Itoa(version)), } } // Generate vulnerabilities. // They are mapped by fixed version, which will make verification really easy afterwards. vulnerabilities := make(map[int][]database.Vulnerability) for i := 0; i < numVulnerabilities; i++ { version := rand.Intn(numFeatureVersions) + 1 // if _, ok := vulnerabilities[version]; !ok { // vulnerabilities[version] = make([]database.Vulnerability) // } vulnerability := database.Vulnerability{ Name: uuid.New(), Namespace: feature.Namespace, FixedIn: []database.FeatureVersion{ { Feature: feature, Version: types.NewVersionUnsafe(strconv.Itoa(version)), }, }, Severity: types.Unknown, } vulnerabilities[version] = append(vulnerabilities[version], vulnerability) } // Insert featureversions and vulnerabilities in parallel. var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for _, vulnerabilitiesM := range vulnerabilities { for _, vulnerability := range vulnerabilitiesM { err = datastore.InsertVulnerabilities([]database.Vulnerability{vulnerability}, true) assert.Nil(t, err) } } fmt.Println("finished to insert vulnerabilities") }() go func() { defer wg.Done() for i := 0; i < len(featureVersions); i++ { featureVersions[i].ID, err = datastore.insertFeatureVersion(featureVersions[i]) assert.Nil(t, err) } fmt.Println("finished to insert featureVersions") }() wg.Wait() // Verify consistency now. var actualAffectedNames []string var expectedAffectedNames []string for _, featureVersion := range featureVersions { featureVersionVersion, _ := strconv.Atoi(featureVersion.Version.String()) // Get actual affects. rows, err := datastore.Query(searchComplexTestFeatureVersionAffects, featureVersion.ID) assert.Nil(t, err) defer rows.Close() var vulnName string for rows.Next() { err = rows.Scan(&vulnName) if !assert.Nil(t, err) { continue } actualAffectedNames = append(actualAffectedNames, vulnName) } if assert.Nil(t, rows.Err()) { rows.Close() } // Get expected affects. for i := numVulnerabilities; i > featureVersionVersion; i-- { for _, vulnerability := range vulnerabilities[i] { expectedAffectedNames = append(expectedAffectedNames, vulnerability.Name) } } assert.Len(t, utils.CompareStringLists(expectedAffectedNames, actualAffectedNames), 0) assert.Len(t, utils.CompareStringLists(actualAffectedNames, expectedAffectedNames), 0) } }