Repository: helm/helm (go)

Issue:
Helm repo add fails with some repositories
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->

`helm repo add` fails with certain repositories when using Helm v3.3.2. The issue does not occur with v3.3.1 or earlier versions.

Example:

```
helm repo add jetstack https://charts.jetstack.io 
Error: looks like "https://charts.jetstack.io" is not a valid chart repository or cannot be reached: error unmarshaling JSON: while decoding JSON: json: unknown field "serverInfo"
```
The issue does not occur with every repository. For example, `https://kubernetes-charts.storage.googleapis.com/` can still be added:

```
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
"stable" has been added to your repositories
```

Output of `helm version`:
```
version.BuildInfo{Version:"v3.3.2", GitCommit:"e5077257b6ca106d1f65652b4ca994736d221ab1", GitTreeState:"clean", GoVersion:"go1.14.9"}
```

Output of `kubectl version`:
```
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.6", GitCommit:"dff82dc0de47299ab66c83c626e08b245ab19037", GitTreeState:"clean", BuildDate:"2020-07-15T16:58:53Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.6", GitCommit:"dff82dc0de47299ab66c83c626e08b245ab19037", GitTreeState:"clean", BuildDate:"2020-07-20T00:20:54Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
```

Cloud Provider/Platform (AKS, GKE, Minikube etc.):

The issue was observed on both KIND and OpenStack clusters.




Pull request:
fix: allow serverInfo field on index files
A recent change merged into Helm fixes a number of security issues related to parsing malformed index files. Unfortunately, it also broke the ability for users to load index files from chartmuseum, which adds a "server info" field to add additional metadata.

This commit adds that field so that index files from chartmuseum can be validated. Since Helm does not use this field for anything, the information is discarded and unused.

closes #8761.

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>

Patch:
diff --git a/pkg/repo/index.go b/pkg/repo/index.go
index 8b831029f..55b984eea 100644
--- a/pkg/repo/index.go
+++ b/pkg/repo/index.go
@@ -77,6 +77,8 @@ func (c ChartVersions) Less(a, b int) bool {
 
 // IndexFile represents the index file in a chart repository
 type IndexFile struct {
+	// This is used ONLY for validation against chartmuseum's index files and is discarded after validation.
+	ServerInfo map[string]interface{}   `json:"serverInfo,omitempty"`
 	APIVersion string                   `json:"apiVersion"`
 	Generated  time.Time                `json:"generated"`
 	Entries    map[string]ChartVersions `json:"entries"`


Test patch:
diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go
index 77b3a90ab..c22588971 100644
--- a/pkg/repo/index_test.go
+++ b/pkg/repo/index_test.go
@@ -35,9 +35,31 @@ import (
 )
 
 const (
-	testfile          = "testdata/local-index.yaml"
-	unorderedTestfile = "testdata/local-index-unordered.yaml"
-	testRepo          = "test-repo"
+	testfile            = "testdata/local-index.yaml"
+	chartmuseumtestfile = "testdata/chartmuseum-index.yaml"
+	unorderedTestfile   = "testdata/local-index-unordered.yaml"
+	testRepo            = "test-repo"
+	indexWithDuplicates = `
+apiVersion: v1
+entries:
+  nginx:
+    - urls:
+        - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz
+      name: nginx
+      description: string
+      version: 0.2.0
+      home: https://github.com/something/else
+      digest: "sha256:1234567890abcdef"
+  nginx:
+    - urls:
+        - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz
+        - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz
+      name: alpine
+      description: string
+      version: 1.0.0
+      home: https://github.com/something
+      digest: "sha256:1234567890abcdef"
+`
 )
 
 func TestIndexFile(t *testing.T) {
@@ -84,39 +106,38 @@ func TestIndexFile(t *testing.T) {
 }
 
 func TestLoadIndex(t *testing.T) {
-	b, err := ioutil.ReadFile(testfile)
-	if err != nil {
-		t.Fatal(err)
+
+	tests := []struct {
+		Name     string
+		Filename string
+	}{
+		{
+			Name:     "regular index file",
+			Filename: testfile,
+		},
+		{
+			Name:     "chartmuseum index file",
+			Filename: chartmuseumtestfile,
+		},
 	}
-	i, err := loadIndex(b)
-	if err != nil {
-		t.Fatal(err)
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.Name, func(t *testing.T) {
+			t.Parallel()
+			b, err := ioutil.ReadFile(tc.Filename)
+			if err != nil {
+				t.Fatal(err)
+			}
+			i, err := loadIndex(b)
+			if err != nil {
+				t.Fatal(err)
+			}
+			verifyLocalIndex(t, i)
+		})
 	}
-	verifyLocalIndex(t, i)
 }
 
-const indexWithDuplicates = `
-apiVersion: v1
-entries:
-  nginx:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz
-      name: nginx
-      description: string
-      version: 0.2.0
-      home: https://github.com/something/else
-      digest: "sha256:1234567890abcdef"
-  nginx:
-    - urls:
-        - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz
-        - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz
-      name: alpine
-      description: string
-      version: 1.0.0
-      home: https://github.com/something
-      digest: "sha256:1234567890abcdef"
-`
-
 // TestLoadIndex_Duplicates is a regression to make sure that we don't non-deterministically allow duplicate packages.
 func TestLoadIndex_Duplicates(t *testing.T) {
 	if _, err := loadIndex([]byte(indexWithDuplicates)); err == nil {
diff --git a/pkg/repo/testdata/chartmuseum-index.yaml b/pkg/repo/testdata/chartmuseum-index.yaml
new file mode 100644
index 000000000..3077596f4
--- /dev/null
+++ b/pkg/repo/testdata/chartmuseum-index.yaml
@@ -0,0 +1,50 @@
+serverInfo:
+  contextPath: /v1/helm
+apiVersion: v1
+entries:
+  nginx:
+    - urls:
+        - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz
+      name: nginx
+      description: string
+      version: 0.2.0
+      home: https://github.com/something/else
+      digest: "sha256:1234567890abcdef"
+      keywords:
+        - popular
+        - web server
+        - proxy
+    - urls:
+        - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz
+      name: nginx
+      description: string
+      version: 0.1.0
+      home: https://github.com/something
+      digest: "sha256:1234567890abcdef"
+      keywords:
+        - popular
+        - web server
+        - proxy
+  alpine:
+    - urls:
+        - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz
+        - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz
+      name: alpine
+      description: string
+      version: 1.0.0
+      home: https://github.com/something
+      keywords:
+        - linux
+        - alpine
+        - small
+        - sumtin
+      digest: "sha256:1234567890abcdef"
+  chartWithNoURL:
+    - name: chartWithNoURL
+      description: string
+      version: 1.0.0
+      home: https://github.com/something
+      keywords:
+        - small
+        - sumtin
+      digest: "sha256:1234567890abcdef"