func parseRHSA(ovalReader io.Reader) (vulnerabilities []database.Vulnerability, err error) { // Decode the XML. var ov oval err = xml.NewDecoder(ovalReader).Decode(&ov) if err != nil { log.Errorf("could not decode RHEL's XML: %s", err) err = cerrors.ErrCouldNotParse return } // Iterate over the definitions and collect any vulnerabilities that affect // at least one package. for _, definition := range ov.Definitions { pkgs := toFeatureVersions(definition.Criteria) if len(pkgs) > 0 { vulnerability := database.Vulnerability{ Name: name(definition), Link: link(definition), Severity: priority(definition), Description: description(definition), } for _, p := range pkgs { vulnerability.FixedIn = append(vulnerability.FixedIn, p) } vulnerabilities = append(vulnerabilities, vulnerability) } } return }
// Parse an Oval file. func (f *OvalFetcher) ParseOval(ovalReader io.Reader) (vulnerabilities []database.Vulnerability, err error) { var ov oval err = xml.NewDecoder(ovalReader).Decode(&ov) if err != nil { log.Errorf("could not decode %s's XML: %s", f.OsInfo.DistName(), err) return vulnerabilities, cerrors.ErrCouldNotParse } for _, definition := range ov.Definitions { pkgs := f.ToFeatureVersions(f.Possibilities(definition.Criteria)) if len(pkgs) > 0 { vulnerability := database.Vulnerability{ Name: name(definition), Link: link(definition, f.OsInfo.SecToken()), Severity: priority(definition), Description: description(definition), } vulnerability.FixedIn = append(vulnerability.FixedIn, pkgs...) vulnerabilities = append(vulnerabilities, vulnerability) } } return }
func parse34YAML(r io.Reader) (vulns []database.Vulnerability, err error) { var rBytes []byte rBytes, err = ioutil.ReadAll(r) if err != nil { return } var file secdb34File err = yaml.Unmarshal(rBytes, &file) if err != nil { return } for _, pack := range file.Packages { pkg := pack.Pkg for versionStr, vulnStrs := range pkg.Fixes { version, err := types.NewVersion(versionStr) if err != nil { log.Warningf("could not parse package version '%s': %s. skipping", versionStr, err.Error()) continue } for _, vulnStr := range vulnStrs { var vuln database.Vulnerability vuln.Severity = types.Unknown vuln.Name = vulnStr vuln.Link = nvdURLPrefix + vulnStr vuln.FixedIn = []database.FeatureVersion{ { Feature: database.Feature{ Namespace: database.Namespace{Name: "alpine:" + file.Distro}, Name: pkg.Name, }, Version: version, }, } vulns = append(vulns, vuln) } } } return }
func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, onlyFixedIn, generateNotification bool) error { tf := time.Now() // Verify parameters if vulnerability.Name == "" || vulnerability.Namespace.Name == "" { return cerrors.NewBadRequestError("insertVulnerability needs at least the Name and the Namespace") } if !onlyFixedIn && !vulnerability.Severity.IsValid() { msg := fmt.Sprintf("could not insert a vulnerability that has an invalid Severity: %s", vulnerability.Severity) log.Warning(msg) return cerrors.NewBadRequestError(msg) } for i := 0; i < len(vulnerability.FixedIn); i++ { fifv := &vulnerability.FixedIn[i] if fifv.Feature.Namespace.Name == "" { // As there is no Namespace on that FixedIn FeatureVersion, set it to the Vulnerability's // Namespace. fifv.Feature.Namespace.Name = vulnerability.Namespace.Name } else if fifv.Feature.Namespace.Name != vulnerability.Namespace.Name { msg := "could not insert an invalid vulnerability that contains FixedIn FeatureVersion that are not in the same namespace as the Vulnerability" log.Warning(msg) return cerrors.NewBadRequestError(msg) } } // We do `defer observeQueryTime` here because we don't want to observe invalid vulnerabilities. defer observeQueryTime("insertVulnerability", "all", tf) // Begin transaction. tx, err := pgSQL.Begin() if err != nil { tx.Rollback() return handleError("insertVulnerability.Begin()", err) } // Find existing vulnerability and its Vulnerability_FixedIn_Features (for update). existingVulnerability, err := findVulnerability(tx, vulnerability.Namespace.Name, vulnerability.Name, true) if err != nil && err != cerrors.ErrNotFound { tx.Rollback() return err } if onlyFixedIn { // Because this call tries to update FixedIn FeatureVersion, import all other data from the // existing one. if existingVulnerability.ID == 0 { return cerrors.ErrNotFound } fixedIn := vulnerability.FixedIn vulnerability = existingVulnerability vulnerability.FixedIn = fixedIn } if existingVulnerability.ID != 0 { updateMetadata := vulnerability.Description != existingVulnerability.Description || vulnerability.Link != existingVulnerability.Link || vulnerability.Severity != existingVulnerability.Severity || !reflect.DeepEqual(castMetadata(vulnerability.Metadata), existingVulnerability.Metadata) // Construct the entire list of FixedIn FeatureVersion, by using the // the FixedIn list of the old vulnerability. // // TODO(Quentin-M): We could use !updateFixedIn to just copy FixedIn/Affects rows from the // existing vulnerability in order to make metadata updates much faster. var updateFixedIn bool vulnerability.FixedIn, updateFixedIn = applyFixedInDiff(existingVulnerability.FixedIn, vulnerability.FixedIn) if !updateMetadata && !updateFixedIn { tx.Commit() return nil } // Mark the old vulnerability as non latest. _, err = tx.Exec(removeVulnerability, vulnerability.Namespace.Name, vulnerability.Name) if err != nil { tx.Rollback() return handleError("removeVulnerability", err) } } else { // The vulnerability is new, we don't want to have any types.MinVersion as they are only used // for diffing existing vulnerabilities. var fixedIn []database.FeatureVersion for _, fv := range vulnerability.FixedIn { if fv.Version != types.MinVersion { fixedIn = append(fixedIn, fv) } } vulnerability.FixedIn = fixedIn } // Find or insert Vulnerability's Namespace. namespaceID, err := pgSQL.insertNamespace(vulnerability.Namespace) if err != nil { return err } // Insert vulnerability. err = tx.QueryRow( insertVulnerability, namespaceID, vulnerability.Name, vulnerability.Description, vulnerability.Link, &vulnerability.Severity, &vulnerability.Metadata, ).Scan(&vulnerability.ID) if err != nil { tx.Rollback() return handleError("insertVulnerability", err) } // Update Vulnerability_FixedIn_Feature and Vulnerability_Affects_FeatureVersion now. err = pgSQL.insertVulnerabilityFixedInFeatureVersions(tx, vulnerability.ID, vulnerability.FixedIn) if err != nil { tx.Rollback() return err } // Create a notification. if generateNotification { err = createNotification(tx, existingVulnerability.ID, vulnerability.ID) if err != nil { return err } } // Commit transaction. err = tx.Commit() if err != nil { tx.Rollback() return handleError("insertVulnerability.Commit()", err) } return nil }
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 }
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) } } }