database: allow removing fixed packages in vulnerabilities

This commit is contained in:
Quentin Machu 2016-01-13 15:30:58 -05:00 committed by Jimmy Zelinskie
parent 7c70fc1c20
commit 1b53142e38
4 changed files with 104 additions and 171 deletions

View File

@ -167,6 +167,11 @@ func init() {
UPDATE Vulnerability_FixedIn_Feature
SET version = $3
WHERE vulnerability_id = $1 AND feature_id = $2
RETURNING id`
queries["r_vulnerability_fixedin_feature"] = `
DELETE FROM Vulnerability_FixedIn_Feature
WHERE vulnerability_id = $1 AND feature_id = $2
RETURNING id`
queries["r_vulnerability_affects_featureversion"] = `

View File

@ -1,32 +1,44 @@
INSERT INTO namespace (id, name) VALUES (1, 'debian:7');
INSERT INTO namespace (id, name) VALUES (2, 'debian:8');
INSERT INTO namespace (id, name) VALUES
(1, 'debian:7'),
(2, 'debian:8');
INSERT INTO feature (id, namespace_id, name) VALUES (1, 1, 'wechat');
INSERT INTO feature (id, namespace_id, name) VALUES (2, 1, 'openssl');
INSERT INTO feature (id, namespace_id, name) VALUES (4, 1, 'libssl');
INSERT INTO feature (id, namespace_id, name) VALUES (3, 2, 'openssl');
INSERT INTO featureversion (id, feature_id, version) VALUES (1, 1, '0.5');
INSERT INTO featureversion (id, feature_id, version) VALUES (2, 2, '1.0');
INSERT INTO featureversion (id, feature_id, version) VALUES (3, 2, '2.0');
INSERT INTO featureversion (id, feature_id, version) VALUES (4, 3, '1.0');
INSERT INTO feature (id, namespace_id, name) VALUES
(1, 1, 'wechat'),
(2, 1, 'openssl'),
(4, 1, 'libssl'),
(3, 2, 'openssl');
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES (1, 'layer-0', 1, NULL, NULL);
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES (2, 'layer-1', 1, 1, 1);
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES (3, 'layer-2', 1, 2, 1);
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES (4, 'layer-3a', 1, 3, 1);
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES (5, 'layer-3b', 1, 3, 2);
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (1, 2, 1, 'add');
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (2, 2, 2, 'add');
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (3, 3, 2, 'del'); -- layer-2: Update Debian:7 OpenSSL 1.0 -> 2.0
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (4, 3, 3, 'add'); -- ^
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (5, 5, 3, 'del'); -- layer-3b: Delete Debian:7 OpenSSL 2.0
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES (6, 5, 4, 'add'); -- layer-3b: Add Debian:8 OpenSSL 1.0
INSERT INTO featureversion (id, feature_id, version) VALUES
(1, 1, '0.5'),
(2, 2, '1.0'),
(3, 2, '2.0'),
(4, 3, '1.0');
INSERT INTO vulnerability (id, namespace_id, name, description, link, severity) VALUES (1, 1, 'CVE-OPENSSL-1-DEB7', 'A vulnerability affecting OpenSSL < 2.0 on Debian 7.0', 'http://google.com/#q=CVE-OPENSSL-1-DEB7', 'High');
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES (1, 1, 2, '2.0');
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES (2, 1, 4, '1.9-abc');
INSERT INTO vulnerability_affects_featureversion (id, vulnerability_id, featureversion_id, fixedin_id) VALUES (1, 1, 2, 1); -- CVE-OPENSSL-1-DEB7 affects Debian:7 OpenSSL 1.0
INSERT INTO vulnerability (id, namespace_id, name, description, link, severity) VALUES (2, 1, 'CVE-NOPE', 'A vulnerability affecting nothing', '', 'Unknown');
INSERT INTO layer (id, name, engineversion, parent_id, namespace_id) VALUES
(1, 'layer-0', 1, NULL, NULL),
(2, 'layer-1', 1, 1, 1),
(3, 'layer-2', 1, 2, 1),
(4, 'layer-3a', 1, 3, 1),
(5, 'layer-3b', 1, 3, 2);
INSERT INTO layer_diff_featureversion (id, layer_id, featureversion_id, modification) VALUES
(1, 2, 1, 'add'),
(2, 2, 2, 'add'),
(3, 3, 2, 'del'), -- layer-2: Update Debian:7 OpenSSL 1.0 -> 2.0
(4, 3, 3, 'add'), -- ^
(5, 5, 3, 'del'), -- layer-3b: Delete Debian:7 OpenSSL 2.0
(6, 5, 4, 'add'); -- layer-3b: Add Debian:8 OpenSSL 1.0
INSERT INTO vulnerability (id, namespace_id, name, description, link, severity) VALUES
(1, 1, 'CVE-OPENSSL-1-DEB7', 'A vulnerability affecting OpenSSL < 2.0 on Debian 7.0', 'http://google.com/#q=CVE-OPENSSL-1-DEB7', 'High'),
(2, 1, 'CVE-NOPE', 'A vulnerability affecting nothing', '', 'Unknown');
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES
(1, 1, 2, '2.0'),
(2, 1, 4, '1.9-abc');
INSERT INTO vulnerability_affects_featureversion (id, vulnerability_id, featureversion_id, fixedin_id) VALUES
(1, 1, 2, 1); -- CVE-OPENSSL-1-DEB7 affects Debian:7 OpenSSL 1.0
SELECT pg_catalog.setval(pg_get_serial_sequence('namespace', 'id'), (SELECT MAX(id) FROM namespace)+1);
SELECT pg_catalog.setval(pg_get_serial_sequence('feature', 'id'), (SELECT MAX(id) FROM feature)+1);

View File

@ -64,6 +64,7 @@ func (pgSQL *pgSQL) FindVulnerability(namespaceName, name string) (database.Vuln
}
// FixedIn.Namespace are not necessary, they are overwritten by the vuln.
// By setting the fixed version to minVersion, we can say that the vuln does'nt affect anymore.
func (pgSQL *pgSQL) InsertVulnerabilities(vulnerabilities []database.Vulnerability) error {
for _, vulnerability := range vulnerabilities {
err := pgSQL.insertVulnerability(vulnerability)
@ -224,10 +225,6 @@ func createFeatureVersionNameMap(features []database.FeatureVersion) (map[string
return m, s
}
// TODO(Quentin-M): Add support for removing Vulnerability_FixedIn_Feature when Version = MinVersion.
// We should then update the vulnerability fetcher to do it.
// Also maybe we would delete a Vulnerability if it hasn't any FixedIn.
// --> And affects
func (pgSQL *pgSQL) updateVulnerabilityFeatureVersions(tx *sql.Tx, vulnerability, existingVulnerability *database.Vulnerability, newFixedInFeatureVersions, updatedFixedInFeatureVersions []database.FeatureVersion) error {
var fixedInID int
@ -247,6 +244,7 @@ func (pgSQL *pgSQL) updateVulnerabilityFeatureVersions(tx *sql.Tx, vulnerability
}
for _, fv := range updatedFixedInFeatureVersions {
if fv.Version != types.MinVersion {
// Update Vulnerability_FixedIn_Feature.
err := tx.QueryRow(getQuery("u_vulnerability_fixedin_feature"), vulnerability.ID, fv.ID,
&fv.Version).Scan(&fixedInID)
@ -265,6 +263,21 @@ func (pgSQL *pgSQL) updateVulnerabilityFeatureVersions(tx *sql.Tx, vulnerability
if err != nil {
return err
}
} else {
// Updating FixedIn by saying that the fixed version is the lowest possible version, it
// basically means that the vulnerability doesn't affect the feature (anymore).
// Drop it from Vulnerability_FixedIn_Feature and Vulnerability_Affects_FeatureVersion.
err := tx.QueryRow(getQuery("r_vulnerability_fixedin_feature"), vulnerability.ID, fv.ID).
Scan(&fixedInID)
if err != nil {
return handleError("r_vulnerability_fixedin_feature", err)
}
_, err = tx.Exec(getQuery("r_vulnerability_affects_featureversion"), fixedInID)
if err != nil {
return handleError("r_vulnerability_affects_featureversion", err)
}
}
}
return nil

View File

@ -27,6 +27,7 @@ func TestFindVulnerability(t *testing.T) {
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{
database.FeatureVersion{
Feature: database.Feature{Name: "openssl"},
@ -46,8 +47,10 @@ func TestFindVulnerability(t *testing.T) {
// Find a vulnerability that has no link, no severity and no FixedIn.
v2 := database.Vulnerability{
Name: "CVE-OPENSSL-1-DEB7",
Description: "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0",
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")
@ -106,6 +109,18 @@ func TestInsertVulnerability(t *testing.T) {
},
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{
@ -147,7 +162,7 @@ func TestInsertVulnerability(t *testing.T) {
v1 := database.Vulnerability{
Name: "TestInsertVulnerability1",
Namespace: n1,
FixedIn: []database.FeatureVersion{f1, f3, f6},
FixedIn: []database.FeatureVersion{f1, f3, f6, f7},
Severity: types.Low,
Description: "TestInsertVulnerabilityDescription1",
Link: "TestInsertVulnerabilityLink1",
@ -164,9 +179,9 @@ func TestInsertVulnerability(t *testing.T) {
v1.Description = "TestInsertVulnerabilityLink2"
v1.Link = "TestInsertVulnerabilityLink2"
v1.Severity = types.High
// Update f3 by f4, add fixed by f5, add fixed by f6 which already exists.
// TODO(Quentin-M): Remove FixedIn.
v1.FixedIn = []database.FeatureVersion{f4, f5, f6}
// 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})
if assert.Nil(t, err) {
@ -175,6 +190,14 @@ func TestInsertVulnerability(t *testing.T) {
// 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)
}
}
@ -206,126 +229,6 @@ func equalsVuln(t *testing.T, expected, actual *database.Vulnerability) {
// TODO Test Affects in Feature_Version and here.
//
// // Some data
// vuln1 := &database.Vulnerability{ID: "test1", Link: "link1", Priority: types.Medium, Description: "testDescription1", FixedInNodes: []string{"pkg1"}}
// vuln2 := &database.Vulnerability{ID: "test2", Link: "link2", Priority: types.High, Description: "testDescription2", FixedInNodes: []string{"pkg1", "pkg2"}}
// vuln3 := &database.Vulnerability{ID: "test3", Link: "link3", Priority: types.High, FixedInNodes: []string{"pkg3"}} // Empty description
//
// // Insert some vulnerabilities
// _, err := InsertVulnerabilities([]*database.Vulnerability{vuln1, vuln2, vuln3})
// if assert.Nil(t, err) {
// // Find one of the vulnerabilities we just inserted and verify its content
// v1, err := FindOnedatabase.Vulnerability(vuln1.ID, Fielddatabase.VulnerabilityAll)
// 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])
// }
// }
// }
//
// // Update a database.Vulnerability and verify its new content
// pkg1 := &Package{OS: "testOS", Name: "testpkg1", Version: types.NewVersionUnsafe("1.0")}
// InsertPackages([]*Package{pkg1})
// vuln5 := &database.Vulnerability{ID: "test5", Link: "link5", Priority: types.Medium, Description: "testDescription5", FixedInNodes: []string{pkg1.Node}}
//
// _, err = InsertVulnerabilities([]*database.Vulnerability{vuln5})
// if assert.Nil(t, err) {
// // Partial updates
// // # Just a field update
// vuln5b := &database.Vulnerability{ID: "test5", Priority: types.High}
// _, err := InsertVulnerabilities([]*database.Vulnerability{vuln5b})
// if assert.Nil(t, err) {
// v5b, err := FindOnedatabase.Vulnerability(vuln5b.ID, Fielddatabase.VulnerabilityAll)
// 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 := &database.Vulnerability{ID: "test5", Link: "http://foo.bar"}
// vuln5b2 := &database.Vulnerability{ID: "test5", Link: "http://bar.foo"}
// _, err = InsertVulnerabilities([]*database.Vulnerability{vuln5b1, vuln5b2})
// if assert.Nil(t, err) {
// v5b2, err := FindOnedatabase.Vulnerability(vuln5b2.ID, Fielddatabase.VulnerabilityAll)
// if assert.Nil(t, err) && assert.NotNil(t, v5b2) {
// assert.Equal(t, vuln5b2.Link, v5b2.Link)
// }
// }
//
// // # All fields except fixedIn update
// vuln5c := &database.Vulnerability{ID: "test5", Link: "link5c", Priority: types.Critical, Description: "testDescription5c"}
// _, err = InsertVulnerabilities([]*database.Vulnerability{vuln5c})
// if assert.Nil(t, err) {
// v5c, err := FindOnedatabase.Vulnerability(vuln5c.ID, Fielddatabase.VulnerabilityAll)
// 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 := &database.Vulnerability{ID: "test5", Link: "link5d", Priority: types.Low, Description: "testDescription5d", FixedInNodes: []string{pkg2.Node, pkg3.Node}}
//
// _, err = InsertVulnerabilities([]*database.Vulnerability{vuln5d})
// if assert.Nil(t, err) {
// v5d, err := FindOnedatabase.Vulnerability(vuln5d.ID, Fielddatabase.VulnerabilityAll)
// 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 database.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 database.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 database.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([]*database.Vulnerability{&database.Vulnerability{ID: "test7", Link: "link7", Priority: types.Medium, Description: "testDescription7", FixedInNodes: []string{pkg0.Node}}})
// if assert.Nil(t, err) {
// vuln7b := &database.Vulnerability{ID: "test7", FixedInNodes: []string{pkg1.Node}}
// vuln7c := &database.Vulnerability{ID: "test7", FixedInNodes: []string{pkg1b.Node}}
// _, err = InsertVulnerabilities([]*database.Vulnerability{vuln7b, vuln7c})
// if assert.Nil(t, err) {
// v7, err := FindOnedatabase.Vulnerability("test7", Fielddatabase.VulnerabilityAll)
// 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 TestInsertVulnerabilityNotifications(t *testing.T) {
// Open(&config.DatabaseConfig{Type: "memstore"})
// defer Close()