Merge commit from fork

Tomer Fichman and Claude Opus 4.5 created

* 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>

Change summary

pkg/web/git_lfs.go | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)

Detailed changes

pkg/web/git_lfs.go 🔗

@@ -893,7 +893,6 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	// Delete another user's lock
 	l := lfs.Lock{
 		ID:       strconv.FormatInt(lock.ID, 10),
 		Path:     lock.Path,
@@ -902,7 +901,27 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
 			Name: owner.Username,
 		},
 	}
+
+	// Retrieve user context first for authorization checks
+	user := proto.UserFromContext(ctx)
+	if user == nil {
+		logger.Error("error getting user from context")
+		renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{
+			Message: "unauthorized",
+		})
+		return
+	}
+
+	// Force delete another user's lock (requires admin privileges)
 	if req.Force {
+		if !user.IsAdmin() {
+			logger.Error("non-admin user attempted force delete", "user", user.Username())
+			renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{
+				Message: "admin access required for force delete",
+			})
+			return
+		}
+
 		if err := datastore.DeleteLFSLock(ctx, dbx, repo.ID(), lockID); err != nil {
 			logger.Error("error deleting lock", "err", err)
 			renderJSON(w, http.StatusInternalServerError, lfs.ErrorResponse{
@@ -915,16 +934,7 @@ func serviceLfsLocksDelete(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	// Delete our own lock
-	user := proto.UserFromContext(ctx)
-	if user == nil {
-		logger.Error("error getting user from context")
-		renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{
-			Message: "unauthorized",
-		})
-		return
-	}
-
+	// Delete our own lock - verify ownership
 	if owner.ID != user.ID() {
 		logger.Error("error deleting another user's lock")
 		renderJSON(w, http.StatusForbidden, lfs.ErrorResponse{