From 8e6a5149d2e2e3beffa51d53048b2fed90d8c529 Mon Sep 17 00:00:00 2001
From: Matt Farina <matt.farina@suse.com>
Date: Wed, 7 Feb 2024 10:54:15 -0500
Subject: [PATCH] validation fix

Signed-off-by: Matt Farina <matt.farina@suse.com>
---
 pkg/chart/metadata.go                         |  6 ++++
 pkg/chart/metadata_test.go                    |  5 ++++
 pkg/chartutil/errors.go                       |  8 +++++
 pkg/chartutil/save.go                         | 20 +++++++++++++
 pkg/chartutil/save_test.go                    | 29 +++++++++++++++++++
 pkg/downloader/manager_test.go                | 26 +++++++++++++++++
 pkg/lint/rules/chartfile.go                   |  4 +++
 pkg/lint/rules/chartfile_test.go              |  8 +++++
 .../rules/testdata/badchartname/Chart.yaml    |  5 ++++
 .../rules/testdata/badchartname/values.yaml   |  1 +
 10 files changed, 112 insertions(+)
 create mode 100644 pkg/lint/rules/testdata/badchartname/Chart.yaml
 create mode 100644 pkg/lint/rules/testdata/badchartname/values.yaml

Index: helm-3.13.3/pkg/chart/metadata.go
===================================================================
--- helm-3.13.3.orig/pkg/chart/metadata.go
+++ helm-3.13.3/pkg/chart/metadata.go
@@ -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")
 	}
Index: helm-3.13.3/pkg/chart/metadata_test.go
===================================================================
--- helm-3.13.3.orig/pkg/chart/metadata_test.go
+++ helm-3.13.3/pkg/chart/metadata_test.go
@@ -41,6 +41,10 @@ func TestValidate(t *testing.T) {
 			ValidationError("chart.metadata.version is required"),
 		},
 		{
+			&Metadata{Name: "../../test", APIVersion: "v2", Version: "1.0"},
+			ValidationError("chart.metadata.name \"../../test\" is invalid"),
+		},
+		{
 			&Metadata{Name: "test", APIVersion: "v2", Version: "1.0", Type: "test"},
 			ValidationError("chart.metadata.type must be application or library"),
 		},
Index: helm-3.13.3/pkg/chartutil/errors.go
===================================================================
--- helm-3.13.3.orig/pkg/chartutil/errors.go
+++ helm-3.13.3/pkg/chartutil/errors.go
@@ -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)
+}
Index: helm-3.13.3/pkg/chartutil/save.go
===================================================================
--- helm-3.13.3.orig/pkg/chartutil/save.go
+++ helm-3.13.3/pkg/chartutil/save.go
@@ -39,6 +39,10 @@ var headerBytes = []byte("+aHR0cHM6Ly95b
 // 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)
 }
 
 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 st
 	_, 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
+}
Index: helm-3.13.3/pkg/chartutil/save_test.go
===================================================================
--- helm-3.13.3.orig/pkg/chartutil/save_test.go
+++ helm-3.13.3/pkg/chartutil/save_test.go
@@ -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())
+	}
 }
Index: helm-3.13.3/pkg/downloader/manager_test.go
===================================================================
--- helm-3.13.3.orig/pkg/downloader/manager_test.go
+++ helm-3.13.3/pkg/downloader/manager_test.go
@@ -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) {
Index: helm-3.13.3/pkg/lint/rules/chartfile.go
===================================================================
--- helm-3.13.3.orig/pkg/lint/rules/chartfile.go
+++ helm-3.13.3/pkg/lint/rules/chartfile.go
@@ -106,6 +106,10 @@ func validateChartName(cf *chart.Metadat
 	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
 }
 
Index: helm-3.13.3/pkg/lint/rules/chartfile_test.go
===================================================================
--- helm-3.13.3.orig/pkg/lint/rules/chartfile_test.go
+++ helm-3.13.3/pkg/lint/rules/chartfile_test.go
@@ -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) {
Index: helm-3.13.3/pkg/lint/rules/testdata/badchartname/Chart.yaml
===================================================================
--- /dev/null
+++ helm-3.13.3/pkg/lint/rules/testdata/badchartname/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v2
+description: A Helm chart for Kubernetes
+version: 0.1.0
+name: "../badchartname"
+type: application
Index: helm-3.13.3/pkg/lint/rules/testdata/badchartname/values.yaml
===================================================================
--- /dev/null
+++ helm-3.13.3/pkg/lint/rules/testdata/badchartname/values.yaml
@@ -0,0 +1 @@
+# Default values for badchartfile.
