CVE-2025-67819
Description
An issue was discovered in Weaviate OSS before 1.33.4. Due to a lack of validation of the fileName field in the transfer logic, an attacker who can call the GetFile method while a shard is in the "Pause file activity" state and the FileReplicationService is reachable can read arbitrary files accessible to the service process.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A path traversal in Weaviate's shard movement API (GetFile method) lets an attacker read arbitrary files by manipulating the fileName field.
Vulnerability
CVE-2025-67819 is a medium-severity path traversal vulnerability in Weaviate OSS, an open-source vector database. The flaw resides in the shard movement API's transfer logic, where the fileName field is not validated. An attacker who can call the GetFile method while a shard is in the "Pause file activity" state can supply a malicious fileName containing parent-directory traversal sequences (e.g., ../../..) or absolute paths, escaping the intended shard root directory [1][2][4].
Exploitation
Exploitation requires the attacker to have the ability to call the GetFile method, and the FileReplicationService must be reachable. Additionally, the shard must be in the "Pause file activity" state. The Shard Movement API is disabled by default, which reduces the attack surface for most deployments [4]. The vulnerability was introduced in Weaviate version 1.30.0 [4].
Impact
A successful attacker can read arbitrary files accessible to the Weaviate service process, potentially exposing sensitive configuration files, credentials, or other data stored on the server's filesystem [2].
Mitigation
Weaviate has released security patches for versions 1.30.x, 1.31.x, 1.32.x, and 1.33.x. Users are advised to update to the latest patched versions (e.g., 1.33.4 or later). Weaviate Cloud and Marketplace customers have been patched automatically [4].
- GitHub - weaviate/weaviate: Weaviate is an open-source vector database that stores both objects and vectors, allowing for the combination of vector search with structured filtering with the fault tolerance and scalability of a cloud-native database.
- NVD - CVE-2025-67819
- Weaviate security release - Medium and High severity fixes for CVE-2025-67818 and CVE-2025-67819
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/weaviate/weaviateGo | >= 1.30.0, < 1.30.20 | 1.30.20 |
github.com/weaviate/weaviateGo | >= 1.31.0-rc.0, < 1.31.19 | 1.31.19 |
github.com/weaviate/weaviateGo | >= 1.32.0-rc.0, < 1.32.16 | 1.32.16 |
github.com/weaviate/weaviateGo | >= 1.33.0-rc.0, < 1.33.4 | 1.33.4 |
Affected products
2Patches
389c2270869e6Fix path breakout in backup
4 files changed · +80 −6
entities/diskio/files.go+33 −1 modified@@ -11,7 +11,12 @@ package diskio -import "os" +import ( + "fmt" + "os" + "path/filepath" + "strings" +) func FileExists(file string) (bool, error) { _, err := os.Stat(file) @@ -33,3 +38,30 @@ func Fsync(path string) error { return f.Sync() } + +// SanitizeFilePathJoin joins a root path and a relative file path, ensuring that the resulting path is within the root +// path. It assumes that the relativeFilePath is attacker controlled. +func SanitizeFilePathJoin(rootPath string, relativeFilePath string) (string, error) { + // Resolve symlinks in root path + rootPath, err := filepath.EvalSymlinks(rootPath) + if err != nil { + return "", fmt.Errorf("resolve symlinks for root path %q: %w", rootPath, err) + } + + // clean the path to remove any ../ or ./ sequences + cleanFilePath := filepath.Clean(relativeFilePath) + if filepath.IsAbs(cleanFilePath) { + return "", fmt.Errorf("relative file path %q is an absolute path", relativeFilePath) + } + combinedPath := filepath.Join(rootPath, cleanFilePath) + finalPath := filepath.Clean(combinedPath) + + rel, err := filepath.Rel(rootPath, finalPath) + if err != nil { + return "", fmt.Errorf("make %q relative to %q: %w", finalPath, rootPath, err) + } + if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { + return "", fmt.Errorf("file path %q is outside shard root %q", finalPath, rootPath) + } + return finalPath, nil +}
entities/diskio/files_test.go+38 −0 added@@ -0,0 +1,38 @@ +package diskio + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSanitizeFilePathJoin(t *testing.T) { + tests := []struct { + name string + relative string + wantErr bool + }{ + {name: "valid relative", relative: "sub/file.txt", wantErr: false}, + {name: "escape with dot-dot", relative: filepath.Join("..", "outside", "out.txt"), wantErr: true}, + {name: "absolute path rejected", relative: filepath.Join(string(filepath.Separator), "etc", "passwd"), wantErr: true}, + {name: "normalized traversal inside root", relative: filepath.Join("sub", "..", "sub", "file.txt"), wantErr: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + root := t.TempDir() + got, err := SanitizeFilePathJoin(root, tc.relative) + + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + rootPath, err := filepath.EvalSymlinks(root) + require.NoError(t, err) + + require.Equal(t, filepath.Join(rootPath, "sub", "file.txt"), got) + }) + } +}
usecases/backup/zip.go+8 −4 modified@@ -26,6 +26,7 @@ import ( "time" "github.com/weaviate/weaviate/entities/backup" + "github.com/weaviate/weaviate/entities/diskio" ) // CompressionLevel represents supported compression level @@ -101,7 +102,7 @@ func (z *zip) WriteShard(ctx context.Context, sd *backup.ShardDescriptor) (writt n, err = z.WriteRegulars(ctx, sd.Files) written += n - return + return written, err } func (z *zip) WriteRegulars(ctx context.Context, relPaths []string) (written int64, err error) { @@ -170,7 +171,7 @@ func (z *zip) writeOne(ctx context.Context, info fs.FileInfo, relPath string, r } return written, fmt.Errorf("copy: %s %w", relPath, err) } - return + return written, err } // lastWritten number of bytes @@ -239,7 +240,10 @@ func (u *unzip) ReadChunk() (written int64, err error) { } // target file - target := filepath.Join(u.destPath, header.Name) + target, err := diskio.SanitizeFilePathJoin(u.destPath, header.Name) + if err != nil { + return written, fmt.Errorf("sanitize file path %s: %w", header.Name, err) + } switch header.Typeflag { case tar.TypeDir: if err := os.MkdirAll(target, 0o755); err != nil { @@ -295,7 +299,7 @@ type readCloser struct { func (r *readCloser) Read(p []byte) (n int, err error) { n, err = r.src.Read(p) atomic.AddInt64(&r.n, int64(n)) - return + return n, err } func (r *readCloser) Close() error { return r.src.Close() }
usecases/backup/zip_test.go+1 −1 modified@@ -138,7 +138,7 @@ func TestUnzipPathEscape(t *testing.T) { }() _, err = uz.ReadChunk() - require.NoError(t, err) + require.ErrorContains(t, err, "outside shard root") entries, err := os.ReadDir(completelyUnrelatedDir) require.NoError(t, err)
4ff2cc89277cReturn correct path
1 file changed · +11 −5
adapters/repos/db/shard_backup.go+11 −5 modified@@ -324,16 +324,22 @@ func (s *Shard) sanitizeFilePath(relativeFilePath string) (string, error) { combinedPath := filepath.Join(s.index.Config.RootPath, cleanFilePath) finalPath, err := filepath.EvalSymlinks(combinedPath) if err != nil { - return "", fmt.Errorf("resolve symlinks for %q: %w", combinedPath, err) + return "", fmt.Errorf("resolve symlinks for %q: %w", finalPath, err) } finalPath = filepath.Clean(finalPath) - rel, err := filepath.Rel(s.index.Config.RootPath, finalPath) + // Resolve symlinks in root path - this is important for testing on MacOs where /var is a symlink + rootPath, err := filepath.EvalSymlinks(s.index.Config.RootPath) if err != nil { - return "", fmt.Errorf("make %q relative to %q: %w", combinedPath, s.index.Config.RootPath, err) + return "", fmt.Errorf("resolve symlinks for root path %q: %w", s.index.Config.RootPath, err) + } + + rel, err := filepath.Rel(rootPath, finalPath) + if err != nil { + return "", fmt.Errorf("make %q relative to %q: %w", finalPath, rootPath, err) } if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { - return "", fmt.Errorf("file path %q is outside shard root %q", relativeFilePath, s.index.Config.RootPath) + return "", fmt.Errorf("file path %q is outside shard root %q", finalPath, rootPath) } - return combinedPath, nil + return finalPath, nil }
b18cc7ea82d8Fix path sanitazion and adapt test
2 files changed · +34 −9
adapters/repos/db/shard_backup.go+26 −2 modified@@ -17,6 +17,7 @@ import ( "io" "os" "path/filepath" + "strings" "time" "github.com/weaviate/weaviate/entities/backup" @@ -283,7 +284,10 @@ func (s *Shard) GetFileMetadata(ctx context.Context, relativeFilePath string) (f s.mayResetInactivityTimer() - finalPath := filepath.Join(s.Index().Config.RootPath, relativeFilePath) + finalPath, err := s.sanitizeFilePath(relativeFilePath) + if err != nil { + return file.FileMetadata{}, fmt.Errorf("sanitize file path %q: %w", relativeFilePath, err) + } return file.GetFileMetadata(finalPath) } @@ -298,7 +302,10 @@ func (s *Shard) GetFile(ctx context.Context, relativeFilePath string) (io.ReadCl s.mayResetInactivityTimer() - finalPath := filepath.Join(s.Index().Config.RootPath, relativeFilePath) + finalPath, err := s.sanitizeFilePath(relativeFilePath) + if err != nil { + return nil, fmt.Errorf("sanitize file path %q: %w", relativeFilePath, err) + } reader, err := os.Open(finalPath) if err != nil { @@ -307,3 +314,20 @@ func (s *Shard) GetFile(ctx context.Context, relativeFilePath string) (io.ReadCl return reader, nil } + +func (s *Shard) sanitizeFilePath(relativeFilePath string) (string, error) { + // clean the path to remove any ../ or ./ sequences + cleanFilePath := filepath.Clean(relativeFilePath) + if filepath.IsAbs(cleanFilePath) { + return "", fmt.Errorf("relative file path %q is an absolute path", relativeFilePath) + } + final := filepath.Join(s.index.Config.RootPath, cleanFilePath) + rel, err := filepath.Rel(s.index.Config.RootPath, final) + if err != nil { + return "", fmt.Errorf("make %q relative to %q: %w", final, s.index.Config.RootPath, err) + } + if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { + return "", fmt.Errorf("file path %q is outside shard root %q", relativeFilePath, s.index.Config.RootPath) + } + return final, nil +}
adapters/repos/db/shard_path_test.go+8 −7 modified@@ -1,14 +1,14 @@ package db import ( - "fmt" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" "github.com/weaviate/weaviate/entities/additional" + "github.com/weaviate/weaviate/entities/backup" ) func TestShardFileSanitize(t *testing.T) { @@ -19,8 +19,6 @@ func TestShardFileSanitize(t *testing.T) { ctx := testCtx() className := "TestClass" shd, _ := testShard(t, ctx, className) - rootPath := shd.Index().Config.RootPath - fmt.Println(rootPath) require.NoError(t, shd.HaltForTransfer(ctx, false, 100*time.Millisecond)) amount := 10 @@ -35,10 +33,13 @@ func TestShardFileSanitize(t *testing.T) { require.Nil(t, err) require.Equal(t, amount, len(objs)) - file, err := shd.GetFile(ctx, "../001/secret.txt") - require.NoError(t, err) + _, err = shd.GetFile(ctx, "../001/secret.txt") + require.Error(t, err) + + ret := &backup.ShardDescriptor{} + require.NoError(t, shd.ListBackupFiles(ctx, ret)) - buf := make([]byte, 6) - _, err = file.Read(buf) + file, err := shd.GetFile(ctx, ret.ShardVersionPath) require.NoError(t, err) + require.NotNil(t, file) }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-hmmh-292h-3364ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-67819ghsaADVISORY
- github.com/weaviate/weaviate/commit/4ff2cc89277c264c37d0f7316d9eb6368cfc30ffghsaWEB
- github.com/weaviate/weaviate/commit/89c2270869e6d64f5b5276b8626c11cd816c6665ghsaWEB
- github.com/weaviate/weaviate/commit/b18cc7ea82d80a61e7943361a6e335e3fd5a49c7ghsaWEB
- weaviate.io/blog/weaviate-security-release-november-2025ghsaWEB
News mentions
0No linked articles in our index yet.