VYPR
Critical severity9.1GHSA Advisory· Published May 14, 2026· Updated May 15, 2026

CVE-2026-44542

CVE-2026-44542

Description

FileBrowser Quantum is a free, self-hosted, web-based file manager. Prior to 1.3.1-stable and 1.3.9-beta, attacker-controlled path input is joined with a trusted base path prior to sanitization, allowing traversal sequences (e.g., ../) to escape the intended shared directory. As a result, an unauthenticated attacker possessing a valid public share hash with delete permissions enabled can delete arbitrary files outside the shared directory within the share owner’s configured storage scope. This affects public/api/resources and public/api/resources/bulk. This vulnerability is fixed in 1.3.1-stable and 1.3.9-beta.

Affected products

1

Patches

1
112740bdd41d

security bugfix (#2374)

https://github.com/gtsteffaniak/filebrowserGraham SteffaniakMay 1, 2026via ghsa
4 files changed · +32 15
  • backend/http/httpRouter.go+1 1 modified
    @@ -150,7 +150,7 @@ func StartHttp(ctx context.Context, storage *bolt.BoltStore, shutdownComplete ch
     	// ========================================
     	api.HandleFunc("GET /share/list", withPermShare(shareListHandler))
     	api.HandleFunc("GET /share/direct", withPermShare(shareDirectDownloadHandler))
    -	api.HandleFunc("GET /share", withPermShare(shareGetHandler))
    +	api.HandleFunc("GET /share", withUser(shareGetHandler))
     	api.HandleFunc("POST /share", withPermShare(sharePostHandler))
     	api.HandleFunc("PATCH /share", withPermShare(sharePatchHandler))
     	api.HandleFunc("DELETE /share", withPermShare(shareDeleteHandler))
    
  • backend/http/middleware.go+5 1 modified
    @@ -53,7 +53,11 @@ type handleFunc func(w http.ResponseWriter, r *http.Request, data *requestContex
     func withHashFileHelper(fn handleFunc) handleFunc {
     	return withOrWithoutUserHelper(func(w http.ResponseWriter, r *http.Request, data *requestContext) (int, error) {
     		hash := r.URL.Query().Get("hash")
    -		path := r.URL.Query().Get("path")
    +		inputPath := r.URL.Query().Get("path")
    +		path, err := utils.SanitizeUserPath(inputPath)
    +		if err != nil && inputPath != "" {
    +			return http.StatusBadRequest, err
    +		}
     
     		// Get the file link by hash
     		link, err := store.Share.GetByHash(hash)
    
  • backend/http/resource.go+23 13 modified
    @@ -251,25 +251,35 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     
     	// Process each item one at a time
     	for _, item := range items {
    +		rawPath := item.Path
     		// Validate item
    -		if item.Path == "" {
    +		if rawPath == "" {
     			response.Failed = append(response.Failed, BulkDeleteItem{
     				Source:  item.Source,
    -				Path:    item.Path,
    +				Path:    rawPath,
     				Message: "path was empty",
     			})
     			continue
     		}
     
     		// Prevent deletion of root
    -		if item.Path == "/" {
    +		if rawPath == "/" {
     			response.Failed = append(response.Failed, BulkDeleteItem{
     				Source:  item.Source,
    -				Path:    item.Path,
    +				Path:    rawPath,
     				Message: "cannot delete root directory",
     			})
     			continue
     		}
    +		sanitizedPath, err := utils.SanitizeUserPath(rawPath)
    +		if err != nil {
    +			response.Failed = append(response.Failed, BulkDeleteItem{
    +				Source:  item.Source,
    +				Path:    sanitizedPath,
    +				Message: err.Error(),
    +			})
    +			continue
    +		}
     
     		if d.share != nil {
     			sourceName := d.share.GetSourceName()
    @@ -281,13 +291,13 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			if err != nil {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: "user does not have access",
     				})
     				continue
     			}
     			withoutUserScope := strings.TrimPrefix(d.share.Path, userScope)
    -			indexPath := utils.JoinPathAsUnix(withoutUserScope, item.Path)
    +			indexPath := utils.JoinPathAsUnix(withoutUserScope, sanitizedPath)
     
     			fileInfo, err := files.FileInfoFaster(utils.FileOptions{
     				FollowSymlinks: true,
    @@ -305,7 +315,7 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     				logger.Errorf("resource bulk delete handler: error deleting file/directory: %v", err)
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: "error deleting file/directory, admin must check the logs",
     				})
     				continue
    @@ -317,7 +327,7 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			if item.Source == "" {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: "source was empty, source is required",
     				})
     				continue
    @@ -328,7 +338,7 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			if err != nil {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: fmt.Sprintf("user does not have access: %v", err),
     				})
     				continue
    @@ -338,7 +348,7 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			if idx == nil {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: "source not found",
     				})
     				continue
    @@ -347,14 +357,14 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			// Get file info
     			fileInfo, err := files.FileInfoFaster(utils.FileOptions{
     				FollowSymlinks: true,
    -				Path:           item.Path,
    +				Path:           sanitizedPath,
     				Source:         item.Source,
     				ShowHidden:     true,
     			}, store.Access, filePermUser, store.Share)
     			if err != nil {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: err.Error(),
     				})
     				continue
    @@ -363,7 +373,7 @@ func resourceBulkDeleteHandler(w http.ResponseWriter, r *http.Request, d *reques
     			if err != nil {
     				response.Failed = append(response.Failed, BulkDeleteItem{
     					Source:  item.Source,
    -					Path:    item.Path,
    +					Path:    sanitizedPath,
     					Message: err.Error(),
     				})
     				continue
    
  • CHANGELOG.md+3 0 modified
    @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. For commit
     
     ## v1.3.9
     
    + **Security**:
    + - [Critical] Unauthenticated Path Traversal in Public Share Delete Allows Arbitrary File Deletion GHSA-fwj3-42wh-8673
    +
      **BugFixes**:
      - Fix context menu items and adjusted when items show to more accurately reflect permissions.
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.