Example #1
0
func layerEqual(expected, actual *Layer) bool {
	eq := true
	eq = eq && expected.Node == actual.Node
	eq = eq && expected.ID == actual.ID
	eq = eq && expected.ParentNode == actual.ParentNode
	eq = eq && expected.OS == actual.OS
	eq = eq && expected.EngineVersion == actual.EngineVersion
	eq = eq && len(utils.CompareStringLists(actual.SuccessorsNodes, expected.SuccessorsNodes)) == 0 && len(utils.CompareStringLists(expected.SuccessorsNodes, actual.SuccessorsNodes)) == 0
	eq = eq && len(utils.CompareStringLists(actual.RemovedPackagesNodes, expected.RemovedPackagesNodes)) == 0 && len(utils.CompareStringLists(expected.RemovedPackagesNodes, actual.RemovedPackagesNodes)) == 0
	eq = eq && len(utils.CompareStringLists(actual.InstalledPackagesNodes, expected.InstalledPackagesNodes)) == 0 && len(utils.CompareStringLists(expected.InstalledPackagesNodes, actual.InstalledPackagesNodes)) == 0
	return eq
}
Example #2
0
// AllPackages computes the full list of packages that l has and return them as
// nodes.
// It requires that FieldLayerParent, FieldLayerContentInstalledPackages,
// FieldLayerContentRemovedPackages fields has been selected on l
func (l *Layer) AllPackages() ([]string, error) {
	var allPackages []string

	parent, err := l.Parent([]string{FieldLayerParent, FieldLayerPackages})
	if err != nil {
		return []string{}, err
	}
	if parent != nil {
		allPackages, err = parent.AllPackages()
		if err != nil {
			return []string{}, err
		}
	}

	return append(utils.CompareStringLists(allPackages, l.RemovedPackagesNodes), l.InstalledPackagesNodes...), nil
}
Example #3
0
// detectAndInsertInstalledAndRemovedPackages finds the installed and removed
// package nodes and inserts the installed packages into the database.
func detectAndInsertInstalledAndRemovedPackages(detectedOS string, packageList []*database.Package, parent *database.Layer) (installedNodes, removedNodes []string, err error) {
	// Get the parent layer's packages.
	parentPackageNodes, err := parent.AllPackages()
	if err != nil {
		return nil, nil, err
	}
	parentPackages, err := database.FindAllPackagesByNodes(parentPackageNodes, []string{database.FieldPackageName, database.FieldPackageVersion})
	if err != nil {
		return nil, nil, err
	}

	// Map detected packages (name:version) string to packages.
	packagesNVMapToPackage := make(map[string]*database.Package)
	for _, p := range packageList {
		packagesNVMapToPackage[p.Name+":"+p.Version.String()] = p
	}

	// Map parent's packages (name:version) string to nodes.
	parentPackagesNVMapToNodes := make(map[string]string)
	for _, p := range parentPackages {
		parentPackagesNVMapToNodes[p.Name+":"+p.Version.String()] = p.Node
	}

	// Build a list of the parent layer's packages' node values.
	var parentPackagesNV []string
	for _, p := range parentPackages {
		parentPackagesNV = append(parentPackagesNV, p.Name+":"+p.Version.String())
	}

	// Build a list of the layer packages' node values.
	var layerPackagesNV []string
	for _, p := range packageList {
		layerPackagesNV = append(layerPackagesNV, p.Name+":"+p.Version.String())
	}

	// Calculate the installed and removed packages.
	removedPackagesNV := utils.CompareStringLists(parentPackagesNV, layerPackagesNV)
	installedPackagesNV := utils.CompareStringLists(layerPackagesNV, parentPackagesNV)

	// Build a list of all the installed packages.
	var installedPackages []*database.Package
	for _, nv := range installedPackagesNV {
		p, _ := packagesNVMapToPackage[nv]
		p.OS = detectedOS
		installedPackages = append(installedPackages, p)
	}

	// Insert that list into the database.
	err = database.InsertPackages(installedPackages)
	if err != nil {
		return nil, nil, err
	}

	// Build the list of installed package nodes.
	for _, p := range installedPackages {
		if p.Node != "" {
			installedNodes = append(installedNodes, p.Node)
		}
	}

	// Build the list of removed package nodes.
	for _, nv := range removedPackagesNV {
		node, _ := parentPackagesNVMapToNodes[nv]
		removedNodes = append(removedNodes, node)
	}

	return
}
Example #4
0
// InsertVulnerabilities inserts or updates several vulnerabilities in the database in one transaction
// It ensures that a vulnerability can't be fixed by two packages belonging the same Branch.
// During an update, if the vulnerability was previously fixed by a version in a branch and a new package of that branch is specified, the previous one is deleted
// Otherwise, it simply adds the defined packages, there is currently no way to delete affected packages.
//
// ID, Link, Priority and FixedInNodes fields have to be specified. Description is optionnal.
func InsertVulnerabilities(vulnerabilities []*Vulnerability) ([]Notification, error) {
	if len(vulnerabilities) == 0 {
		return []Notification{}, nil
	}

	// Create required data structure
	var err error
	t := cayley.NewTransaction()
	cachedVulnerabilities := make(map[string]*Vulnerability)
	newVulnerabilityNotifications := make(map[string]*NewVulnerabilityNotification)
	vulnerabilityPriorityIncreasedNotifications := make(map[string]*VulnerabilityPriorityIncreasedNotification)
	vulnerabilityPackageChangedNotifications := make(map[string]*VulnerabilityPackageChangedNotification)

	// Iterate over all the vulnerabilities we need to insert/update
	for _, vulnerability := range vulnerabilities {
		// Is the vulnerability already existing ?
		existingVulnerability, _ := cachedVulnerabilities[vulnerability.ID]
		if existingVulnerability == nil {
			existingVulnerability, err = FindOneVulnerability(vulnerability.ID, FieldVulnerabilityAll)
			if err != nil && err != cerrors.ErrNotFound {
				return []Notification{}, err
			}
			if existingVulnerability != nil {
				cachedVulnerabilities[vulnerability.ID] = existingVulnerability
			}
		}

		// Don't allow inserting/updating a vulnerability which is fixed in two packages of the same branch
		if len(vulnerability.FixedInNodes) > 0 {
			fixedInPackages, err := FindAllPackagesByNodes(vulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName})
			if err != nil {
				return []Notification{}, err
			}
			fixedInBranches := make(map[string]struct{})
			for _, fixedInPackage := range fixedInPackages {
				branch := fixedInPackage.Branch()
				if _, branchExists := fixedInBranches[branch]; branchExists {
					log.Warningf("could not insert vulnerability %s because it is fixed in two packages of the same branch", vulnerability.ID)
					return []Notification{}, cerrors.NewBadRequestError("could not insert a vulnerability which is fixed in two packages of the same branch")
				}
				fixedInBranches[branch] = struct{}{}
			}
		}

		// Insert/Update vulnerability
		if existingVulnerability == nil {
			// The vulnerability does not exist, create it

			// Verify parameters
			if vulnerability.ID == "" || vulnerability.Link == "" || vulnerability.Priority == "" {
				log.Warningf("could not insert an incomplete vulnerability [ID: %s, Link: %s, Priority: %s]", vulnerability.ID, vulnerability.Link, vulnerability.Priority)
				return []Notification{}, cerrors.NewBadRequestError("Could not insert an incomplete vulnerability")
			}
			if !vulnerability.Priority.IsValid() {
				log.Warningf("could not insert a vulnerability which has an invalid priority [ID: %s, Link: %s, Priority: %s]. Valid priorities are: %v.", vulnerability.ID, vulnerability.Link, vulnerability.Priority, types.Priorities)
				return []Notification{}, cerrors.NewBadRequestError("Could not insert a vulnerability which has an invalid priority")
			}
			if len(vulnerability.FixedInNodes) == 0 {
				log.Warningf("could not insert a vulnerability which doesn't affect any package [ID: %s].", vulnerability.ID)
				return []Notification{}, cerrors.NewBadRequestError("could not insert a vulnerability which doesn't affect any package")
			}

			// Insert it
			vulnerability.Node = vulnerability.GetNode()
			cachedVulnerabilities[vulnerability.ID] = vulnerability

			t.AddQuad(cayley.Quad(vulnerability.Node, FieldIs, FieldVulnerabilityIsValue, ""))
			t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityID, vulnerability.ID, ""))
			t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityLink, vulnerability.Link, ""))
			t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority), ""))
			t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description, ""))
			for _, p := range vulnerability.FixedInNodes {
				t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityFixedIn, p, ""))
			}

			// Add a notification
			newVulnerabilityNotifications[vulnerability.ID] = &NewVulnerabilityNotification{VulnerabilityID: vulnerability.ID}
		} else {
			// The vulnerability already exists, update it
			if vulnerability.Link != "" && existingVulnerability.Link != vulnerability.Link {
				t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityLink, existingVulnerability.Link, ""))
				t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityLink, vulnerability.Link, ""))
				existingVulnerability.Link = vulnerability.Link
			}
			if vulnerability.Priority != "" && vulnerability.Priority != types.Unknown && existingVulnerability.Priority != vulnerability.Priority {
				if !vulnerability.Priority.IsValid() {
					log.Warningf("could not update a vulnerability which has an invalid priority [ID: %s, Link: %s, Priority: %s]. Valid priorities are: %v.", vulnerability.ID, vulnerability.Link, vulnerability.Priority, types.Priorities)
					return []Notification{}, cerrors.NewBadRequestError("Could not update a vulnerability which has an invalid priority")
				}

				// Add a notification about the priority change if the new priority is higher and the vulnerability is not new
				if vulnerability.Priority.Compare(existingVulnerability.Priority) > 0 {
					if _, newVulnerabilityNotificationExists := newVulnerabilityNotifications[vulnerability.ID]; !newVulnerabilityNotificationExists {
						// Any priorityChangeNotification already ?
						if existingPriorityNotification, _ := vulnerabilityPriorityIncreasedNotifications[vulnerability.ID]; existingPriorityNotification != nil {
							// There is a priority change notification, replace it but keep the old priority field
							vulnerabilityPriorityIncreasedNotifications[vulnerability.ID] = &VulnerabilityPriorityIncreasedNotification{OldPriority: existingPriorityNotification.OldPriority, NewPriority: vulnerability.Priority, VulnerabilityID: existingVulnerability.ID}
						} else {
							// No previous notification, just add a new one
							vulnerabilityPriorityIncreasedNotifications[vulnerability.ID] = &VulnerabilityPriorityIncreasedNotification{OldPriority: existingVulnerability.Priority, NewPriority: vulnerability.Priority, VulnerabilityID: existingVulnerability.ID}
						}
					}
				}

				t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityPriority, string(existingVulnerability.Priority), ""))
				t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority), ""))
				existingVulnerability.Priority = vulnerability.Priority
			}
			if vulnerability.Description != "" && existingVulnerability.Description != vulnerability.Description {
				t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityDescription, existingVulnerability.Description, ""))
				t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description, ""))
				existingVulnerability.Description = vulnerability.Description
			}
			if len(vulnerability.FixedInNodes) > 0 && len(utils.CompareStringLists(vulnerability.FixedInNodes, existingVulnerability.FixedInNodes)) != 0 {
				var removedNodes []string
				var addedNodes []string

				existingVulnerabilityFixedInPackages, err := FindAllPackagesByNodes(existingVulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion})
				if err != nil {
					return []Notification{}, err
				}
				vulnerabilityFixedInPackages, err := FindAllPackagesByNodes(vulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion})
				if err != nil {
					return []Notification{}, err
				}

				for _, p := range vulnerabilityFixedInPackages {
					// Any already existing link ?
					fixedInLinkAlreadyExists := false
					for _, ep := range existingVulnerabilityFixedInPackages {
						if *p == *ep {
							// This exact link already exists, we won't insert it again
							fixedInLinkAlreadyExists = true
						} else if p.Branch() == ep.Branch() {
							// A link to this package branch already exist and is not the same version, we will delete it
							t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityFixedIn, ep.Node, ""))

							var index int
							for i, n := range existingVulnerability.FixedInNodes {
								if n == ep.Node {
									index = i
									break
								}
							}
							existingVulnerability.FixedInNodes = append(existingVulnerability.FixedInNodes[index:], existingVulnerability.FixedInNodes[index+1:]...)
							removedNodes = append(removedNodes, ep.Node)
						}
					}

					if fixedInLinkAlreadyExists == false {
						t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityFixedIn, p.Node, ""))
						existingVulnerability.FixedInNodes = append(existingVulnerability.FixedInNodes, p.Node)
						addedNodes = append(addedNodes, p.Node)
					}
				}

				// Add notification about the FixedIn modification if the vulnerability is not new
				if len(removedNodes) > 0 || len(addedNodes) > 0 {
					if _, newVulnerabilityNotificationExists := newVulnerabilityNotifications[vulnerability.ID]; !newVulnerabilityNotificationExists {
						// Any VulnerabilityPackageChangedNotification already ?
						if existingPackageNotification, _ := vulnerabilityPackageChangedNotifications[vulnerability.ID]; existingPackageNotification != nil {
							// There is a priority change notification, add the packages modifications to it
							existingPackageNotification.AddedFixedInNodes = append(existingPackageNotification.AddedFixedInNodes, addedNodes...)
							existingPackageNotification.RemovedFixedInNodes = append(existingPackageNotification.RemovedFixedInNodes, removedNodes...)
						} else {
							// No previous notification, just add a new one
							vulnerabilityPackageChangedNotifications[vulnerability.ID] = &VulnerabilityPackageChangedNotification{VulnerabilityID: vulnerability.ID, AddedFixedInNodes: addedNodes, RemovedFixedInNodes: removedNodes}
						}
					}
				}
			}
		}
	}

	// Apply transaction
	if err = store.ApplyTransaction(t); err != nil {
		log.Errorf("failed transaction (InsertVulnerabilities): %s", err)
		return []Notification{}, ErrTransaction
	}

	// Group all notifications
	var allNotifications []Notification
	for _, notification := range newVulnerabilityNotifications {
		allNotifications = append(allNotifications, notification)
	}
	for _, notification := range vulnerabilityPriorityIncreasedNotifications {
		allNotifications = append(allNotifications, notification)
	}
	for _, notification := range vulnerabilityPackageChangedNotifications {
		allNotifications = append(allNotifications, notification)
	}

	return allNotifications, nil
}
Example #5
0
func (n *VulnerabilityPackageChangedNotification) GetContent() (interface{}, error) {
	// Returns the removed and added packages as well as the layers that
	// introduced the vulnerability in the past but don't anymore because of the
	// removed packages and the layers that now introduce the vulnerability
	// because of the added packages

	// Find vulnerability.
	vulnerability, err := FindOneVulnerability(n.VulnerabilityID, []string{FieldVulnerabilityID, FieldVulnerabilityLink, FieldVulnerabilityPriority, FieldVulnerabilityDescription, FieldVulnerabilityFixedIn})
	if err != nil {
		return []byte{}, err
	}
	abstractVulnerability, err := vulnerability.ToAbstractVulnerability()
	if err != nil {
		return []byte{}, err
	}

	// First part of the answer : added/removed packages
	addedPackages, err := FindAllPackagesByNodes(n.AddedFixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion, FieldPackagePreviousVersion})
	if err != nil {
		return []byte{}, err
	}
	removedPackages, err := FindAllPackagesByNodes(n.RemovedFixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion, FieldPackagePreviousVersion})
	if err != nil {
		return []byte{}, err
	}

	// Second part of the answer
	var addedPackagesPreviousVersions []string
	for _, pkg := range addedPackages {
		previousVersions, err := pkg.PreviousVersions([]string{})
		if err != nil {
			return []*Layer{}, err
		}
		for _, version := range previousVersions {
			addedPackagesPreviousVersions = append(addedPackagesPreviousVersions, version.Node)
		}
	}
	var removedPackagesPreviousVersions []string
	for _, pkg := range removedPackages {
		previousVersions, err := pkg.PreviousVersions([]string{})
		if err != nil {
			return []*Layer{}, err
		}
		for _, version := range previousVersions {
			removedPackagesPreviousVersions = append(removedPackagesPreviousVersions, version.Node)
		}
	}

	newIntroducingLayers, err := FindAllLayersByAddedPackageNodes(addedPackagesPreviousVersions, []string{FieldLayerID})
	if err != nil {
		return []byte{}, err
	}
	formerIntroducingLayers, err := FindAllLayersByAddedPackageNodes(removedPackagesPreviousVersions, []string{FieldLayerID})
	if err != nil {
		return []byte{}, err
	}

	newIntroducingLayersIDs := []string{} // empty slice, not null
	for _, l := range newIntroducingLayers {
		newIntroducingLayersIDs = append(newIntroducingLayersIDs, l.ID)
	}
	formerIntroducingLayersIDs := []string{} // empty slice, not null
	for _, l := range formerIntroducingLayers {
		formerIntroducingLayersIDs = append(formerIntroducingLayersIDs, l.ID)
	}

	// Remove layers which appears both in new and former lists (eg. case of updated packages but still vulnerable)
	filteredNewIntroducingLayersIDs := utils.CompareStringLists(newIntroducingLayersIDs, formerIntroducingLayersIDs)
	filteredFormerIntroducingLayersIDs := utils.CompareStringLists(formerIntroducingLayersIDs, newIntroducingLayersIDs)

	return struct {
		Vulnerability                                      *AbstractVulnerability
		AddedAffectedPackages, RemovedAffectedPackages     []*AbstractPackage
		NewIntroducingLayersIDs, FormerIntroducingLayerIDs []string
	}{
		Vulnerability:             abstractVulnerability,
		AddedAffectedPackages:     PackagesToAbstractPackages(addedPackages),
		RemovedAffectedPackages:   PackagesToAbstractPackages(removedPackages),
		NewIntroducingLayersIDs:   filteredNewIntroducingLayersIDs,
		FormerIntroducingLayerIDs: filteredFormerIntroducingLayersIDs,
	}, nil
}