Some checks failed
Automated Container Build / build-and-push (push) Failing after 12s
68 lines
1.9 KiB
Go
68 lines
1.9 KiB
Go
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()
|
|
}
|
|
}
|