mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-29 09:35:02 -04:00
listModelFiles gave utils.VerifyPath paths that it had already joined onto the models directory. VerifyPath joins its argument onto the base again, so an absolute path always passes and none of the four checks could fail. Model deletion then removed files outside the models directory: - A model name such as "../outside/victim" removed outside/victim.yaml. The in-process MCP delete_model tool passes the name from the tool call without a check. - A gallery file that lists a files: entry with "../" removed that file. listModelFiles now gives VerifyPath the relative names. InTrustedRoot also looped forever when a relative path was outside a relative root. filepath.Dir stops at "." for a relative path, and the loop waited for "/". The loop now stops when Dir returns its input. Assisted-by: Claude:claude-opus-5-5 [Claude Code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
62 lines
1.5 KiB
Go
62 lines
1.5 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func ExistsInPath(path string, s string) bool {
|
|
_, err := os.Stat(filepath.Join(path, s))
|
|
return err == nil
|
|
}
|
|
|
|
func InTrustedRoot(path string, trustedRoot string) error {
|
|
for {
|
|
parent := filepath.Dir(path)
|
|
// Dir stops changing at "/" for an absolute path and at "." for a
|
|
// relative one; waiting for "/" alone spins forever on the latter.
|
|
if parent == path {
|
|
return fmt.Errorf("path is outside of trusted root")
|
|
}
|
|
path = parent
|
|
if path == trustedRoot {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// VerifyPath verifies that path is based in basePath.
|
|
func VerifyPath(path, basePath string) error {
|
|
c := filepath.Clean(filepath.Join(basePath, path))
|
|
return InTrustedRoot(c, filepath.Clean(basePath))
|
|
}
|
|
|
|
// SanitizeFileName sanitizes the given filename
|
|
func SanitizeFileName(fileName string) string {
|
|
// filepath.Clean to clean the path
|
|
cleanName := filepath.Clean(fileName)
|
|
// filepath.Base to ensure we only get the final element, not any directory path
|
|
baseName := filepath.Base(cleanName)
|
|
// Replace any remaining tricky characters that might have survived cleaning
|
|
safeName := strings.ReplaceAll(baseName, "..", "")
|
|
return safeName
|
|
}
|
|
|
|
func GenerateUniqueFileName(dir, baseName, ext string) string {
|
|
counter := 1
|
|
fileName := baseName + ext
|
|
|
|
for {
|
|
filePath := filepath.Join(dir, fileName)
|
|
_, err := os.Stat(filePath)
|
|
if os.IsNotExist(err) {
|
|
return fileName
|
|
}
|
|
|
|
counter++
|
|
fileName = fmt.Sprintf("%s_%d%s", baseName, counter, ext)
|
|
}
|
|
}
|