Path Traversal in mudler/localai
Description
A path traversal vulnerability exists in mudler/localai version 2.14.0, where an attacker can exploit the model parameter during the model deletion process to delete arbitrary files. Specifically, by crafting a request with a manipulated model parameter, an attacker can traverse the directory structure and target files outside of the intended directory, leading to the deletion of sensitive data. This vulnerability is due to insufficient input validation and sanitization of the model parameter.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/go-skynet/LocalAIGo | < 2.16.0 | 2.16.0 |
Affected products
1- Range: unspecified
Patches
11a3dedece06cdependencies(grpcio): bump to fix CI issues (#2362)
28 files changed · +159 −47
backend/python/autogptq/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate auto-gptq==0.7.1 -grpcio==1.63.0 +grpcio==1.64.0 protobuf torch certifi
backend/python/bark/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate bark==0.1.5 -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi transformers \ No newline at end of file
backend/python/common/template/requirements.txt+1 −1 modified@@ -1,2 +1,2 @@ -grpcio==1.63.0 +grpcio==1.64.0 protobuf \ No newline at end of file
backend/python/coqui/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate TTS==0.22.0 -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi transformers \ No newline at end of file
backend/python/diffusers/requirements.txt+1 −1 modified@@ -1,7 +1,7 @@ accelerate compel diffusers -grpcio==1.63.0 +grpcio==1.64.0 opencv-python pillow protobuf
backend/python/exllama2/requirements.txt+1 −1 modified@@ -1,5 +1,5 @@ accelerate -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi torch
backend/python/exllama/requirements.txt+1 −1 modified@@ -1,4 +1,4 @@ -grpcio==1.63.0 +grpcio==1.64.0 protobuf torch transformers
backend/python/mamba/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ causal-conv1d==1.2.0.post2 mamba-ssm==1.2.0.post1 -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi transformers \ No newline at end of file
backend/python/openvoice/requirements-intel.txt+1 −1 modified@@ -2,7 +2,7 @@ intel-extension-for-pytorch torch optimum[openvino] -grpcio==1.63.0 +grpcio==1.64.0 protobuf librosa==0.9.1 faster-whisper==0.9.0
backend/python/openvoice/requirements.txt+1 −1 modified@@ -1,4 +1,4 @@ -grpcio==1.63.0 +grpcio==1.64.0 protobuf librosa==0.9.1 faster-whisper==0.9.0
backend/python/parler-tts/requirements.txt+1 −1 modified@@ -1,5 +1,5 @@ accelerate -grpcio==1.63.0 +grpcio==1.64.0 protobuf torch git+https://github.com/huggingface/parler-tts.git@10016fb0300c0dc31a0fb70e26f3affee7b62f16
backend/python/rerankers/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate rerankers[transformers] -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi transformers \ No newline at end of file
backend/python/sentencetransformers/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate sentence-transformers==2.5.1 transformers -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi \ No newline at end of file
backend/python/transformers-musicgen/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate transformers -grpcio==1.63.0 +grpcio==1.64.0 protobuf torch scipy==1.13.0
backend/python/transformers/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate transformers -grpcio==1.63.0 +grpcio==1.64.0 protobuf torch certifi \ No newline at end of file
backend/python/vall-e-x/requirements.txt+1 −1 modified@@ -1,4 +1,4 @@ accelerate -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi \ No newline at end of file
backend/python/vllm/requirements.txt+1 −1 modified@@ -1,6 +1,6 @@ accelerate vllm -grpcio==1.63.0 +grpcio==1.64.0 protobuf certifi transformers
core/config/backend_config.go+24 −0 modified@@ -2,6 +2,8 @@ package config import ( "os" + "regexp" + "strings" "github.com/go-skynet/LocalAI/core/schema" "github.com/go-skynet/LocalAI/pkg/downloader" @@ -356,3 +358,25 @@ func (cfg *BackendConfig) SetDefaults(opts ...ConfigLoaderOption) { cfg.Debug = &trueV } } + +func (c *BackendConfig) Validate() bool { + // Simple validation to make sure the model can be correctly loaded + for _, n := range []string{c.Backend, c.Model} { + if strings.HasPrefix(n, string(os.PathSeparator)) || + strings.Contains(n, "..") { + return false + } + } + + if c.Name == "" { + return false + } + + if c.Backend != "" { + // a regex that checks that is a string name with no special characters, except '-' and '_' + re := regexp.MustCompile(`^[a-zA-Z0-9-_]+$`) + return re.MatchString(c.Backend) + } + + return true +}
core/config/backend_config_loader.go+17 −4 modified@@ -143,7 +143,9 @@ func (cm *BackendConfigLoader) LoadBackendConfigFile(file string, opts ...Config } for _, cc := range c { - cm.configs[cc.Name] = *cc + if cc.Validate() { + cm.configs[cc.Name] = *cc + } } return nil } @@ -156,7 +158,12 @@ func (cl *BackendConfigLoader) LoadBackendConfig(file string, opts ...ConfigLoad return fmt.Errorf("cannot read config file: %w", err) } - cl.configs[c.Name] = *c + if c.Validate() { + cl.configs[c.Name] = *c + } else { + return fmt.Errorf("config is not valid") + } + return nil } @@ -297,7 +304,7 @@ func (cm *BackendConfigLoader) LoadBackendConfigsFromPath(path string, opts ...C defer cm.Unlock() entries, err := os.ReadDir(path) if err != nil { - return err + return fmt.Errorf("cannot read directory '%s': %w", path, err) } files := make([]fs.FileInfo, 0, len(entries)) for _, entry := range entries { @@ -314,8 +321,14 @@ func (cm *BackendConfigLoader) LoadBackendConfigsFromPath(path string, opts ...C continue } c, err := ReadBackendConfig(filepath.Join(path, file.Name()), opts...) - if err == nil { + if err != nil { + log.Error().Err(err).Msgf("cannot read config file: %s", file.Name()) + continue + } + if c.Validate() { cm.configs[c.Name] = *c + } else { + log.Error().Err(err).Msgf("config is not valid") } }
core/config/backend_config_test.go+65 −0 added@@ -0,0 +1,65 @@ +package config_test + +import ( + "io" + "net/http" + "os" + + . "github.com/go-skynet/LocalAI/core/config" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Test cases for config related functions", func() { + Context("Test Read configuration functions", func() { + It("Test Validate", func() { + tmp, err := os.CreateTemp("", "config.yaml") + Expect(err).To(BeNil()) + defer os.Remove(tmp.Name()) + _, err = tmp.WriteString( + `backend: "foo-bar" +parameters: + model: "foo-bar"`) + Expect(err).ToNot(HaveOccurred()) + config, err := ReadBackendConfig(tmp.Name()) + Expect(err).To(BeNil()) + Expect(config).ToNot(BeNil()) + Expect(config.Validate()).To(BeFalse()) + }) + It("Test Validate", func() { + tmp, err := os.CreateTemp("", "config.yaml") + Expect(err).To(BeNil()) + defer os.Remove(tmp.Name()) + _, err = tmp.WriteString( + `name: bar-baz +backend: "foo-bar" +parameters: + model: "foo-bar"`) + Expect(err).ToNot(HaveOccurred()) + config, err := ReadBackendConfig(tmp.Name()) + Expect(err).To(BeNil()) + Expect(config).ToNot(BeNil()) + // two configs in config.yaml + Expect(config.Name).To(Equal("bar-baz")) + Expect(config.Validate()).To(BeTrue()) + + // download https://raw.githubusercontent.com/mudler/LocalAI/master/embedded/models/hermes-2-pro-mistral.yaml + httpClient := http.Client{} + resp, err := httpClient.Get("https://raw.githubusercontent.com/mudler/LocalAI/master/embedded/models/hermes-2-pro-mistral.yaml") + Expect(err).To(BeNil()) + defer resp.Body.Close() + tmp, err = os.CreateTemp("", "config.yaml") + Expect(err).To(BeNil()) + defer os.Remove(tmp.Name()) + _, err = io.Copy(tmp, resp.Body) + Expect(err).To(BeNil()) + config, err = ReadBackendConfig(tmp.Name()) + Expect(err).To(BeNil()) + Expect(config).ToNot(BeNil()) + // two configs in config.yaml + Expect(config.Name).To(Equal("hermes-2-pro-mistral")) + Expect(config.Validate()).To(BeTrue()) + }) + }) +})
core/config/config_suite_test.go+13 −0 added@@ -0,0 +1,13 @@ +package config_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestConfig(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Config test suite") +}
core/config/config_test.go+2 −7 modified@@ -28,16 +28,11 @@ var _ = Describe("Test cases for config related functions", func() { It("Test LoadConfigs", func() { cm := NewBackendConfigLoader() - opts := NewApplicationConfig() - err := cm.LoadBackendConfigsFromPath(opts.ModelPath) + err := cm.LoadBackendConfigsFromPath(os.Getenv("MODELS_PATH")) Expect(err).To(BeNil()) Expect(cm.ListBackendConfigs()).ToNot(BeNil()) - // config should includes gpt4all models's api.config - Expect(cm.ListBackendConfigs()).To(ContainElements("gpt4all")) - - // config should includes gpt2 models's api.config - Expect(cm.ListBackendConfigs()).To(ContainElements("gpt4all-2")) + Expect(cm.ListBackendConfigs()).To(ContainElements("code-search-ada-code-001")) // config should includes text-embedding-ada-002 models's api.config Expect(cm.ListBackendConfigs()).To(ContainElements("text-embedding-ada-002"))
.github/workflows/generate_grpc_cache.yaml+1 −1 modified@@ -84,7 +84,7 @@ jobs: build-args: | GRPC_BASE_IMAGE=${{ matrix.grpc-base-image }} GRPC_MAKEFLAGS=--jobs=4 --output-sync=target - GRPC_VERSION=v1.63.0 + GRPC_VERSION=v1.64.0 context: . file: ./Dockerfile cache-to: type=gha,ignore-error=true
.github/workflows/image_build.yml+1 −1 modified@@ -218,7 +218,7 @@ jobs: BASE_IMAGE=${{ inputs.base-image }} GRPC_BASE_IMAGE=${{ inputs.grpc-base-image || inputs.base-image }} GRPC_MAKEFLAGS=--jobs=4 --output-sync=target - GRPC_VERSION=v1.63.0 + GRPC_VERSION=v1.64.0 MAKEFLAGS=${{ inputs.makeflags }} context: . file: ./Dockerfile
.github/workflows/release.yaml+1 −1 modified@@ -5,7 +5,7 @@ on: - pull_request env: - GRPC_VERSION: v1.63.0 + GRPC_VERSION: v1.64.0 permissions: contents: write
.github/workflows/test-extra.yml+12 −12 modified@@ -29,7 +29,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test transformers run: | @@ -51,7 +51,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test sentencetransformers run: | @@ -74,7 +74,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test rerankers run: | @@ -96,7 +96,7 @@ jobs: sudo apt-get install -y libopencv-dev # Install UV curl -LsSf https://astral.sh/uv/install.sh | sh - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test diffusers run: | make --jobs=5 --output-sync=target -C backend/python/diffusers @@ -117,7 +117,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test parler-tts run: | @@ -139,7 +139,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test openvoice run: | @@ -161,7 +161,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test transformers-musicgen run: | @@ -185,7 +185,7 @@ jobs: # curl -LsSf https://astral.sh/uv/install.sh | sh # sudo apt-get install -y ca-certificates cmake curl patch python3-pip # sudo apt-get install -y libopencv-dev - # pip install --user grpcio-tools==1.63.0 + # pip install --user grpcio-tools==1.64.0 # - name: Test petals # run: | @@ -249,7 +249,7 @@ jobs: # curl -LsSf https://astral.sh/uv/install.sh | sh # sudo apt-get install -y ca-certificates cmake curl patch python3-pip # sudo apt-get install -y libopencv-dev - # pip install --user grpcio-tools==1.63.0 + # pip install --user grpcio-tools==1.64.0 # - name: Test bark # run: | @@ -274,7 +274,7 @@ jobs: # curl -LsSf https://astral.sh/uv/install.sh | sh # sudo apt-get install -y ca-certificates cmake curl patch python3-pip # sudo apt-get install -y libopencv-dev - # pip install --user grpcio-tools==1.63.0 + # pip install --user grpcio-tools==1.64.0 # - name: Test vllm # run: | # make --jobs=5 --output-sync=target -C backend/python/vllm @@ -294,7 +294,7 @@ jobs: curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get install -y ca-certificates cmake curl patch python3-pip sudo apt-get install -y libopencv-dev - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test vall-e-x run: | make --jobs=5 --output-sync=target -C backend/python/vall-e-x @@ -314,7 +314,7 @@ jobs: sudo apt-get install -y ca-certificates cmake curl patch espeak espeak-ng python3-pip # Install UV curl -LsSf https://astral.sh/uv/install.sh | sh - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test coqui run: | make --jobs=5 --output-sync=target -C backend/python/coqui
.github/workflows/test.yml+2 −2 modified@@ -10,7 +10,7 @@ on: - '*' env: - GRPC_VERSION: v1.63.0 + GRPC_VERSION: v1.64.0 concurrency: group: ci-tests-${{ github.head_ref || github.ref }}-${{ github.repository }} @@ -213,7 +213,7 @@ jobs: - name: Dependencies run: | brew install protobuf grpc make protoc-gen-go protoc-gen-go-grpc - pip install --user grpcio-tools==1.63.0 + pip install --user grpcio-tools==1.64.0 - name: Test run: | export C_INCLUDE_PATH=/usr/local/include
pkg/model/process.go+4 −2 modified@@ -30,8 +30,10 @@ func (ml *ModelLoader) StopAllExcept(s string) error { } func (ml *ModelLoader) deleteProcess(s string) error { - if err := ml.grpcProcesses[s].Stop(); err != nil { - return err + if _, exists := ml.grpcProcesses[s]; exists { + if err := ml.grpcProcesses[s].Stop(); err != nil { + return err + } } delete(ml.grpcProcesses, s) delete(ml.models, s)
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
4News mentions
0No linked articles in our index yet.