Repository: helm/helm (go)

Issue:
helm 3.6.1: `--pass-credentials` required with urls: ['charts/foobar-0.0.2.tgz']
I've read the [security advisory](https://github.com/helm/helm/security/advisories/GHSA-56hp-xqp3-w2jf).

This works:

```
$ helm repo add --pass-credentials --username 'robot$mph-pull' --password $PASSWORD harbor-dev https://harbor.company.dk/chartrepo/dev
"harbor-dev" has been added to your repositories
$ helm fetch --version 0.0.2 harbor-dev/foobar
(works)
```

But if I don't provide --pass-credentials:

```
$ helm fetch --version 0.0.2 harbor-dev/foobar
Error: failed to fetch https://harbor.lessor.dk/chartrepo/dev/charts/foobar-0.0.2.tgz : 401 Unauthorized
```

When I look at https://harbor.lessor.dk/chartrepo/dev/index.yaml I see this for `foobar`:

```
  foobar:
  - apiVersion: v2
    appVersion: 0.0.1
    created: "2021-06-25T13:37:08.725501745Z"
    description: Foobar
    digest: 61f7daa97ead907afc0f89f5bcdd0021ccc41f3ebf27a4b92e7aa84baa1edc6a
    name: foobar
    type: application
    urls:
    - charts/foobar-0.0.2.tgz
    version: 0.0.2
```

I see nothing here to indicate that
> charts are being served from a different domain than the repository

which is what should require `--pass-credentials`.

In #9857,  @bacongobbler writes:

> The security release discusses a breaking change to the way Helm passes username/password credentials to the server hosting the chart package, listed in the urls field of the repository's index.yaml file. We no longer pass those credentials to upstream servers unless the URL

> 1. Is relative (e.g. /foo-1.0.0.tgz)
> 2. Is served from localhost (e.g. http://localhost/foo-1.0.0.tgz)
> 3. Is served from the same host (e.g. you used helm repo add foo https://foo.com, and the index.yaml referenced https://foo.com/foo-1.0.0.tgz)

A URL like 'charts/foobar-0.0.2.tgz` clearly is relative but also does not match the above list of criteria for "local" URLs. Is that the problem?

Output of `helm version`:

version.BuildInfo{Version:"v3.6.1", GitCommit:"61d8e8c4a6f95540c15c6a65f36a6dd0a45e7a2f", GitTreeState:"clean", GoVersion:"go1.16.5"}


Output of `kubectl version`:

Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.2", GitCommit:"092fbfbf53427de67cac1e9fa54aaa09a28371d7", GitTreeState:"clean", BuildDate:"2021-06-16T12:59:11Z", GoVersion:"go1.16.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.13", GitCommit:"30d651da517185653e34e7ab99a792be6a3d9495", GitTreeState:"clean", BuildDate:"2020-10-15T00:59:17Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}

Cloud Provider/Platform (AKS, GKE, Minikube etc.):

On-premise v1.17 k8s served by nutanix. 




Pull request:
Fix the url being set by WithURL on the getters
The URL passed to the getter for WithURL needs to be a full URL
rather than a chart reference used at the CLI. For example,
bitnami/wordpress can point to the wordpress chart in the bitnami
repo where the bitnami repo is at https://charts.bitnami.com.
WithURL needs the full URL to the repo and not bitnami/wordpress.
This is important because getters use the full URL information.
In this case the http getter uses the host name for SNI handling.

Before this change WithURL was being set to the chart reference
instead of the URL. This was a silent bug.

This change sets WithURL using a URL after for the repo is
available when a reference is used instead of a full url.

Closes #9868

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. Make sure to read the Contributing Guide before submitting your PR: https://github.com/helm/helm/blob/main/CONTRIBUTING.md
2. If this PR closes another issue, add 'closes #<issue number>' somewhere in the PR summary. GitHub will automatically close that issue when this PR gets merged. Alternatively, adding 'refs #<issue number>' will not close the issue, but help provide the reviewer more context.-->

**What this PR does / why we need it**:

**Special notes for your reviewer**:

**If applicable**:
- [ ] this PR contains documentation
- [ ] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility


Patch:
diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go
index 2c0d55a55..575c94151 100644
--- a/pkg/downloader/chart_downloader.go
+++ b/pkg/downloader/chart_downloader.go
@@ -158,7 +158,6 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 	if err != nil {
 		return nil, errors.Errorf("invalid chart URL format: %s", ref)
 	}
-	c.Options = append(c.Options, getter.WithURL(ref))
 
 	rf, err := loadRepoConfig(c.RepositoryConfig)
 	if err != nil {
@@ -177,6 +176,8 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 			// If there is no special config, return the default HTTP client and
 			// swallow the error.
 			if err == ErrNoOwnerRepo {
+				// Make sure to add the ref URL as the URL for the getter
+				c.Options = append(c.Options, getter.WithURL(ref))
 				return u, nil
 			}
 			return u, err
@@ -215,6 +216,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
 		return u, err
 	}
 
+	// Now that we have the chart repository information we can use that URL
+	// to set the URL for the getter.
+	c.Options = append(c.Options, getter.WithURL(rc.URL))
+
 	r, err := repo.NewChartRepository(rc, c.Getters)
 	if err != nil {
 		return u, err


Test patch:
diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go
index 13c994b92..b7349e3d5 100644
--- a/cmd/helm/install_test.go
+++ b/cmd/helm/install_test.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"net/http"
 	"net/http/httptest"
+	"path/filepath"
 	"testing"
 
 	"helm.sh/helm/v3/pkg/repo/repotest"
@@ -48,6 +49,8 @@ func TestInstall(t *testing.T) {
 		t.Fatal(err)
 	}
 
+	repoFile := filepath.Join(srv.Root(), "repositories.yaml")
+
 	tests := []cmdTestCase{
 		// Install, base case
 		{
@@ -244,6 +247,11 @@ func TestInstall(t *testing.T) {
 			cmd:    "install aeneas reqtest --namespace default --repo " + srv2.URL + " --username username --password password --pass-credentials",
 			golden: "output/install.txt",
 		},
+		{
+			name:   "basic install with credentials and no repo",
+			cmd:    fmt.Sprintf("install aeneas test/reqtest --username username --password password --repository-config %s --repository-cache %s", repoFile, srv.Root()),
+			golden: "output/install.txt",
+		},
 	}
 
 	runTestActionCmd(t, tests)