CVE-2024-5182 (published 2024-06-20T00:15Z)

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.


Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/mudler/localai
commit_details: repo_url: https://github.com/mudler/localai
author: Ettore Di Giacinto
author_date: 2024-05-21 14:33:47+02:00
msg: dependencies(grpcio): bump to fix CI issues (#2362)

feat(grpcio): bump to fix CI issues

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
repository: repo_url: https://github.com/mudler/localai
repo_name: mudler/LocalAI
description: :robot: The free, Open Source OpenAI alternative. Self-hosted, community-driven and local-first. Drop-in replacement for OpenAI running on consumer-grade hardware. No GPU required. Runs gguf, transformers, diffusers and many more models architectures. It allows to generate Text, Audio, Video, Images. Also with voice cloning capabilities.
repo_language: C++
owner: mudler
file_changes: filename: generate_grpc_cache.yaml
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Shell
filename: image_build.yml
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Shell
filename: release.yaml
change_type: ModificationType.MODIFY
diff: @@ -5,7 +5,7 @@ on:
 - pull_request
 
 env:
-  GRPC_VERSION: v1.63.0
+  GRPC_VERSION: v1.64.0
 
 permissions:
   contents: write

programming_language: Shell
filename: test-extra.yml
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Shell
filename: test.yml
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Shell
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 accelerate
 auto-gptq==0.7.1
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 torch
 certifi

programming_language: Shell
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,2 +1,2 @@
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
\ No newline at end of file

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 accelerate
 compel
 diffusers
-grpcio==1.63.0
+grpcio==1.64.0
 opencv-python
 pillow
 protobuf

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 torch
 transformers

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 accelerate
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 certifi
 torch

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Batchfile
filename: requirements-intel.txt
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Shell
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 librosa==0.9.1
 faster-whisper==0.9.0

programming_language: Shell
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 accelerate
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 torch
 git+https://github.com/huggingface/parler-tts.git@10016fb0300c0dc31a0fb70e26f3affee7b62f16

programming_language: Shell
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 accelerate
 rerankers[transformers]
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 certifi
 transformers
\ No newline at end of file

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 accelerate
 transformers
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 torch
 scipy==1.13.0

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 accelerate
 transformers
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 torch
 certifi
\ No newline at end of file

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
 accelerate
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 certifi
\ No newline at end of file

programming_language: Batchfile
filename: requirements.txt
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 accelerate
 vllm
-grpcio==1.63.0
+grpcio==1.64.0
 protobuf
 certifi
 transformers

programming_language: Batchfile
filename: backend_config.go
change_type: ModificationType.MODIFY
diff: @@ -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
+}

programming_language: Go
filename: backend_config_loader.go
change_type: ModificationType.MODIFY
diff: @@ -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")
 		}
 	}
 

programming_language: Go
filename: backend_config_test.go
change_type: ModificationType.ADD
diff: @@ -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())
+		})
+	})
+})

programming_language: Go
filename: config_suite_test.go
change_type: ModificationType.ADD
diff: @@ -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")
+}

programming_language: Go
filename: config_test.go
change_type: ModificationType.MODIFY
diff: @@ -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"))

programming_language: Go
filename: process.go
change_type: ModificationType.MODIFY
diff: @@ -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)

programming_language: Go