fix: critical security vulnerabilities

- Fix type assertion panics in auth.go (9 handlers)
  * GetProfile, UpdateProfile, UpdateTheme, UpdateUsername
  * UpdateEmail, UpdatePassword, DeleteAccount
  * UpdateScanSettings, GetScanSettings, Register admin check
  * Replace c.Get("user_id").(string) with MustGetAuthenticatedUser()

- Fix type assertion panic in library.go
  * GetUserVisibleLibraries now uses MustGetAuthenticatedUser()

- Add path traversal protection to AddLibraryFolder
  * Detect and block ".." in paths
  * Clean paths with filepath.Clean()
  * Verify path is a directory before adding

- Remove debug logging from Login handler
  * Removed all fmt.Printf statements
  * No more plaintext password logging

- Create safe context helper functions
  * internal/handlers/context.go added
  * GetAuthenticatedUser() for safe retrieval
  * MustGetAuthenticatedUser() for post-auth middleware

Security: Critical
Tests: All 62 integration tests pass
Breaking: None - backward compatible
This commit is contained in:
2026-01-30 08:58:43 -05:00
parent 8a8a81ef78
commit 420af7978a
6 changed files with 683 additions and 104 deletions
+19 -8
View File
@@ -5,6 +5,8 @@ import (
"bookmann/internal/services"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
@@ -107,13 +109,9 @@ func (h *LibraryHandler) ListLibraries(c echo.Context) error {
// GetUserVisibleLibraries retrieves libraries visible to the current user
func (h *LibraryHandler) GetUserVisibleLibraries(c echo.Context) error {
userID := c.Get("user_id").(string)
userUUID, err := uuid.Parse(userID)
if err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid user id"})
}
user := MustGetAuthenticatedUser(c)
libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), pgtype.UUID{Bytes: userUUID, Valid: true})
libraries, err := h.libraryService.GetUserVisibleLibraries(c.Request().Context(), user.ID)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
@@ -179,8 +177,16 @@ func (h *LibraryHandler) AddLibraryFolder(c echo.Context) error {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
// Path traversal protection - detect and block .. in path
if strings.Contains(req.FolderPath, "..") {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "path traversal not allowed"})
}
// Clean the path to remove any redundant separators or . references
cleanPath := filepath.Clean(req.FolderPath)
// Validate that folder path exists and is accessible
_, err = os.Stat(req.FolderPath)
fileInfo, err := os.Stat(cleanPath)
if err != nil {
if os.IsNotExist(err) {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "folder path does not exist"})
@@ -188,10 +194,15 @@ func (h *LibraryHandler) AddLibraryFolder(c echo.Context) error {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "folder is not accessible"})
}
// Ensure it's actually a directory, not a file
if !fileInfo.IsDir() {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "path must be a directory"})
}
folder, err := h.libraryService.AddLibraryFolder(
c.Request().Context(),
libraryID,
req.FolderPath,
cleanPath,
)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})