mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-29 01:25:03 -04:00
utils.VerifyPath joins its argument onto the base path, so a path that the caller already joined always passes. Several callers gave it joined paths, and their checks could not fail: - modeladmin (config view, patch, edit, pin and state): the config file path from the loader. A config loaded from outside the models directory (--models-config-file) could be pinned, and the pin wrote the outside file. The patch and state paths stopped later, in the mutation snapshot, with a different error. - core/backend/tts.go: the model path joined onto the models path. - The trellis2cpp and stablediffusion-ggml backends: option paths (*_path) joined onto the model path. A "../" value outside the model directory was accepted. Add utils.VerifyResolvedPath for a full path. modeladmin and tts use it. The backends now check the relative option value before they join it. A rename in modeladmin checks the new relative name. For models from a config file outside the models directory, the admin API and web UI now return ErrPathNotTrusted for view, edit, pin, and enable or disable. The docs describe this. 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>
46 lines
1.5 KiB
Go
46 lines
1.5 KiB
Go
package modeladmin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/mudler/LocalAI/pkg/utils"
|
|
)
|
|
|
|
// SyncPinnedFn lets the caller (HTTP handler or inproc client) propagate a
|
|
// pin/unpin to the watchdog without coupling this package to it.
|
|
type SyncPinnedFn func()
|
|
|
|
// TogglePinned pins or unpins a model. action must be ActionPin or
|
|
// ActionUnpin. syncPinned, if non-nil, is invoked after a successful
|
|
// reload so the watchdog can refresh its eviction-exempt set.
|
|
func (s *ConfigService) TogglePinned(_ context.Context, name string, action Action, syncPinned SyncPinnedFn) (*ToggleResult, error) {
|
|
if name == "" {
|
|
return nil, ErrNameRequired
|
|
}
|
|
if !action.Valid(ActionPin, ActionUnpin) {
|
|
return nil, fmt.Errorf("%w: must be %q or %q, got %q", ErrBadAction, ActionPin, ActionUnpin, action)
|
|
}
|
|
cfg, exists := s.Loader.GetModelConfig(name)
|
|
if !exists {
|
|
return nil, ErrNotFound
|
|
}
|
|
configPath := cfg.GetModelConfigFile()
|
|
if configPath == "" {
|
|
return nil, ErrConfigFileMissing
|
|
}
|
|
if err := utils.VerifyResolvedPath(configPath, s.modelsPath()); err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrPathNotTrusted, err)
|
|
}
|
|
if err := mutateYAMLBoolFlag(configPath, "pinned", action == ActionPin); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := s.Loader.LoadModelConfigsFromPath(s.modelsPath(), s.AppConfig.ToConfigLoaderOptions()...); err != nil {
|
|
return nil, fmt.Errorf("reload configs: %w", err)
|
|
}
|
|
if syncPinned != nil {
|
|
syncPinned()
|
|
}
|
|
return &ToggleResult{Filename: configPath, Action: action}, nil
|
|
}
|