From 0ed3cd714b4168f6a0432405d5e2d7d6e18e0dbb Mon Sep 17 00:00:00 2001 From: Eunan Hardy <25510708+eunanio@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:47:24 +0100 Subject: [PATCH 1/3] Added Env for max decompressed chart size and chart file size limits Address #30738 Allows the user to set the max decompressed chart and file size limits introduced in v3.17.3 via environment variables Signed-off-by: Eunan Hardy <25510708+eunanio@users.noreply.github.com> --- pkg/chart/v2/loader/archive.go | 49 ++++++++++++++++++++++++++++---- pkg/chart/v2/loader/directory.go | 5 ++-- pkg/cli/environment.go | 9 ++++++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/pkg/chart/v2/loader/archive.go b/pkg/chart/v2/loader/archive.go index b9f370f56..609c124aa 100644 --- a/pkg/chart/v2/loader/archive.go +++ b/pkg/chart/v2/loader/archive.go @@ -27,11 +27,15 @@ import ( "os" "path" "regexp" + "strconv" "strings" chart "helm.sh/helm/v4/pkg/chart/v2" ) +const MaxChartFileSizeEnv = "HELM_MAX_DECOMPRESSED_FILE_SIZE" +const MaxChartSizeEnv = "HELM_MAX_DECOMPRESSED_CHART_SIZE" + // MaxDecompressedChartSize is the maximum size of a chart archive that will be // decompressed. This is the decompressed size of all the files. // The default value is 100 MiB. @@ -115,6 +119,34 @@ func isGZipApplication(data []byte) bool { return bytes.HasPrefix(data, sig) } +// getMaxDecompressedChartSize retrieves the maximum size of a decompressed chart from the environment variable +func getMaxDecompressedChartSize() int64 { + chartSizeEnv, ok := os.LookupEnv(MaxChartSizeEnv) + if !ok { + return MaxDecompressedChartSize + } + + maxSize, err := strconv.ParseInt(chartSizeEnv, 10, 64) + if err != nil { + return MaxDecompressedChartSize + } + return maxSize +} + +// getMaxDecompressedFileSize retrieves the maximum size of a decompressed file from the environment variable +func getMaxDecompressedFileSize() int64 { + fileSizeEnv, ok := os.LookupEnv(MaxChartFileSizeEnv) + if !ok { + return MaxDecompressedFileSize + } + + maxSize, err := strconv.ParseInt(fileSizeEnv, 10, 64) + if err != nil { + return MaxDecompressedFileSize + } + return maxSize +} + // LoadArchiveFiles reads in files out of an archive into memory. This function // performs important path security checks and should always be used before // expanding a tarball @@ -127,7 +159,14 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { files := []*BufferedFile{} tr := tar.NewReader(unzipped) - remainingSize := MaxDecompressedChartSize + + // Set the maximum size of the decompressed chart. + maxChartSize := getMaxDecompressedChartSize() + remainingSize := maxChartSize + + // Set the maximum size of a single file in the decompressed chart. + maxChartFileSize := getMaxDecompressedFileSize() + for { b := bytes.NewBuffer(nil) hd, err := tr.Next() @@ -188,11 +227,11 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { } if hd.Size > remainingSize { - return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", MaxDecompressedChartSize) + return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", maxChartSize) } - if hd.Size > MaxDecompressedFileSize { - return nil, fmt.Errorf("decompressed chart file %q is larger than the maximum file size %d", hd.Name, MaxDecompressedFileSize) + if hd.Size > maxChartFileSize { + return nil, fmt.Errorf("decompressed chart file %q is larger than the maximum file size %d", hd.Name, maxChartFileSize) } limitedReader := io.LimitReader(tr, remainingSize) @@ -208,7 +247,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { // is the one that goes over the limit. It assumes the Size stored in the tar header // is correct, something many applications do. if bytesWritten < hd.Size || remainingSize <= 0 { - return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", MaxDecompressedChartSize) + return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", maxChartSize) } data := bytes.TrimPrefix(b.Bytes(), utf8bom) diff --git a/pkg/chart/v2/loader/directory.go b/pkg/chart/v2/loader/directory.go index 4f72925dc..2dfc4cda6 100644 --- a/pkg/chart/v2/loader/directory.go +++ b/pkg/chart/v2/loader/directory.go @@ -99,8 +99,9 @@ func LoadDir(dir string) (*chart.Chart, error) { return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name) } - if fi.Size() > MaxDecompressedFileSize { - return fmt.Errorf("chart file %q is larger than the maximum file size %d", fi.Name(), MaxDecompressedFileSize) + maxChartFileSize := getMaxDecompressedFileSize() + if fi.Size() > maxChartFileSize { + return fmt.Errorf("chart file %q is larger than the maximum file size %d", fi.Name(), maxChartFileSize) } data, err := os.ReadFile(name) diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index 3f2dc00b2..6f0489773 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -89,9 +89,16 @@ type EnvSettings struct { BurstLimit int // QPS is queries per second which may be used to avoid throttling. QPS float32 + // Max DecompressedChartSize is the maximum size of a chart archive that will be decompressed. + MaxDecompressedChartSize int + // MaxDecompressedFileSize is the size of the largest file that Helm will attempt to load. + MaxDecompressedFileSize int } func New() *EnvSettings { + var defaultMaxDecompressedFileSize int = 5 * 1024 * 1024 // Default 5 MiB + var defaultMaxDecompressedChartSize int = 100 * 1024 * 1024 // Default 100 MiB + env := &EnvSettings{ namespace: os.Getenv("HELM_NAMESPACE"), MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), @@ -109,6 +116,8 @@ func New() *EnvSettings { RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit), QPS: envFloat32Or("HELM_QPS", defaultQPS), + MaxDecompressedChartSize: envIntOr("HELM_MAX_DECOMPRESSED_CHART_SIZE", defaultMaxDecompressedChartSize), + MaxDecompressedFileSize: envIntOr("HELM_MAX_DECOMPRESSED_FILE_SIZE", defaultMaxDecompressedFileSize), } env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) From 62eeac1ca482917c0572c2f3562c9d6d63aa706a Mon Sep 17 00:00:00 2001 From: Eunan Hardy <25510708+eunanio@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:16:01 +0100 Subject: [PATCH 2/3] Remove duplication Removed duplication of env. Signed-off-by: Eunan Hardy <25510708+eunanio@users.noreply.github.com> --- pkg/cli/environment.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index 6f0489773..db90de838 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -89,15 +89,9 @@ type EnvSettings struct { BurstLimit int // QPS is queries per second which may be used to avoid throttling. QPS float32 - // Max DecompressedChartSize is the maximum size of a chart archive that will be decompressed. - MaxDecompressedChartSize int - // MaxDecompressedFileSize is the size of the largest file that Helm will attempt to load. - MaxDecompressedFileSize int } func New() *EnvSettings { - var defaultMaxDecompressedFileSize int = 5 * 1024 * 1024 // Default 5 MiB - var defaultMaxDecompressedChartSize int = 100 * 1024 * 1024 // Default 100 MiB env := &EnvSettings{ namespace: os.Getenv("HELM_NAMESPACE"), @@ -116,8 +110,6 @@ func New() *EnvSettings { RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit), QPS: envFloat32Or("HELM_QPS", defaultQPS), - MaxDecompressedChartSize: envIntOr("HELM_MAX_DECOMPRESSED_CHART_SIZE", defaultMaxDecompressedChartSize), - MaxDecompressedFileSize: envIntOr("HELM_MAX_DECOMPRESSED_FILE_SIZE", defaultMaxDecompressedFileSize), } env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) From c2bcfa94017f8e321a9aa06d7466a094507f7191 Mon Sep 17 00:00:00 2001 From: Eunan Hardy <25510708+eunanio@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:17:19 +0100 Subject: [PATCH 3/3] Remove Whitespace Remove Whitespace Signed-off-by: Eunan Hardy <25510708+eunanio@users.noreply.github.com> --- pkg/cli/environment.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index db90de838..3f2dc00b2 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -92,7 +92,6 @@ type EnvSettings struct { } func New() *EnvSettings { - env := &EnvSettings{ namespace: os.Getenv("HELM_NAMESPACE"), MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),