diff --git a/SPECS/vitess/CVE-2026-27965.patch b/SPECS/vitess/CVE-2026-27965.patch new file mode 100644 index 00000000000..186ef668650 --- /dev/null +++ b/SPECS/vitess/CVE-2026-27965.patch @@ -0,0 +1,246 @@ +From 33a0a5658e79a1226e372630a77ac6d63e8cb0d2 Mon Sep 17 00:00:00 2001 +From: Tim Vaillancourt +Date: Tue, 24 Feb 2026 20:21:37 +0100 +Subject: [PATCH] Restore: make loading compressor commands from `MANIFEST` + opt-in (#19460) + +Signed-off-by: Tim Vaillancourt +Co-authored-by: Mohamed Hamza +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/vitessio/vitess/commit/4c0173293907af9cb942a6683c465c3f1e9fdb5c.patch +--- + .../backup/vtctlbackup/backup_test.go | 1 + + .../backup/vtctlbackup/backup_utils.go | 4 + + .../backup/xtrabackup/xtrabackup_test.go | 1 + + go/vt/mysqlctl/builtinbackupengine.go | 5 +- + go/vt/mysqlctl/compression.go | 18 +++- + .../compression_external_decompressor_test.go | 93 +++++++++++++++++++ + go/vt/mysqlctl/xtrabackupengine.go | 5 +- + 7 files changed, 118 insertions(+), 9 deletions(-) + create mode 100644 go/vt/mysqlctl/compression_external_decompressor_test.go + +diff --git a/go/test/endtoend/backup/vtctlbackup/backup_test.go b/go/test/endtoend/backup/vtctlbackup/backup_test.go +index 92c7a2f..154f2d1 100644 +--- a/go/test/endtoend/backup/vtctlbackup/backup_test.go ++++ b/go/test/endtoend/backup/vtctlbackup/backup_test.go +@@ -57,6 +57,7 @@ func TestBuiltinBackupWithExternalZstdCompressionAndManifestedDecompressor(t *te + CompressorEngineName: "external", + ExternalCompressorCmd: "zstd", + ExternalCompressorExt: ".zst", ++ ExternalDecompressorUseManifest: true, + ManifestExternalDecompressorCmd: "zstd -d", + } + +diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go +index a70d180..3823eef 100644 +--- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go ++++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go +@@ -97,6 +97,7 @@ type CompressionDetails struct { + ExternalCompressorCmd string + ExternalCompressorExt string + ExternalDecompressorCmd string ++ ExternalDecompressorUseManifest bool + ManifestExternalDecompressorCmd string + } + +@@ -287,6 +288,9 @@ func getCompressorArgs(cDetails *CompressionDetails) []string { + if cDetails.ExternalDecompressorCmd != "" { + args = append(args, fmt.Sprintf("--external-decompressor=%s", cDetails.ExternalDecompressorCmd)) + } ++ if cDetails.ExternalDecompressorUseManifest { ++ args = append(args, "--external-decompressor-use-manifest") ++ } + if cDetails.ManifestExternalDecompressorCmd != "" { + args = append(args, fmt.Sprintf("--manifest-external-decompressor=%s", cDetails.ManifestExternalDecompressorCmd)) + } +diff --git a/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go b/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go +index 3402a17..e18f229 100644 +--- a/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go ++++ b/go/test/endtoend/backup/xtrabackup/xtrabackup_test.go +@@ -59,6 +59,7 @@ func TestXtrabackupWithExternalZstdCompressionAndManifestedDecompressor(t *testi + CompressorEngineName: "external", + ExternalCompressorCmd: "zstd", + ExternalCompressorExt: ".zst", ++ ExternalDecompressorUseManifest: true, + ManifestExternalDecompressorCmd: "zstd -d", + } + +diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go +index 94ed7bd..fcf7521 100644 +--- a/go/vt/mysqlctl/builtinbackupengine.go ++++ b/go/vt/mysqlctl/builtinbackupengine.go +@@ -1120,10 +1120,7 @@ func (be *BuiltinBackupEngine) restoreFile(ctx context.Context, params RestorePa + // for backward compatibility + deCompressionEngine = PgzipCompressor + } +- externalDecompressorCmd := ExternalDecompressorCmd +- if externalDecompressorCmd == "" && bm.ExternalDecompressor != "" { +- externalDecompressorCmd = bm.ExternalDecompressor +- } ++ externalDecompressorCmd := resolveExternalDecompressor(bm.ExternalDecompressor) + if externalDecompressorCmd != "" { + if deCompressionEngine == ExternalCompressor { + deCompressionEngine = externalDecompressorCmd +diff --git a/go/vt/mysqlctl/compression.go b/go/vt/mysqlctl/compression.go +index c2d3cbb..5737b70 100644 +--- a/go/vt/mysqlctl/compression.go ++++ b/go/vt/mysqlctl/compression.go +@@ -52,9 +52,10 @@ var ( + ExternalCompressorCmd string + ExternalCompressorExt string + ExternalDecompressorCmd string ++ ExternalDecompressorUseManifest bool + ManifestExternalDecompressorCmd string + +- errUnsupportedDeCompressionEngine = errors.New("unsupported engine in MANIFEST. You need to provide --external-decompressor if using 'external' compression engine") ++ errUnsupportedDeCompressionEngine = errors.New("unsupported engine in MANIFEST. You need to provide --external-decompressor if using 'external' compression engine. Alternatively, set --external-decompressor-use-manifest to use the decompressor command from the backup manifest, but this is NOT RECOMMENDED as it is a security risk") + errUnsupportedCompressionEngine = errors.New("unsupported engine value for --compression-engine-name. supported values are 'external', 'pgzip', 'pargzip', 'zstd', 'lz4'") + + // this is used by getEngineFromExtension() to figure out which engine to use in case the user didn't specify +@@ -77,6 +78,7 @@ func registerBackupCompressionFlags(fs *pflag.FlagSet) { + fs.StringVar(&ExternalCompressorCmd, "external-compressor", ExternalCompressorCmd, "command with arguments to use when compressing a backup.") + fs.StringVar(&ExternalCompressorExt, "external-compressor-extension", ExternalCompressorExt, "extension to use when using an external compressor.") + fs.StringVar(&ExternalDecompressorCmd, "external-decompressor", ExternalDecompressorCmd, "command with arguments to use when decompressing a backup.") ++ fs.BoolVar(&ExternalDecompressorUseManifest, "external-decompressor-use-manifest", ExternalDecompressorUseManifest, "allows the decompressor command stored in the backup manifest to be used at restore time. Enabling this is a security risk: an attacker with write access to the backup storage could modify the manifest to execute arbitrary commands on the tablet as the Vitess user. NOT RECOMMENDED.") + fs.StringVar(&ManifestExternalDecompressorCmd, "manifest-external-decompressor", ManifestExternalDecompressorCmd, "command with arguments to store in the backup manifest when compressing a backup with an external compression engine.") + } + +@@ -91,6 +93,20 @@ func getExtensionFromEngine(engine string) (string, error) { + return "", fmt.Errorf("%w %q", errUnsupportedCompressionEngine, engine) + } + ++// resolveExternalDecompressor returns the external decompressor command to use ++// at restore time. The CLI flag (--external-decompressor) takes precedence. The ++// backup manifest value is only used when --external-decompressor-use-manifest ++// is explicitly set to true. ++func resolveExternalDecompressor(manifestDecompressor string) string { ++ if ExternalDecompressorCmd != "" { ++ return ExternalDecompressorCmd ++ } ++ if ExternalDecompressorUseManifest && manifestDecompressor != "" { ++ return manifestDecompressor ++ } ++ return "" ++} ++ + // Validates if the external decompressor exists and return its path. + func validateExternalCmd(cmd string) (string, error) { + if cmd == "" { +diff --git a/go/vt/mysqlctl/compression_external_decompressor_test.go b/go/vt/mysqlctl/compression_external_decompressor_test.go +new file mode 100644 +index 0000000..fd4732a +--- /dev/null ++++ b/go/vt/mysqlctl/compression_external_decompressor_test.go +@@ -0,0 +1,93 @@ ++/* ++Copyright 2026 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 mysqlctl ++ ++import ( ++ "testing" ++ ++ "github.com/stretchr/testify/assert" ++) ++ ++func TestResolveExternalDecompressor(t *testing.T) { ++ tests := []struct { ++ name string ++ cliDecompressorCmd string ++ useManifest bool ++ manifestDecompressor string ++ expected string ++ }{ ++ { ++ name: "CLI flag takes precedence over manifest", ++ cliDecompressorCmd: "zstd -d", ++ useManifest: true, ++ manifestDecompressor: "gzip -d", ++ expected: "zstd -d", ++ }, ++ { ++ name: "CLI flag takes precedence even when use-manifest is false", ++ cliDecompressorCmd: "zstd -d", ++ useManifest: false, ++ manifestDecompressor: "gzip -d", ++ expected: "zstd -d", ++ }, ++ { ++ name: "manifest used when use-manifest is true and no CLI flag", ++ cliDecompressorCmd: "", ++ useManifest: true, ++ manifestDecompressor: "gzip -d", ++ expected: "gzip -d", ++ }, ++ { ++ name: "manifest ignored when use-manifest is false", ++ cliDecompressorCmd: "", ++ useManifest: false, ++ manifestDecompressor: "gzip -d", ++ expected: "", ++ }, ++ { ++ name: "empty when nothing is set", ++ cliDecompressorCmd: "", ++ useManifest: false, ++ manifestDecompressor: "", ++ expected: "", ++ }, ++ { ++ name: "empty when use-manifest is true but manifest is empty", ++ cliDecompressorCmd: "", ++ useManifest: true, ++ manifestDecompressor: "", ++ expected: "", ++ }, ++ } ++ ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ origCmd := ExternalDecompressorCmd ++ origAllow := ExternalDecompressorUseManifest ++ t.Cleanup(func() { ++ ExternalDecompressorCmd = origCmd ++ ExternalDecompressorUseManifest = origAllow ++ }) ++ ++ ExternalDecompressorCmd = tt.cliDecompressorCmd ++ ExternalDecompressorUseManifest = tt.useManifest ++ ++ result := resolveExternalDecompressor(tt.manifestDecompressor) ++ assert.Equal(t, tt.expected, result) ++ }) ++ } ++} +diff --git a/go/vt/mysqlctl/xtrabackupengine.go b/go/vt/mysqlctl/xtrabackupengine.go +index 3f8491f..7552a90 100644 +--- a/go/vt/mysqlctl/xtrabackupengine.go ++++ b/go/vt/mysqlctl/xtrabackupengine.go +@@ -644,10 +644,7 @@ func (be *XtrabackupEngine) extractFiles(ctx context.Context, logger logutil.Log + // then we assign the default value of compressionEngine. + deCompressionEngine = PgzipCompressor + } +- externalDecompressorCmd := ExternalDecompressorCmd +- if externalDecompressorCmd == "" && bm.ExternalDecompressor != "" { +- externalDecompressorCmd = bm.ExternalDecompressor +- } ++ externalDecompressorCmd := resolveExternalDecompressor(bm.ExternalDecompressor) + if externalDecompressorCmd != "" { + if deCompressionEngine == ExternalCompressor { + deCompressionEngine = externalDecompressorCmd +-- +2.45.4 + diff --git a/SPECS/vitess/vitess.spec b/SPECS/vitess/vitess.spec index 1667b050041..8cfd46b957c 100644 --- a/SPECS/vitess/vitess.spec +++ b/SPECS/vitess/vitess.spec @@ -3,7 +3,7 @@ Name: vitess Version: 19.0.4 -Release: 7%{?dist} +Release: 8%{?dist} Summary: Database clustering system for horizontal scaling of MySQL # Upstream license specification: MIT and Apache-2.0 License: MIT and ASL 2.0 @@ -31,6 +31,7 @@ Patch1: CVE-2024-45339.patch Patch2: CVE-2025-22868.patch Patch3: CVE-2025-22870.patch Patch4: CVE-2024-53257.patch +Patch5: CVE-2026-27965.patch BuildRequires: golang < 1.23 %description @@ -108,6 +109,9 @@ go check -t go/cmd \ %{_bindir}/* %changelog +* Thu Feb 26 2026 Azure Linux Security Servicing Account - 19.0.4-8 +- Patch for CVE-2026-27965 + * Fri Apr 11 2025 Kevin Lockwood - 19.0.4-7 - Add patch for CVE-2024-53257