From 4f903598ce68ef07c9069a08d9424487268949db Mon Sep 17 00:00:00 2001 From: Guillaume Da Silva Date: Wed, 18 Mar 2026 12:23:48 +0100 Subject: [PATCH 1/2] fix(admin): validate execution id UUID format before making S3 request --- pkg/download_s3_archive.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/download_s3_archive.go b/pkg/download_s3_archive.go index d6c781d2..f446baa2 100644 --- a/pkg/download_s3_archive.go +++ b/pkg/download_s3_archive.go @@ -10,11 +10,14 @@ import ( "net/http" "os" "path/filepath" + "regexp" "strings" "github.com/qovery/qovery-cli/utils" ) +var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) + type ArchiveTagsResponse struct { Key string Value string @@ -26,13 +29,17 @@ type ArchiveResponse struct { } func DownloadS3Archive(executionId string, directory string) { + if !uuidRegex.MatchString(executionId) { + log.Errorf("Invalid execution id format: '%s'. Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). For cluster execution id be sure to remove the last part (it's a timestamp)", executionId) + return + } + fileName := executionId + ".tgz" res := download(utils.GetAdminUrl()+"/getS3ArchiveObject", fileName) if !strings.Contains(res.Status, "200") { result, _ := io.ReadAll(res.Body) log.Errorf("Could not download archive for key %s: %s. %s", fileName, res.Status, string(result)) - log.Info("For cluster execution id be sure to remove the last part (it's a timestamp)") return } From 501d417764321fc2e2a9882798a886aa63fb8441 Mon Sep 17 00:00:00 2001 From: Guillaume Da Silva Date: Wed, 18 Mar 2026 12:34:13 +0100 Subject: [PATCH 2/2] fix(admin): auto-strip timestamp suffix from execution id instead of failing --- pkg/download_s3_archive.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/download_s3_archive.go b/pkg/download_s3_archive.go index f446baa2..8d7a7f44 100644 --- a/pkg/download_s3_archive.go +++ b/pkg/download_s3_archive.go @@ -17,6 +17,7 @@ import ( ) var uuidRegex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) +var uuidWithTimestampRegex = regexp.MustCompile(`^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-\d+$`) type ArchiveTagsResponse struct { Key string @@ -29,8 +30,11 @@ type ArchiveResponse struct { } func DownloadS3Archive(executionId string, directory string) { - if !uuidRegex.MatchString(executionId) { - log.Errorf("Invalid execution id format: '%s'. Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). For cluster execution id be sure to remove the last part (it's a timestamp)", executionId) + if matches := uuidWithTimestampRegex.FindStringSubmatch(executionId); matches != nil { + log.Warnf("Execution id '%s' contains a timestamp suffix, stripping it automatically", executionId) + executionId = matches[1] + } else if !uuidRegex.MatchString(executionId) { + log.Errorf("Invalid execution id format: '%s'. Expected a UUID (e.g. xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)", executionId) return }