This commit is contained in:
parent
fa2be029a2
commit
724d70e58b
3339 changed files with 1075535 additions and 0 deletions
68
backend/middleware/pathjail.go
Normal file
68
backend/middleware/pathjail.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// PathJail middleware ensures all file path parameters are confined within
|
||||
// the storage root. Any path that resolves outside the jail is rejected
|
||||
// immediately, preventing directory traversal attacks.
|
||||
func PathJail(storageRoot string) fiber.Handler {
|
||||
// Resolve the absolute jail root once at startup
|
||||
absRoot, err := filepath.Abs(storageRoot)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("pathjail: invalid storage root: %v", err))
|
||||
}
|
||||
// Ensure it ends with separator for prefix matching
|
||||
absRootSlash := absRoot + string(filepath.Separator)
|
||||
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Extract the path parameter from the request.
|
||||
// Handlers attach the relative file path as "filepath" param or query.
|
||||
rawPath := c.Params("*")
|
||||
if rawPath == "" {
|
||||
rawPath = c.Query("path", "")
|
||||
}
|
||||
|
||||
// If no path param, assume root path '.'
|
||||
if rawPath == "" {
|
||||
rawPath = "."
|
||||
}
|
||||
|
||||
// Clean and resolve the path
|
||||
cleaned := filepath.Clean(rawPath)
|
||||
|
||||
// Reject obvious traversal attempts before even joining
|
||||
if strings.Contains(cleaned, "..") {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
||||
"error": "path traversal detected",
|
||||
})
|
||||
}
|
||||
|
||||
// Build the full absolute path within the jail
|
||||
fullPath := filepath.Join(absRoot, cleaned)
|
||||
absPath, err := filepath.Abs(fullPath)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "invalid path",
|
||||
})
|
||||
}
|
||||
|
||||
// Final check: resolved path must be within the jail root
|
||||
if absPath != absRoot && !strings.HasPrefix(absPath, absRootSlash) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
|
||||
"error": "access denied: path outside storage boundary",
|
||||
})
|
||||
}
|
||||
|
||||
// Store the validated absolute path for downstream handlers
|
||||
c.Locals("resolvedPath", absPath)
|
||||
c.Locals("relativePath", cleaned)
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
Reference in a new issue