000ab5164f0be68cf1ea6b6e7227f11c0e388a42

Author
Tomer Fichman <tomer@patternlabs.co>
Committer
GitHub <noreply@github.com>
Date

Message

Merge commit from fork

* fix: require admin privileges for force delete of LFS locks

Move user context retrieval before the force flag check to ensure
proper authorization. Force deletions now require admin privileges,
preventing non-admin users from deleting locks owned by others.

Fixes GHSA-6jm8-x3g6-r33j (CVE-2026-22253)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: improve comment clarity for force delete path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

Diff

 1diff --git a/pkg/web/git_lfs.go b/pkg/web/git_lfs.go
 2index 72b37e88c9bd93773dd338bebceb2832a8f01b57..cc109107312891630b8f3c3884ec33bba9c0d2f9 100644
 3--- a/pkg/web/git_lfs.go
 4+++ b/pkg/web/git_lfs.go
 5@@ -893,7 +893,6 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
 6 		return
 7 	}
 8 
 9-	// Delete another user's lock
10 	l := lfs.Lock{
11 		ID:       strconv.FormatInt(lock.ID, 10),
12 		Path:     lock.Path,
13@@ -902,7 +901,27 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
14 			Name: owner.Username,
15 		},
16 	}
17+
18+	// Retrieve user context first for authorization checks
19+	user := proto.UserFromContext(ctx)
20+	if user == nil {
21+		logger.Error("error getting user from context")
22+		renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{
23+			Message: "unauthorized",
24+		})
25+		return
26+	}
27+
28+	// Force delete another user's lock (requires admin privileges)
29 	if req.Force {
30+		if !user.IsAdmin() {
31+			logger.Error("non-admin user attempted force delete", "user", user.Username())
32+			renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{
33+				Message: "admin access required for force delete",
34+			})
35+			return
36+		}
37+
38 		if err := datastore.DeleteLFSLock(ctx, dbx, repo.ID(), lockID); err != nil {
39 			logger.Error("error deleting lock", "err", err)
40 			renderJSON(w, http.StatusInternalServerError, lfs.ErrorResponse{
41@@ -915,16 +934,7 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
42 		return
43 	}
44 
45-	// Delete our own lock
46-	user := proto.UserFromContext(ctx)
47-	if user == nil {
48-		logger.Error("error getting user from context")
49-		renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{
50-			Message: "unauthorized",
51-		})
52-		return
53-	}
54-
55+	// Delete our own lock - verify ownership
56 	if owner.ID != user.ID() {
57 		logger.Error("error deleting another user's lock")
58 		renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{