Esempio n. 1
0
File: lock.go Progetto: ruo91/clair
// Lock tries to set a temporary lock in the database.
// If a lock already exists with the given name/owner, then the lock is renewed
//
// Lock does not block, instead, it returns true and its expiration time
// is the lock has been successfully acquired or false otherwise
func Lock(name string, duration time.Duration, owner string) (bool, time.Time) {
	pruneLocks()

	until := time.Now().Add(duration)
	untilString := strconv.FormatInt(until.Unix(), 10)

	// Try to get the expiration time of a lock with the same name/owner
	currentExpiration, err := toValue(cayley.StartPath(store, name).Has("locked_by", owner).Out("locked_until"))
	if err == nil && currentExpiration != "" {
		// Renew our lock
		if currentExpiration == untilString {
			return true, until
		}

		t := cayley.NewTransaction()
		t.RemoveQuad(cayley.Quad(name, "locked_until", currentExpiration, ""))
		t.AddQuad(cayley.Quad(name, "locked_until", untilString, ""))
		// It is not necessary to verify if the lock is ours again in the transaction
		// because if someone took it, the lock's current expiration probably changed and the transaction will fail
		return store.ApplyTransaction(t) == nil, until
	}

	t := cayley.NewTransaction()
	t.AddQuad(cayley.Quad(name, "locked", "locked", "")) // Necessary to make the transaction fails if the lock already exists (and has not been pruned)
	t.AddQuad(cayley.Quad(name, "locked_until", untilString, ""))
	t.AddQuad(cayley.Quad(name, "locked_by", owner, ""))

	glog.SetStderrThreshold("FATAL")
	success := store.ApplyTransaction(t) == nil
	glog.SetStderrThreshold("ERROR")

	return success, until
}
Esempio n. 2
0
File: lock.go Progetto: dwdm/clair
// Unlock unlocks a lock specified by its name if I own it
func Unlock(name, owner string) {
	unlocked := 0
	it, _ := cayley.StartPath(store, name).Has(fieldLockLocked, fieldLockLockedValue).Has(fieldLockLockedBy, owner).Save(fieldLockLockedUntil, fieldLockLockedUntil).BuildIterator().Optimize()
	defer it.Close()
	for cayley.RawNext(it) {
		tags := make(map[string]graph.Value)
		it.TagResults(tags)

		t := cayley.NewTransaction()
		t.RemoveQuad(cayley.Triple(name, fieldLockLocked, fieldLockLockedValue))
		t.RemoveQuad(cayley.Triple(name, fieldLockLockedUntil, store.NameOf(tags[fieldLockLockedUntil])))
		t.RemoveQuad(cayley.Triple(name, fieldLockLockedBy, owner))
		err := store.ApplyTransaction(t)
		if err != nil {
			log.Errorf("failed transaction (Unlock): %s", err)
		}

		unlocked++
	}
	if it.Err() != nil {
		log.Errorf("failed query in Unlock: %s", it.Err())
	}
	if unlocked > 1 {
		// We should never see this, it would mean that our database doesn't ensure quad uniqueness
		// and that the entire lock system is jeopardized.
		log.Errorf("found inconsistency in Unlock: matched %d times a locked named: %s", unlocked, name)
	}
}
Esempio n. 3
0
File: lock.go Progetto: ruo91/clair
// pruneLocks removes every expired locks from the database
func pruneLocks() {
	now := time.Now()

	// Delete every expired locks
	tr := cayley.NewTransaction()
	it, _ := cayley.StartPath(store, "locked").In("locked").Save("locked_until", "locked_until").Save("locked_by", "locked_by").BuildIterator().Optimize()
	defer it.Close()
	for cayley.RawNext(it) {
		tags := make(map[string]graph.Value)
		it.TagResults(tags)

		n := store.NameOf(it.Result())
		t := store.NameOf(tags["locked_until"])
		o := store.NameOf(tags["locked_by"])
		tt, _ := strconv.ParseInt(t, 10, 64)

		if now.Unix() > tt {
			log.Debugf("Lock %s owned by %s has expired.", n, o)
			tr.RemoveQuad(cayley.Quad(n, "locked", "locked", ""))
			tr.RemoveQuad(cayley.Quad(n, "locked_until", t, ""))
			tr.RemoveQuad(cayley.Quad(n, "locked_by", o, ""))
		}
	}
	store.ApplyTransaction(tr)
}
Esempio n. 4
0
File: flag.go Progetto: ruo91/clair
// UpdateFlag creates a flag or update an existing flag's value
func UpdateFlag(name, value string) error {
	if name == "" || value == "" {
		log.Warning("could not insert a flag which has an empty name or value")
		return cerrors.NewBadRequestError("could not insert a flag which has an empty name or value")
	}

	// Initialize transaction
	t := cayley.NewTransaction()

	// Get current flag value
	currentValue, err := GetFlagValue(name)
	if err != nil {
		return err
	}

	// Build transaction
	name = "flag:" + name
	if currentValue != "" {
		t.RemoveQuad(cayley.Quad(name, "value", currentValue, ""))
	}
	t.AddQuad(cayley.Quad(name, "value", value, ""))

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

	// Return
	return nil
}
Esempio n. 5
0
File: lock.go Progetto: dwdm/clair
// pruneLocks removes every expired locks from the database
func pruneLocks() {
	now := time.Now()

	// Delete every expired locks
	it, _ := cayley.StartPath(store, "locked").In("locked").Save(fieldLockLockedUntil, fieldLockLockedUntil).Save(fieldLockLockedBy, fieldLockLockedBy).BuildIterator().Optimize()
	defer it.Close()
	for cayley.RawNext(it) {
		tags := make(map[string]graph.Value)
		it.TagResults(tags)

		n := store.NameOf(it.Result())
		t := store.NameOf(tags[fieldLockLockedUntil])
		o := store.NameOf(tags[fieldLockLockedBy])
		tt, _ := strconv.ParseInt(t, 10, 64)

		if now.Unix() > tt {
			log.Debugf("lock %s owned by %s has expired.", n, o)

			tr := cayley.NewTransaction()
			tr.RemoveQuad(cayley.Triple(n, fieldLockLocked, fieldLockLockedValue))
			tr.RemoveQuad(cayley.Triple(n, fieldLockLockedUntil, t))
			tr.RemoveQuad(cayley.Triple(n, fieldLockLockedBy, o))
			err := store.ApplyTransaction(tr)
			if err != nil {
				log.Errorf("failed transaction (pruneLocks): %s", err)
				continue
			}
			log.Debugf("lock %s has been successfully pruned.", n)
		}
	}
	if it.Err() != nil {
		log.Errorf("failed query in Unlock: %s", it.Err())
	}
}
Esempio n. 6
0
func (ng *NodeGraph) removeNode(nd *Node) (err error) {
	if len(nd.Children()) > 0 {
		return errors.New("Can't delete node with children, must delete children first")
	}

	transaction := cayley.NewTransaction()
	if nd.Mode() > 0 {
		transaction.RemoveQuad(cayley.Quad(nd.Id, modeLink, fmt.Sprint(int(nd.Mode())), ""))
	}
	if !nd.MTime().IsZero() {
		transaction.RemoveQuad(cayley.Quad(nd.Id, mTimeLink, fmt.Sprint(nd.MTime().Format(timeFormat)), ""))
	}
	if nd.Name() != "" {
		transaction.RemoveQuad(cayley.Quad(nd.Id, nameLink, nd.Name(), ""))
	}
	if nd.Parent() != nil {
		transaction.RemoveQuad(cayley.Quad(nd.Id, parentLink, nd.Parent().Id, ""))
	}
	if nd.Type() != "" {
		transaction.RemoveQuad(cayley.Quad(nd.Id, typeLink, nd.Type(), ""))
	}

	err = ng.ApplyTransaction(transaction)

	if err == nil {
		nd.mode = os.FileMode(0)
		nd.mTime = time.Time{}
		nd.name = ""
		nd.parentId = ""
		nd.Id = ""
	}

	return
}
Esempio n. 7
0
// InsertLayer insert a single layer in the database
//
// ID, and EngineVersion fields are required.
// ParentNode, OS, InstalledPackagesNodes and RemovedPackagesNodes are optional,
// SuccessorsNodes is unnecessary.
//
// The ID MUST be unique for two different layers.
//
//
// If the Layer already exists, nothing is done, except if the provided engine
// version is higher than the existing one, in which case, the OS,
// InstalledPackagesNodes and RemovedPackagesNodes fields will be replaced.
//
// The layer should only contains the newly installed/removed packages
// There is no safeguard that prevents from marking a package as newly installed
// while it has already been installed in one of its parent.
func InsertLayer(layer *Layer) error {
	// Verify parameters
	if layer.ID == "" {
		log.Warning("could not insert a layer which has an empty ID")
		return cerrors.NewBadRequestError("could not insert a layer which has an empty ID")
	}

	// Create required data structures
	t := cayley.NewTransaction()
	layer.Node = layer.GetNode()

	// Try to find an existing layer
	existingLayer, err := FindOneLayerByNode(layer.Node, FieldLayerAll)
	if err != nil && err != cerrors.ErrNotFound {
		return err
	}

	if existingLayer != nil && existingLayer.EngineVersion >= layer.EngineVersion {
		// The layer exists and has an equal or higher engine verison, do nothing
		return nil
	}

	if existingLayer == nil {
		// Create case: add permanent nodes
		t.AddQuad(cayley.Quad(layer.Node, FieldIs, FieldLayerIsValue, ""))
		t.AddQuad(cayley.Quad(layer.Node, FieldLayerID, layer.ID, ""))
		t.AddQuad(cayley.Quad(layer.Node, FieldLayerParent, layer.ParentNode, ""))
	} else {
		// Update case: remove everything before we add updated data
		t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerOS, existingLayer.OS, ""))
		for _, pkg := range existingLayer.InstalledPackagesNodes {
			t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
		}
		for _, pkg := range existingLayer.RemovedPackagesNodes {
			t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
		}
		t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(existingLayer.EngineVersion), ""))
	}

	// Add OS/Packages
	t.AddQuad(cayley.Quad(layer.Node, FieldLayerOS, layer.OS, ""))
	for _, pkg := range layer.InstalledPackagesNodes {
		t.AddQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, ""))
	}
	for _, pkg := range layer.RemovedPackagesNodes {
		t.AddQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, ""))
	}
	t.AddQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion), ""))

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

	return nil
}
Esempio n. 8
0
// MarkNotificationAsSent marks a notification as sent.
func MarkNotificationAsSent(node string) {
	// Initialize transaction
	t := cayley.NewTransaction()

	t.RemoveQuad(cayley.Quad(node, "isSent", strconv.FormatBool(false), ""))
	t.AddQuad(cayley.Quad(node, "isSent", strconv.FormatBool(true), ""))

	// Apply transaction
	store.ApplyTransaction(t)
}
Esempio n. 9
0
// Healthcheck simply adds and then remove a quad in Cayley to ensure it is working
// It returns true when everything is ok
func Healthcheck() health.Status {
	var err error
	if store != nil {
		t := cayley.NewTransaction()
		q := cayley.Triple("cayley", "is", "healthy")
		t.AddQuad(q)
		t.RemoveQuad(q)
		glog.SetStderrThreshold("FATAL") // TODO REMOVE ME
		err = store.ApplyTransaction(t)
		glog.SetStderrThreshold("ERROR") // TODO REMOVE ME
	}

	return health.Status{IsEssential: true, IsHealthy: err == nil, Details: nil}
}
Esempio n. 10
0
File: lock.go Progetto: ruo91/clair
// Unlock unlocks a lock specified by its name if I own it
func Unlock(name, owner string) {
	pruneLocks()

	t := cayley.NewTransaction()
	it, _ := cayley.StartPath(store, name).Has("locked", "locked").Has("locked_by", owner).Save("locked_until", "locked_until").BuildIterator().Optimize()
	defer it.Close()

	for cayley.RawNext(it) {
		tags := make(map[string]graph.Value)
		it.TagResults(tags)

		t.RemoveQuad(cayley.Quad(name, "locked", "locked", ""))
		t.RemoveQuad(cayley.Quad(name, "locked_until", store.NameOf(tags["locked_until"]), ""))
		t.RemoveQuad(cayley.Quad(name, "locked_by", owner, ""))
	}

	store.ApplyTransaction(t)
}
Esempio n. 11
0
File: layer.go Progetto: dwdm/clair
func deleteLayerTreeFrom(node string, t *graph.Transaction) error {
	// Determine if that function call is the root call of the recursivity
	// And create transaction if its the case.
	root := (t == nil)
	if root {
		t = cayley.NewTransaction()
	}

	// Find layer.
	layer, err := FindOneLayerByNode(node, FieldLayerAll)
	if err != nil {
		// Ignore missing layer.
		return nil
	}

	// Remove all successor layers.
	for _, succNode := range layer.SuccessorsNodes {
		deleteLayerTreeFrom(succNode, t)
	}

	// Remove layer.
	t.RemoveQuad(cayley.Triple(layer.Node, fieldIs, fieldLayerIsValue))
	t.RemoveQuad(cayley.Triple(layer.Node, FieldLayerID, layer.ID))
	t.RemoveQuad(cayley.Triple(layer.Node, FieldLayerParent, layer.ParentNode))
	t.RemoveQuad(cayley.Triple(layer.Node, FieldLayerOS, layer.OS))
	t.RemoveQuad(cayley.Triple(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion)))
	for _, pkg := range layer.InstalledPackagesNodes {
		t.RemoveQuad(cayley.Triple(layer.Node, fieldLayerInstalledPackages, pkg))
	}
	for _, pkg := range layer.RemovedPackagesNodes {
		t.RemoveQuad(cayley.Triple(layer.Node, fieldLayerRemovedPackages, pkg))
	}

	// Apply transaction if root call.
	if root {
		if err = store.ApplyTransaction(t); err != nil {
			log.Errorf("failed transaction (deleteLayerTreeFrom): %s", err)
			return ErrTransaction
		}
	}

	return nil
}
Esempio n. 12
0
// DeleteVulnerability deletes the vulnerability having the given ID
func DeleteVulnerability(id string) error {
	vulnerability, err := FindOneVulnerability(id, FieldVulnerabilityAll)
	if err != nil {
		return err
	}

	t := cayley.NewTransaction()
	t.RemoveQuad(cayley.Triple(vulnerability.Node, FieldVulnerabilityID, vulnerability.ID))
	t.RemoveQuad(cayley.Triple(vulnerability.Node, FieldVulnerabilityLink, vulnerability.Link))
	t.RemoveQuad(cayley.Triple(vulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority)))
	t.RemoveQuad(cayley.Triple(vulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description))
	for _, p := range vulnerability.FixedInNodes {
		t.RemoveQuad(cayley.Triple(vulnerability.Node, FieldVulnerabilityFixedIn, p))
	}

	if err := store.ApplyTransaction(t); err != nil {
		log.Errorf("failed transaction (DeleteVulnerability): %s", err)
		return ErrTransaction
	}

	return nil
}
Esempio n. 13
0
// InsertNotifications stores multiple Notification in the database
// It uses the given NotificationWrapper to convert these notifications to
// something that can be stored in the database.
func InsertNotifications(notifications []Notification, wrapper NotificationWrapper) error {
	if len(notifications) == 0 {
		return nil
	}

	// Do not send notifications if there are too many of them (first update for example)
	if len(notifications) > maxNotifications {
		log.Noticef("Ignoring %d notifications", len(notifications))
		return nil
	}

	// Initialize transaction
	t := cayley.NewTransaction()

	// Iterate over all the vulnerabilities we need to insert
	for _, notification := range notifications {
		// Wrap notification
		wrappedNotification, err := wrapper.Wrap(notification)
		if err != nil {
			return err
		}

		node := "notification:" + uuid.New()
		t.AddQuad(cayley.Quad(node, FieldIs, "notification", ""))
		t.AddQuad(cayley.Quad(node, "type", wrappedNotification.Type, ""))
		t.AddQuad(cayley.Quad(node, "data", wrappedNotification.Data, ""))
		t.AddQuad(cayley.Quad(node, "isSent", strconv.FormatBool(false), ""))
	}

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

	return nil
}
Esempio n. 14
0
// InsertVulnerabilities inserts or updates several vulnerabilities in the database in one transaction
// 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)

	var notifications []Notification
	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 {
		// Check if the vulnerability already exists
		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
			}
		}

		// 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()

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

			// Add a notification
			notification := &NewVulnerabilityNotification{VulnerabilityID: vulnerability.ID}
			notifications = append(notifications, notification)
			newVulnerabilityNotifications[vulnerability.ID] = notification

			cachedVulnerabilities[vulnerability.ID] = vulnerability
		} else {
			// The vulnerability already exists, update it
			if vulnerability.Link != "" && existingVulnerability.Link != vulnerability.Link {
				t.RemoveQuad(cayley.Triple(existingVulnerability.Node, FieldVulnerabilityLink, existingVulnerability.Link))
				t.AddQuad(cayley.Triple(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
							existingPriorityNotification.NewPriority = vulnerability.Priority
						} else {
							// No previous notification, just add a new one
							notification := &VulnerabilityPriorityIncreasedNotification{OldPriority: existingVulnerability.Priority, NewPriority: vulnerability.Priority, VulnerabilityID: existingVulnerability.ID}
							notifications = append(notifications, notification)
							vulnerabilityPriorityIncreasedNotifications[vulnerability.ID] = notification
						}
					}
				}

				t.RemoveQuad(cayley.Triple(existingVulnerability.Node, FieldVulnerabilityPriority, string(existingVulnerability.Priority)))
				t.AddQuad(cayley.Triple(existingVulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority)))
				existingVulnerability.Priority = vulnerability.Priority
			}

			if vulnerability.Description != "" && existingVulnerability.Description != vulnerability.Description {
				t.RemoveQuad(cayley.Triple(existingVulnerability.Node, FieldVulnerabilityDescription, existingVulnerability.Description))
				t.AddQuad(cayley.Triple(existingVulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description))
				existingVulnerability.Description = vulnerability.Description
			}

			newFixedInNodes := utils.CompareStringLists(vulnerability.FixedInNodes, existingVulnerability.FixedInNodes)
			if len(newFixedInNodes) > 0 {
				var removedNodes []string
				var addedNodes []string

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

				for _, p := range newFixedInPackages {
					for _, ep := range existingVulnerabilityFixedInPackages {
						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.Triple(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)
						}
					}

					t.AddQuad(cayley.Triple(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 _, 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
						notification := &VulnerabilityPackageChangedNotification{VulnerabilityID: vulnerability.ID, AddedFixedInNodes: addedNodes, RemovedFixedInNodes: removedNodes}
						notifications = append(notifications, notification)
						vulnerabilityPackageChangedNotifications[vulnerability.ID] = notification
					}
				}
			}
		}
	}

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

	return notifications, nil
}
Esempio n. 15
0
// InsertPackages inserts several packages in the database in one transaction
// Packages are stored in linked lists, one per Branch. Each linked list has a start package and an end package defined with types.MinVersion/types.MaxVersion versions
//
// OS, Name and Version fields have to be specified.
// If the insertion is successfull, the Node field is filled and represents the graph node identifier.
func InsertPackages(packageParameters []*Package) error {
	if len(packageParameters) == 0 {
		return nil
	}

	// Verify parameters
	for _, pkg := range packageParameters {
		if pkg.OS == "" || pkg.Name == "" || pkg.Version.String() == "" {
			log.Warningf("could not insert an incomplete package [OS: %s, Name: %s, Version: %s]", pkg.OS, pkg.Name, pkg.Version)
			return cerrors.NewBadRequestError("could not insert an incomplete package")
		}
	}

	// Iterate over all the packages we need to insert
	for _, packageParameter := range packageParameters {
		t := cayley.NewTransaction()

		// Is the package already existing ?
		pkg, err := FindOnePackage(packageParameter.OS, packageParameter.Name, packageParameter.Version, []string{})
		if err != nil && err != cerrors.ErrNotFound {
			return err
		}
		if pkg != nil {
			packageParameter.Node = pkg.Node
			continue
		}

		// Get all packages of the same branch (both from local cache and database)
		branchPackages, err := FindAllPackagesByBranch(packageParameter.OS, packageParameter.Name, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion, FieldPackageNextVersion})
		if err != nil {
			return err
		}

		if len(branchPackages) == 0 {
			// The branch does not exist yet
			insertingStartPackage := packageParameter.Version == types.MinVersion
			insertingEndPackage := packageParameter.Version == types.MaxVersion

			// Create and insert a end package
			endPackage := &Package{
				OS:      packageParameter.OS,
				Name:    packageParameter.Name,
				Version: types.MaxVersion,
			}
			endPackage.Node = endPackage.GetNode()

			t.AddQuad(cayley.Quad(endPackage.Node, FieldIs, FieldPackageIsValue, ""))
			t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageOS, endPackage.OS, ""))
			t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageName, endPackage.Name, ""))
			t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageVersion, endPackage.Version.String(), ""))
			t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageNextVersion, "", ""))

			// Create the inserted package if it is different than a start/end package
			var newPackage *Package
			if !insertingStartPackage && !insertingEndPackage {
				newPackage = &Package{
					OS:      packageParameter.OS,
					Name:    packageParameter.Name,
					Version: packageParameter.Version,
				}
				newPackage.Node = newPackage.GetNode()

				t.AddQuad(cayley.Quad(newPackage.Node, FieldIs, FieldPackageIsValue, ""))
				t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageOS, newPackage.OS, ""))
				t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageName, newPackage.Name, ""))
				t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageVersion, newPackage.Version.String(), ""))
				t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageNextVersion, endPackage.Node, ""))

				packageParameter.Node = newPackage.Node
			}

			// Create and insert a start package
			startPackage := &Package{
				OS:      packageParameter.OS,
				Name:    packageParameter.Name,
				Version: types.MinVersion,
			}
			startPackage.Node = startPackage.GetNode()

			t.AddQuad(cayley.Quad(startPackage.Node, FieldIs, FieldPackageIsValue, ""))
			t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageOS, startPackage.OS, ""))
			t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageName, startPackage.Name, ""))
			t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageVersion, startPackage.Version.String(), ""))
			if !insertingStartPackage && !insertingEndPackage {
				t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageNextVersion, newPackage.Node, ""))
			} else {
				t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageNextVersion, endPackage.Node, ""))
			}

			// Set package node
			if insertingEndPackage {
				packageParameter.Node = endPackage.Node
			} else if insertingStartPackage {
				packageParameter.Node = startPackage.Node
			}
		} else {
			// The branch already exists

			// Create the package
			newPackage := &Package{OS: packageParameter.OS, Name: packageParameter.Name, Version: packageParameter.Version}
			newPackage.Node = "package:" + utils.Hash(newPackage.Key())
			packageParameter.Node = newPackage.Node

			t.AddQuad(cayley.Quad(newPackage.Node, FieldIs, FieldPackageIsValue, ""))
			t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageOS, newPackage.OS, ""))
			t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageName, newPackage.Name, ""))
			t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageVersion, newPackage.Version.String(), ""))

			// Sort branchPackages by version (including the new package)
			branchPackages = append(branchPackages, newPackage)
			sort.Sort(ByVersion(branchPackages))

			// Find my prec/succ GraphID in the sorted slice now
			newPackageKey := newPackage.Key()
			var pred, succ *Package
			var found bool
			for _, p := range branchPackages {
				equal := p.Key() == newPackageKey
				if !equal && !found {
					pred = p
				} else if found {
					succ = p
					break
				} else if equal {
					found = true
					continue
				}
			}
			if pred == nil || succ == nil {
				log.Warningf("could not find any package predecessor/successor of: [OS: %s, Name: %s, Version: %s].", packageParameter.OS, packageParameter.Name, packageParameter.Version)
				return cerrors.NewBadRequestError("could not find package predecessor/successor")
			}

			// Link the new packages with the branch
			t.RemoveQuad(cayley.Quad(pred.Node, FieldPackageNextVersion, succ.Node, ""))

			pred.NextVersionNode = newPackage.Node
			t.AddQuad(cayley.Quad(pred.Node, FieldPackageNextVersion, newPackage.Node, ""))

			newPackage.NextVersionNode = succ.Node
			t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageNextVersion, succ.Node, ""))
		}

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

	// Return
	return nil
}