diff --git a/SPECS/vitess/CVE-2026-27969.patch b/SPECS/vitess/CVE-2026-27969.patch new file mode 100644 index 00000000000..bfdca2cc9bd --- /dev/null +++ b/SPECS/vitess/CVE-2026-27969.patch @@ -0,0 +1,203 @@ +From 7d6f34eec3d29a0e7b69d0180bbaa1646d80e1ad Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Mon, 2 Mar 2026 20:32:06 +0000 +Subject: [PATCH] mysqlctl: use fileutil.SafePathJoin to validate FileEntry + paths and prevent path traversal; add tests for fullPath; add SafePathJoin + implementation + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/vitessio/vitess/pull/19470.diff +--- + go/fileutil/safe_path_join.go | 47 ++++++++++++ + go/vt/mysqlctl/builtinbackupengine.go | 7 +- + go/vt/mysqlctl/builtinbackupengine_test.go | 88 ++++++++++++++++++++++ + 3 files changed, 140 insertions(+), 2 deletions(-) + create mode 100644 go/fileutil/safe_path_join.go + +diff --git a/go/fileutil/safe_path_join.go b/go/fileutil/safe_path_join.go +new file mode 100644 +index 0000000..6d1a1ef +--- /dev/null ++++ b/go/fileutil/safe_path_join.go +@@ -0,0 +1,47 @@ ++/* ++Copyright 2024 The Vitess Authors. ++ ++Licensed under the Apache License, Version 2.0 (the "License"); ++you may not use this file except in compliance with the License. ++You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++Unless required by applicable law or agreed to in writing, software ++distributed under the License is distributed on an "AS IS" BASIS, ++WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++See the License for the specific language governing permissions and ++limitations under the License. ++*/ ++ ++// Package fileutil contains utility functions related to files and paths. ++package fileutil ++ ++import ( ++ "errors" ++ "path" ++ "strings" ++) ++ ++// ErrInvalidJoinedPath is returned when SafePathJoin detects that the joined ++// path escapes the provided base directory. ++var ErrInvalidJoinedPath = errors.New("invalid joined path: escapes base directory") ++ ++// SafePathJoin joins base and rel into a cleaned path, ensuring that rel does ++// not escape the base directory. It rejects path traversal sequences that would ++// resolve outside of base, e.g. "../../etc/passwd". ++func SafePathJoin(base, rel string) (string, error) { ++ // Clean both base and rel to normalize any redundant segments. ++ cleanBase := path.Clean(base) ++ if cleanBase == "." { ++ cleanBase = "" ++ } ++ joined := path.Clean(path.Join(cleanBase, rel)) ++ ++ // Ensure the joined path resides within the base directory. It must either ++ // equal the base or have the base followed by a path separator. ++ if cleanBase != "" && joined != cleanBase && !strings.HasPrefix(joined, cleanBase+"/") { ++ return "", ErrInvalidJoinedPath ++ } ++ return joined, nil ++} +diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go +index cf8c6a1..8fac602 100644 +--- a/go/vt/mysqlctl/builtinbackupengine.go ++++ b/go/vt/mysqlctl/builtinbackupengine.go +@@ -35,6 +35,7 @@ import ( + "github.com/spf13/pflag" + "golang.org/x/sync/semaphore" + ++ "vitess.io/vitess/go/fileutil" + "vitess.io/vitess/go/ioutil" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/concurrency" +@@ -154,7 +155,9 @@ func registerBuiltinBackupEngineFlags(fs *pflag.FlagSet) { + fs.UintVar(&builtinBackupFileWriteBufferSize, "builtinbackup-file-write-buffer-size", builtinBackupFileWriteBufferSize, "write files using an IO buffer of this many bytes. Golang defaults are used when set to 0.") + } + +-// fullPath returns the full path of the entry, based on its type ++// fullPath returns the full path of the entry, based on its type. ++// It validates that the resolved path does not escape the base directory ++// via path traversal (e.g. "../../" sequences in fe.Name). + func (fe *FileEntry) fullPath(cnf *Mycnf) (string, error) { + // find the root to use + var root string +@@ -171,7 +174,7 @@ func (fe *FileEntry) fullPath(cnf *Mycnf) (string, error) { + return "", vterrors.Errorf(vtrpc.Code_UNKNOWN, "unknown base: %v", fe.Base) + } + +- return path.Join(fe.ParentPath, root, fe.Name), nil ++ return fileutil.SafePathJoin(path.Join(fe.ParentPath, root), fe.Name) + } + + // open attempts t oopen the file +diff --git a/go/vt/mysqlctl/builtinbackupengine_test.go b/go/vt/mysqlctl/builtinbackupengine_test.go +index 1d30956..35579bc 100644 +--- a/go/vt/mysqlctl/builtinbackupengine_test.go ++++ b/go/vt/mysqlctl/builtinbackupengine_test.go +@@ -67,6 +67,94 @@ func createBackupFiles(root string, fileCount int, ext string) error { + if _, err := f.Write([]byte("hello, world!")); err != nil { + return err + } ++ ++func TestFileEntryFullPath(t *testing.T) { ++ cnf := &mysqlctl.Mycnf{ ++ DataDir: "/vt/data", ++ InnodbDataHomeDir: "/vt/innodb-data", ++ InnodbLogGroupHomeDir: "/vt/innodb-log", ++ BinLogPath: "/vt/binlogs/mysql-bin", ++ } ++ ++ tests := []struct { ++ name string ++ entry mysqlctl.FileEntry ++ wantPath string ++ wantError error ++ }{ ++ { ++ name: "valid relative path in DataDir", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "mydb/table1.ibd"}, ++ wantPath: "/vt/data/mydb/table1.ibd", ++ }, ++ { ++ name: "valid relative path in InnodbDataHomeDir", ++ entry: mysqlctl.FileEntry{Base: "innodb_data_home", Name: "ibdata1"}, ++ wantPath: "/vt/innodb-data/ibdata1", ++ }, ++ { ++ name: "valid relative path in InnodbLogGroupHomeDir", ++ entry: mysqlctl.FileEntry{Base: "innodb_log_group_home", Name: "ib_logfile0"}, ++ wantPath: "/vt/innodb-log/ib_logfile0", ++ }, ++ { ++ name: "valid relative path in BinlogDir", ++ entry: mysqlctl.FileEntry{Base: "binlog_dir", Name: "mysql-bin.000001"}, ++ wantPath: "/vt/binlogs/mysql-bin.000001", ++ }, ++ { ++ name: "valid path with ParentPath", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "mydb/table1.ibd", ParentPath: "/tmp/restore"}, ++ wantPath: "/tmp/restore/vt/data/mydb/table1.ibd", ++ }, ++ { ++ name: "path traversal escapes base directory", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "../../etc/passwd"}, ++ wantError: fileutil.ErrInvalidJoinedPath, ++ }, ++ { ++ name: "path traversal with deeper nesting", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "mydb/../../../etc/shadow"}, ++ wantError: fileutil.ErrInvalidJoinedPath, ++ }, ++ { ++ name: "path traversal to root", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "../../../../../etc/crontab"}, ++ wantError: fileutil.ErrInvalidJoinedPath, ++ }, ++ { ++ name: "path traversal escapes ParentPath", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "../../../../etc/passwd", ParentPath: "/tmp/restore"}, ++ wantError: fileutil.ErrInvalidJoinedPath, ++ }, ++ { ++ name: "relative path with dot-dot that stays within base", ++ entry: mysqlctl.FileEntry{Base: "data", Name: "mydb/../mydb/table1.ibd"}, ++ wantPath: "/vt/data/mydb/table1.ibd", ++ }, ++ } ++ ++ // Test unknown base separately since it returns a different error type. ++ t.Run("unknown base", func(t *testing.T) { ++ entry := mysqlctl.FileEntry{Base: "unknown", Name: "file"} ++ _, err := entry.fullPath(cnf) ++ require.Error(t, err) ++ assert.Contains(t, err.Error(), "unknown base") ++ }) ++ ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ got, err := tt.entry.fullPath(cnf) ++ if tt.wantError != nil { ++ require.ErrorIs(t, err, tt.wantError) ++ } else { ++ require.NoError(t, err) ++ assert.Equal(t, tt.wantPath, got) ++ } ++ }) ++ } ++} ++ + defer f.Close() + } + +-- +2.45.4 + diff --git a/SPECS/vitess/vitess.spec b/SPECS/vitess/vitess.spec index 6b0125e6386..44dc850be34 100644 --- a/SPECS/vitess/vitess.spec +++ b/SPECS/vitess/vitess.spec @@ -3,7 +3,7 @@ Name: vitess Version: 17.0.7 -Release: 12%{?dist} +Release: 13%{?dist} Summary: Database clustering system for horizontal scaling of MySQL # Upstream license specification: MIT and Apache-2.0 License: MIT and ASL 2.0 @@ -33,6 +33,7 @@ Patch3: CVE-2024-53257.patch Patch4: CVE-2025-22870.patch # CVE-2025-22872 is fixed in go net v0.38 by https://github.com/golang/net/commit/e1fcd82abba34df74614020343be8eb1fe85f0d9 Patch5: CVE-2025-22872.patch +Patch6: CVE-2026-27969.patch BuildRequires: golang %description @@ -102,6 +103,9 @@ go test -v ./go/cmd/... \ %{_bindir}/* %changelog +* Tue Mar 03 2026 Azure Linux Security Servicing Account - 17.0.7-13 +- Patch for CVE-2026-27969 + * Thu Oct 09 2025 Mykhailo Bykhovtsev - 17.0.7-12 - Enable debuginfo subpackage generation