CVE-2023-0739: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') in GitHub repository answerdev/answer prior to 1.0.4.
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: 6.8
Published: 2023-02-08T05:15Z
Repository: https://github.com/answerdev/answer
Language: Go
Files changed: docs/docs.go, docs/swagger.json, docs/swagger.yaml, internal/repo/collection/collection_repo.go
Commit date: 2023-01-30 15:25:43 +0800
Commit message: update add db session

Diff: diff --git a/docs/docs.go b/docs/docs.go
index 1660ee72..f0d447e2 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -7505,15 +7505,10 @@ const docTemplate = `{
                 "bio": {
                     "description": "bio",
                     "type": "string",
                     "maxLength": 4096
                 },
-                "bio_html": {
-                    "description": "bio",
-                    "type": "string",
-                    "maxLength": 4096
-                },
                 "display_name": {
                     "description": "display_name",
                     "type": "string",
                     "maxLength": 30
                 },
diff --git a/docs/swagger.json b/docs/swagger.json
index 91ca0f4c..ec2b0db0 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -7493,15 +7493,10 @@
                 "bio": {
                     "description": "bio",
                     "type": "string",
                     "maxLength": 4096
                 },
-                "bio_html": {
-                    "description": "bio",
-                    "type": "string",
-                    "maxLength": 4096
-                },
                 "display_name": {
                     "description": "display_name",
                     "type": "string",
                     "maxLength": 30
                 },
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 232fe850..8e5ccf5b 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -1585,14 +1585,10 @@ definitions:
         description: avatar
       bio:
         description: bio
         maxLength: 4096
         type: string
-      bio_html:
-        description: bio
-        maxLength: 4096
-        type: string
       display_name:
         description: display_name
         maxLength: 30
         type: string
       location:
diff --git a/internal/repo/collection/collection_repo.go b/internal/repo/collection/collection_repo.go
index f73c74b7..396398c5 100644
--- a/internal/repo/collection/collection_repo.go
+++ b/internal/repo/collection/collection_repo.go
@@ -9,10 +9,11 @@ import (
 	"github.com/answerdev/answer/internal/base/reason"
 	"github.com/answerdev/answer/internal/entity"
 	collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
 	"github.com/answerdev/answer/internal/service/unique"
 	"github.com/segmentfault/pacman/errors"
+	"xorm.io/xorm"
 )
 
 // collectionRepo collection repository
 type collectionRepo struct {
 	data         *data.Data
@@ -27,19 +28,32 @@ func NewCollectionRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo) collec
 	}
 }
 
 // AddCollection add collection
 func (cr *collectionRepo) AddCollection(ctx context.Context, collection *entity.Collection) (err error) {
-	id, err := cr.uniqueIDRepo.GenUniqueIDStr(ctx, collection.TableName())
-	if err == nil {
-		collection.ID = id
-		_, err = cr.data.DB.Insert(collection)
+	_, err = cr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
+		var has bool
+		dbcollection := &entity.Collection{}
+		result = nil
+		has, err = session.Where("user_id = ? and object_id = ?", collection.UserID, collection.ObjectID).Get(dbcollection)
 		if err != nil {
-			return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+			return
 		}
-	}
-	return nil
+		if has {
+			return
+		}
+		id, err := cr.uniqueIDRepo.GenUniqueIDStr(ctx, collection.TableName())
+		if err == nil {
+			collection.ID = id
+			_, err = session.Insert(collection)
+			if err != nil {
+				return result, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+			}
+		}
+		return
+	})
+	return err
 }
 
 // RemoveCollection delete collection
 func (cr *collectionRepo) RemoveCollection(ctx context.Context, id string) (err error) {
 	_, err = cr.data.DB.Where("id =?", id).Delete(&entity.Collection{})

Vulnerable code:                 "bio_html": {
                    "description": "bio",
                    "type": "string",
                    "maxLength": 4096
                },
                "bio_html": {
                    "description": "bio",
                    "type": "string",
                    "maxLength": 4096
                },
      bio_html:
        description: bio
        maxLength: 4096
        type: string
	id, err := cr.uniqueIDRepo.GenUniqueIDStr(ctx, collection.TableName())
	if err == nil {
		collection.ID = id
		_, err = cr.data.DB.Insert(collection)
			return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
	}
	return nil
Fixed code: 	"xorm.io/xorm"
	_, err = cr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
		var has bool
		dbcollection := &entity.Collection{}
		result = nil
		has, err = session.Where("user_id = ? and object_id = ?", collection.UserID, collection.ObjectID).Get(dbcollection)
			return
		if has {
			return
		}
		id, err := cr.uniqueIDRepo.GenUniqueIDStr(ctx, collection.TableName())
		if err == nil {
			collection.ID = id
			_, err = session.Insert(collection)
			if err != nil {
				return result, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
			}
		}
		return
	})
	return err