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>
pull/30995/head
Eunan Hardy 3 months ago
parent 8e69436435
commit 0ed3cd714b
No known key found for this signature in database
GPG Key ID: 35158A267E7F34B4

@ -27,11 +27,15 @@ import (
"os" "os"
"path" "path"
"regexp" "regexp"
"strconv"
"strings" "strings"
chart "helm.sh/helm/v4/pkg/chart/v2" 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 // MaxDecompressedChartSize is the maximum size of a chart archive that will be
// decompressed. This is the decompressed size of all the files. // decompressed. This is the decompressed size of all the files.
// The default value is 100 MiB. // The default value is 100 MiB.
@ -115,6 +119,34 @@ func isGZipApplication(data []byte) bool {
return bytes.HasPrefix(data, sig) 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 // LoadArchiveFiles reads in files out of an archive into memory. This function
// performs important path security checks and should always be used before // performs important path security checks and should always be used before
// expanding a tarball // expanding a tarball
@ -127,7 +159,14 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
files := []*BufferedFile{} files := []*BufferedFile{}
tr := tar.NewReader(unzipped) 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 { for {
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
hd, err := tr.Next() hd, err := tr.Next()
@ -188,11 +227,11 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
} }
if hd.Size > remainingSize { 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 { if hd.Size > maxChartFileSize {
return nil, fmt.Errorf("decompressed chart file %q is larger than the maximum file size %d", hd.Name, MaxDecompressedFileSize) return nil, fmt.Errorf("decompressed chart file %q is larger than the maximum file size %d", hd.Name, maxChartFileSize)
} }
limitedReader := io.LimitReader(tr, remainingSize) 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 the one that goes over the limit. It assumes the Size stored in the tar header
// is correct, something many applications do. // is correct, something many applications do.
if bytesWritten < hd.Size || remainingSize <= 0 { 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) data := bytes.TrimPrefix(b.Bytes(), utf8bom)

@ -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) return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name)
} }
if fi.Size() > MaxDecompressedFileSize { maxChartFileSize := getMaxDecompressedFileSize()
return fmt.Errorf("chart file %q is larger than the maximum file size %d", fi.Name(), MaxDecompressedFileSize) 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) data, err := os.ReadFile(name)

@ -89,9 +89,16 @@ type EnvSettings struct {
BurstLimit int BurstLimit int
// QPS is queries per second which may be used to avoid throttling. // QPS is queries per second which may be used to avoid throttling.
QPS float32 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 { func New() *EnvSettings {
var defaultMaxDecompressedFileSize int = 5 * 1024 * 1024 // Default 5 MiB
var defaultMaxDecompressedChartSize int = 100 * 1024 * 1024 // Default 100 MiB
env := &EnvSettings{ env := &EnvSettings{
namespace: os.Getenv("HELM_NAMESPACE"), namespace: os.Getenv("HELM_NAMESPACE"),
MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory), MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
@ -109,6 +116,8 @@ func New() *EnvSettings {
RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")),
BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit), BurstLimit: envIntOr("HELM_BURST_LIMIT", defaultBurstLimit),
QPS: envFloat32Or("HELM_QPS", defaultQPS), 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")) env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG"))

Loading…
Cancel
Save