VYPR
Moderate severityNVD Advisory· Published Feb 14, 2024· Updated Aug 26, 2024

Dependency management path traversal in helm

CVE-2024-25620

Description

Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources. When either the Helm client or SDK is used to save a chart whose name within the Chart.yaml file includes a relative path change, the chart would be saved outside its expected directory based on the changes in the relative path. The validation and linting did not detect the path changes in the name. This issue has been resolved in Helm v3.14.1. Users unable to upgrade should check all charts used by Helm for path changes in their name as found in the Chart.yaml file. This includes dependencies.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
helm.sh/helm/v3Go
< 3.14.13.14.1

Affected products

1

Patches

1
0d0f91d1ce27

Merge pull request from GHSA-v53g-5gjp-272r

https://github.com/helm/helmMatt FarinaFeb 14, 2024via ghsa
10 files changed · +112 0
  • pkg/chart/metadata.go+6 0 modified
    @@ -16,6 +16,7 @@ limitations under the License.
     package chart
     
     import (
    +	"path/filepath"
     	"strings"
     	"unicode"
     
    @@ -110,6 +111,11 @@ func (md *Metadata) Validate() error {
     	if md.Name == "" {
     		return ValidationError("chart.metadata.name is required")
     	}
    +
    +	if md.Name != filepath.Base(md.Name) {
    +		return ValidationErrorf("chart.metadata.name %q is invalid", md.Name)
    +	}
    +
     	if md.Version == "" {
     		return ValidationError("chart.metadata.version is required")
     	}
    
  • pkg/chart/metadata_test.go+5 0 modified
    @@ -40,6 +40,11 @@ func TestValidate(t *testing.T) {
     			&Metadata{APIVersion: "v2", Version: "1.0"},
     			ValidationError("chart.metadata.name is required"),
     		},
    +		{
    +			"chart without name",
    +			&Metadata{Name: "../../test", APIVersion: "v2", Version: "1.0"},
    +			ValidationError("chart.metadata.name \"../../test\" is invalid"),
    +		},
     		{
     			"chart without version",
     			&Metadata{Name: "test", APIVersion: "v2"},
    
  • pkg/chartutil/errors.go+8 0 modified
    @@ -33,3 +33,11 @@ type ErrNoValue struct {
     }
     
     func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e.Key) }
    +
    +type ErrInvalidChartName struct {
    +	Name string
    +}
    +
    +func (e ErrInvalidChartName) Error() string {
    +	return fmt.Sprintf("%q is not a valid chart name", e.Name)
    +}
    
  • pkg/chartutil/save.go+20 0 modified
    @@ -39,6 +39,10 @@ var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
     // directory, writing the chart's contents to that subdirectory.
     func SaveDir(c *chart.Chart, dest string) error {
     	// Create the chart directory
    +	err := validateName(c.Name())
    +	if err != nil {
    +		return err
    +	}
     	outdir := filepath.Join(dest, c.Name())
     	if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {
     		return errors.Errorf("file %s already exists and is not a directory", outdir)
    @@ -149,6 +153,10 @@ func Save(c *chart.Chart, outDir string) (string, error) {
     }
     
     func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
    +	err := validateName(c.Name())
    +	if err != nil {
    +		return err
    +	}
     	base := filepath.Join(prefix, c.Name())
     
     	// Pull out the dependencies of a v1 Chart, since there's no way
    @@ -242,3 +250,15 @@ func writeToTar(out *tar.Writer, name string, body []byte) error {
     	_, err := out.Write(body)
     	return err
     }
    +
    +// If the name has directory name has characters which would change the location
    +// they need to be removed.
    +func validateName(name string) error {
    +	nname := filepath.Base(name)
    +
    +	if nname != name {
    +		return ErrInvalidChartName{name}
    +	}
    +
    +	return nil
    +}
    
  • pkg/chartutil/save_test.go+29 0 modified
    @@ -106,6 +106,24 @@ func TestSave(t *testing.T) {
     			}
     		})
     	}
    +
    +	c := &chart.Chart{
    +		Metadata: &chart.Metadata{
    +			APIVersion: chart.APIVersionV1,
    +			Name:       "../ahab",
    +			Version:    "1.2.3",
    +		},
    +		Lock: &chart.Lock{
    +			Digest: "testdigest",
    +		},
    +		Files: []*chart.File{
    +			{Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")},
    +		},
    +	}
    +	_, err := Save(c, tmp)
    +	if err == nil {
    +		t.Fatal("Expected error saving chart with invalid name")
    +	}
     }
     
     // Creates a copy with a different schema; does not modify anything.
    @@ -232,4 +250,15 @@ func TestSaveDir(t *testing.T) {
     	if len(c2.Files) != 1 || c2.Files[0].Name != c.Files[0].Name {
     		t.Fatal("Files data did not match")
     	}
    +
    +	tmp2 := t.TempDir()
    +	c.Metadata.Name = "../ahab"
    +	pth := filepath.Join(tmp2, "tmpcharts")
    +	if err := os.MkdirAll(filepath.Join(pth), 0755); err != nil {
    +		t.Fatal(err)
    +	}
    +
    +	if err := SaveDir(c, pth); err.Error() != "\"../ahab\" is not a valid chart name" {
    +		t.Fatalf("Did not get expected error for chart named %q", c.Name())
    +	}
     }
    
  • pkg/downloader/manager_test.go+26 0 modified
    @@ -262,6 +262,32 @@ func TestDownloadAll(t *testing.T) {
     	if _, err := os.Stat(filepath.Join(chartPath, "charts", "signtest-0.1.0.tgz")); os.IsNotExist(err) {
     		t.Error(err)
     	}
    +
    +	// A chart with a bad name like this cannot be loaded and saved. Handling in
    +	// the loading and saving will return an error about the invalid name. In
    +	// this case, the chart needs to be created directly.
    +	badchartyaml := `apiVersion: v2
    +description: A Helm chart for Kubernetes
    +name: ../bad-local-subchart
    +version: 0.1.0`
    +	if err := os.MkdirAll(filepath.Join(chartPath, "testdata", "bad-local-subchart"), 0755); err != nil {
    +		t.Fatal(err)
    +	}
    +	err = os.WriteFile(filepath.Join(chartPath, "testdata", "bad-local-subchart", "Chart.yaml"), []byte(badchartyaml), 0644)
    +	if err != nil {
    +		t.Fatal(err)
    +	}
    +
    +	badLocalDep := &chart.Dependency{
    +		Name:       "../bad-local-subchart",
    +		Repository: "file://./testdata/bad-local-subchart",
    +		Version:    "0.1.0",
    +	}
    +
    +	err = m.downloadAll([]*chart.Dependency{badLocalDep})
    +	if err == nil {
    +		t.Fatal("Expected error for bad dependency name")
    +	}
     }
     
     func TestUpdateBeforeBuild(t *testing.T) {
    
  • pkg/lint/rules/chartfile.go+4 0 modified
    @@ -106,6 +106,10 @@ func validateChartName(cf *chart.Metadata) error {
     	if cf.Name == "" {
     		return errors.New("name is required")
     	}
    +	name := filepath.Base(cf.Name)
    +	if name != cf.Name {
    +		return fmt.Errorf("chart name %q is invalid", cf.Name)
    +	}
     	return nil
     }
     
    
  • pkg/lint/rules/chartfile_test.go+8 0 modified
    @@ -30,16 +30,19 @@ import (
     )
     
     const (
    +	badCharNametDir    = "testdata/badchartname"
     	badChartDir        = "testdata/badchartfile"
     	anotherBadChartDir = "testdata/anotherbadchartfile"
     )
     
     var (
    +	badChartNamePath         = filepath.Join(badCharNametDir, "Chart.yaml")
     	badChartFilePath         = filepath.Join(badChartDir, "Chart.yaml")
     	nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml")
     )
     
     var badChart, _ = chartutil.LoadChartfile(badChartFilePath)
    +var badChartName, _ = chartutil.LoadChartfile(badChartNamePath)
     
     // Validation functions Test
     func TestValidateChartYamlNotDirectory(t *testing.T) {
    @@ -69,6 +72,11 @@ func TestValidateChartName(t *testing.T) {
     	if err == nil {
     		t.Errorf("validateChartName to return a linter error, got no error")
     	}
    +
    +	err = validateChartName(badChartName)
    +	if err == nil {
    +		t.Error("expected validateChartName to return a linter error for an invalid name, got no error")
    +	}
     }
     
     func TestValidateChartVersion(t *testing.T) {
    
  • pkg/lint/rules/testdata/badchartname/Chart.yaml+5 0 added
    @@ -0,0 +1,5 @@
    +apiVersion: v2
    +description: A Helm chart for Kubernetes
    +version: 0.1.0
    +name: "../badchartname"
    +type: application
    
  • pkg/lint/rules/testdata/badchartname/values.yaml+1 0 added
    @@ -0,0 +1 @@
    +# Default values for badchartfile.
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.