CVE-2016-15036: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: ** UNSUPPORTED WHEN ASSIGNED ** A vulnerability was found in Deis Workflow Manager up to 2.3.2. It has been classified as problematic. This affects an unknown part. The manipulation leads to race condition. The complexity of an attack is rather high. The exploitability is told to be difficult. Upgrading to version 2.3.3 is able to address this issue. The patch is named 31fe3bccbdde134a185752e53380330d16053f7f. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-248847. NOTE: This vulnerability only affects products that are no longer supported by the maintainer.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 7.5
Published: 2023-12-23T20:15Z
Repository: https://github.com/deis/workflow-manager
Language: Go
Files changed: boot.go, data/cluster_id.go, jobs/jobs.go
Commit date: 2016-08-11 14:54:43 -0700
Commit message: cluster_id is vulnerable to race condition when called concurrently (#94)

* fix(cluster_id): Get() is "insert if not exist", needs read-write lock protection

* fix(jobs): remove static clusterID getter in sendVersionsImpl

1) add generic data.ClusterID type in sendVersions type struct
2) enable common clusterID reference to be included in multiple, concurrent jobs, so that appropriate locking can be enforced

Diff: diff --git a/boot.go b/boot.go
index ee201a4..b4f359f 100644
--- a/boot.go
+++ b/boot.go
@@ -45,10 +45,11 @@ func main() {
 		pollDur,
 	)
 
 	svPeriodic := jobs.NewSendVersionsPeriodic(
 		apiClient,
+		clusterID,
 		deisK8sResources,
 		availableVersion,
 		pollDur,
 	)
 	toDo := []jobs.Periodic{glvdPeriodic, svPeriodic}
diff --git a/data/cluster_id.go b/data/cluster_id.go
index 352810c..a1b883d 100644
--- a/data/cluster_id.go
+++ b/data/cluster_id.go
@@ -49,10 +49,12 @@ func NewClusterIDFromPersistentStorage(sgc k8s.KubeSecretGetterCreator) ClusterI
 	}
 }
 
 // Get is the ClusterID interface implementation
 func (c clusterIDFromPersistentStorage) Get() (string, error) {
+	c.rwm.Lock()
+	defer c.rwm.Unlock()
 	secret, err := c.secretGetterCreator.Get(wfmSecretName)
 	//If we don't have the secret we shouldn't be returning error and instead a create a new one
 	if err != nil && !apierrors.IsNotFound(err) {
 		return "", err
 	}
diff --git a/jobs/jobs.go b/jobs/jobs.go
index 98a88f8..d5b6aab 100644
--- a/jobs/jobs.go
+++ b/jobs/jobs.go
@@ -20,34 +20,37 @@ type Periodic interface {
 }
 
 // SendVersions fulfills the Periodic interface
 type sendVersions struct {
 	k8sResources      *k8s.ResourceInterfaceNamespaced
+	clusterID         data.ClusterID
 	apiClient         *apiclient.WorkflowManager
 	availableVersions data.AvailableVersions
 	frequency         time.Duration
 }
 
 // NewSendVersionsPeriodic creates a new SendVersions using sgc and rcl as the the secret getter / creator and replication controller lister implementations (respectively)
 func NewSendVersionsPeriodic(
 	apiClient *apiclient.WorkflowManager,
+	clusterID data.ClusterID,
 	ri *k8s.ResourceInterfaceNamespaced,
 	availableVersions data.AvailableVersions,
 	frequency time.Duration,
 ) Periodic {
 	return &sendVersions{
 		k8sResources:      ri,
+		clusterID:         clusterID,
 		apiClient:         apiClient,
 		availableVersions: availableVersions,
 		frequency:         frequency,
 	}
 }
 
 // Do is the Periodic interface implementation
 func (s sendVersions) Do() error {
 	if config.Spec.CheckVersions {
-		err := sendVersionsImpl(s.apiClient, s.k8sResources, s.availableVersions)
+		err := sendVersionsImpl(s.apiClient, s.clusterID, s.k8sResources, s.availableVersions)
 		if err != nil {
 			return err
 		}
 	}
 	return nil
@@ -133,16 +136,17 @@ func DoPeriodic(pSlice []Periodic) chan<- struct{} {
 }
 
 //  sendVersions sends cluster version data
 func sendVersionsImpl(
 	apiClient *apiclient.WorkflowManager,
+	clusterID data.ClusterID,
 	k8sResources *k8s.ResourceInterfaceNamespaced,
 	availableVersions data.AvailableVersions,
 ) error {
 	cluster, err := data.GetCluster(
 		data.NewInstalledDeisData(k8sResources),
-		data.NewClusterIDFromPersistentStorage(k8sResources.Secrets()),
+		clusterID,
 		data.NewLatestReleasedComponent(k8sResources, availableVersions),
 	)
 	if err != nil {
 		log.Println("error getting installed components data")
 		return err

Vulnerable code: 		err := sendVersionsImpl(s.apiClient, s.k8sResources, s.availableVersions)
		data.NewClusterIDFromPersistentStorage(k8sResources.Secrets()),
Fixed code: 		clusterID,
	c.rwm.Lock()
	defer c.rwm.Unlock()
	clusterID         data.ClusterID
	clusterID data.ClusterID,
		clusterID:         clusterID,
		err := sendVersionsImpl(s.apiClient, s.clusterID, s.k8sResources, s.availableVersions)
	clusterID data.ClusterID,
		clusterID,