Repository credentials passed to alternate domain
Description
Helm is a tool for managing Charts (packages of pre-configured Kubernetes resources). In versions of helm prior to 3.6.1, a vulnerability exists where the username and password credentials associated with a Helm repository could be passed on to another domain referenced by that Helm repository. This issue has been resolved in 3.6.1. There is a workaround through which one may check for improperly passed credentials. One may use a username and password for a Helm repository and may audit the Helm repository in order to check for another domain being used that could have received the credentials. In the index.yaml file for that repository, one may look for another domain in the urls list for the chart versions. If there is another domain found and that chart version was pulled or installed, the credentials would be passed on.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
helm.sh/helm/v3Go | < 3.6.1 | 3.6.1 |
Affected products
1Patches
115 files changed · +338 −17
cmd/helm/flags.go+1 −0 modified@@ -57,6 +57,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") + f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains") } // bindOutputFlag will add the output flag to the given command and bind the
cmd/helm/install_test.go+37 −0 modified@@ -18,10 +18,36 @@ package main import ( "fmt" + "net/http" + "net/http/httptest" "testing" + + "helm.sh/helm/v3/pkg/repo/repotest" ) func TestInstall(t *testing.T) { + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") + if err != nil { + t.Fatal(err) + } + defer srv.Stop() + + srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if !ok || username != "username" || password != "password" { + t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) + } + })) + + srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) + })) + defer srv2.Close() + + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + tests := []cmdTestCase{ // Install, base case { @@ -207,6 +233,17 @@ func TestInstall(t *testing.T) { name: "install chart with only crds", cmd: "install crd-test testdata/testcharts/chart-with-only-crds --namespace default", }, + // Verify the user/pass works + { + name: "basic install with credentials", + cmd: "install aeneas reqtest --namespace default --repo " + srv.URL() + " --username username --password password", + golden: "output/install.txt", + }, + { + name: "basic install with credentials", + cmd: "install aeneas reqtest --namespace default --repo " + srv2.URL + " --username username --password password --pass-credentials", + golden: "output/install.txt", + }, } runTestActionCmd(t, tests)
cmd/helm/pull_test.go+111 −0 modified@@ -18,6 +18,8 @@ package main import ( "fmt" + "net/http" + "net/http/httptest" "os" "path/filepath" "testing" @@ -250,6 +252,115 @@ func TestPullCmd(t *testing.T) { } } +func TestPullWithCredentialsCmd(t *testing.T) { + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") + if err != nil { + t.Fatal(err) + } + defer srv.Stop() + + srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if !ok || username != "username" || password != "password" { + t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) + } + })) + + srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) + })) + defer srv2.Close() + + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + + // all flags will get "-d outdir" appended. + tests := []struct { + name string + args string + existFile string + existDir string + wantError bool + wantErrorMsg string + expectFile string + expectDir bool + }{ + { + name: "Chart fetch using repo URL", + expectFile: "./signtest-0.1.0.tgz", + args: "signtest --repo " + srv.URL() + " --username username --password password", + }, + { + name: "Fail fetching non-existent chart on repo URL", + args: "someChart --repo " + srv.URL() + " --username username --password password", + wantError: true, + }, + { + name: "Specific version chart fetch using repo URL", + expectFile: "./signtest-0.1.0.tgz", + args: "signtest --version=0.1.0 --repo " + srv.URL() + " --username username --password password", + }, + { + name: "Specific version chart fetch using repo URL", + args: "signtest --version=0.2.0 --repo " + srv.URL() + " --username username --password password", + wantError: true, + }, + { + name: "Chart located on different domain with credentials passed", + args: "reqtest --repo " + srv2.URL + " --username username --password password --pass-credentials", + expectFile: "./reqtest-0.1.0.tgz", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + outdir := srv.Root() + cmd := fmt.Sprintf("pull %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s", + tt.args, + outdir, + filepath.Join(outdir, "repositories.yaml"), + outdir, + filepath.Join(outdir, "config.json"), + ) + // Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182 + if tt.existFile != "" { + file := filepath.Join(outdir, tt.existFile) + _, err := os.Create(file) + if err != nil { + t.Fatal(err) + } + } + if tt.existDir != "" { + file := filepath.Join(outdir, tt.existDir) + err := os.Mkdir(file, 0755) + if err != nil { + t.Fatal(err) + } + } + _, _, err := executeActionCommand(cmd) + if err != nil { + if tt.wantError { + if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() { + t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg) + } + return + } + t.Fatalf("%q reported error: %s", tt.name, err) + } + + ef := filepath.Join(outdir, tt.expectFile) + fi, err := os.Stat(ef) + if err != nil { + t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) + } + if fi.IsDir() != tt.expectDir { + t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) + } + }) + } +} + func TestPullVersionCompletion(t *testing.T) { repoFile := "testdata/helmhome/helm/repositories.yaml" repoCache := "testdata/helmhome/helm/repository"
cmd/helm/repo_add.go+3 −0 modified@@ -48,6 +48,7 @@ type repoAddOptions struct { url string username string password string + passCredentialsAll bool forceUpdate bool allowDeprecatedRepos bool @@ -91,6 +92,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the repository") f.BoolVar(&o.allowDeprecatedRepos, "allow-deprecated-repos", false, "by default, this command will not allow adding official repos that have been permanently deleted. This disables that behavior") + f.BoolVar(&o.passCredentialsAll, "pass-credentials", false, "pass credentials to all domains") return cmd } @@ -149,6 +151,7 @@ func (o *repoAddOptions) run(out io.Writer) error { URL: o.url, Username: o.username, Password: o.password, + PassCredentialsAll: o.passCredentialsAll, CertFile: o.certFile, KeyFile: o.keyFile, CAFile: o.caFile,
pkg/action/install.go+27 −3 modified@@ -20,6 +20,7 @@ import ( "bytes" "fmt" "io/ioutil" + "net/url" "os" "path" "path/filepath" @@ -113,6 +114,7 @@ type ChartPathOptions struct { InsecureSkipTLSverify bool // --insecure-skip-verify Keyring string // --keyring Password string // --password + PassCredentialsAll bool // --pass-credentials RepoURL string // --repo Username string // --username Verify bool // --verify @@ -654,7 +656,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( Keyring: c.Keyring, Getters: getter.All(settings), Options: []getter.Option{ - getter.WithBasicAuth(c.Username, c.Password), + getter.WithPassCredentialsAll(c.PassCredentialsAll), getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), }, @@ -665,12 +667,34 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( dl.Verify = downloader.VerifyAlways } if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, getter.All(settings)) + chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version, + c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, getter.All(settings)) if err != nil { return "", err } name = chartURL + + // Only pass the user/pass on when the user has said to or when the + // location of the chart repo and the chart are the same domain. + u1, err := url.Parse(c.RepoURL) + if err != nil { + return "", err + } + u2, err := url.Parse(chartURL) + if err != nil { + return "", err + } + + // Host on URL (returned from url.Parse) contains the port if present. + // This check ensures credentials are not passed between different + // services on different ports. + if c.PassCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) { + dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password)) + } else { + dl.Options = append(dl.Options, getter.WithBasicAuth("", "")) + } + } else { + dl.Options = append(dl.Options, getter.WithBasicAuth(c.Username, c.Password)) } if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil {
pkg/action/pull.go+2 −1 modified@@ -82,6 +82,7 @@ func (p *Pull) Run(chartRef string) (string, error) { Getters: getter.All(p.Settings), Options: []getter.Option{ getter.WithBasicAuth(p.Username, p.Password), + getter.WithPassCredentialsAll(p.PassCredentialsAll), getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), }, @@ -118,7 +119,7 @@ func (p *Pull) Run(chartRef string) (string, error) { } if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, getter.All(p.Settings)) + chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings)) if err != nil { return out.String(), err }
pkg/downloader/chart_downloader.go+5 −1 modified@@ -195,6 +195,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er c.Options = append( c.Options, getter.WithBasicAuth(rc.Username, rc.Password), + getter.WithPassCredentialsAll(rc.PassCredentialsAll), ) } return u, nil @@ -224,7 +225,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) } if r.Config.Username != "" && r.Config.Password != "" { - c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) + c.Options = append(c.Options, + getter.WithBasicAuth(r.Config.Username, r.Config.Password), + getter.WithPassCredentialsAll(r.Config.PassCredentialsAll), + ) } }
pkg/downloader/chart_downloader_test.go+1 −0 modified@@ -205,6 +205,7 @@ func TestDownloadTo(t *testing.T) { }), Options: []getter.Option{ getter.WithBasicAuth("username", "password"), + getter.WithPassCredentialsAll(false), }, } cname := "/signtest-0.1.0.tgz"
pkg/downloader/manager.go+7 −5 modified@@ -310,7 +310,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { // Any failure to resolve/download a chart should fail: // https://github.com/helm/helm/issues/1439 - churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) + churl, username, password, passcredentialsall, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) if err != nil { saveError = errors.Wrapf(err, "could not find %s", churl) break @@ -332,6 +332,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { Getters: m.Getters, Options: []getter.Option{ getter.WithBasicAuth(username, password), + getter.WithPassCredentialsAll(passcredentialsall), }, } @@ -685,9 +686,9 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { // repoURL is the repository to search // // If it finds a URL that is "relative", it will prepend the repoURL. -func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) { +func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, passcredentialsall bool, err error) { if strings.HasPrefix(repoURL, "oci://") { - return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", nil + return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, nil } for _, cr := range repos { @@ -709,15 +710,16 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* } username = cr.Config.Username password = cr.Config.Password + passcredentialsall = cr.Config.PassCredentialsAll return } } url, err = repo.FindChartInRepoURL(repoURL, name, version, "", "", "", m.Getters) if err == nil { - return url, username, password, err + return url, username, password, false, err } err = errors.Errorf("chart %s not found in %s: %s", name, repoURL, err) - return url, username, password, err + return url, username, password, false, err } // findEntryByName finds an entry in the chart repository whose name matches the given name.
pkg/downloader/manager_test.go+4 −1 modified@@ -81,7 +81,7 @@ func TestFindChartURL(t *testing.T) { version := "0.1.0" repoURL := "http://example.com/charts" - churl, username, password, err := m.findChartURL(name, version, repoURL, repos) + churl, username, password, passcredentialsall, err := m.findChartURL(name, version, repoURL, repos) if err != nil { t.Fatal(err) } @@ -94,6 +94,9 @@ func TestFindChartURL(t *testing.T) { if password != "" { t.Errorf("Unexpected password %q", password) } + if passcredentialsall != false { + t.Errorf("Unexpected passcredentialsall %t", passcredentialsall) + } } func TestGetRepoNames(t *testing.T) {
pkg/getter/getter.go+7 −0 modified@@ -38,6 +38,7 @@ type options struct { insecureSkipVerifyTLS bool username string password string + passCredentialsAll bool userAgent string version string registryClient *registry.Client @@ -64,6 +65,12 @@ func WithBasicAuth(username, password string) Option { } } +func WithPassCredentialsAll(pass bool) Option { + return func(opts *options) { + opts.passCredentialsAll = pass + } +} + // WithUserAgent sets the request's User-Agent header to use the provided agent name. func WithUserAgent(userAgent string) Option { return func(opts *options) {
pkg/getter/httpgetter.go+19 −2 modified@@ -20,6 +20,7 @@ import ( "crypto/tls" "io" "net/http" + "net/url" "github.com/pkg/errors" @@ -56,8 +57,24 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) { req.Header.Set("User-Agent", g.opts.userAgent) } - if g.opts.username != "" && g.opts.password != "" { - req.SetBasicAuth(g.opts.username, g.opts.password) + // Before setting the basic auth credentials, make sure the URL associated + // with the basic auth is the one being fetched. + u1, err := url.Parse(g.opts.url) + if err != nil { + return buf, errors.Wrap(err, "Unable to parse getter URL") + } + u2, err := url.Parse(href) + if err != nil { + return buf, errors.Wrap(err, "Unable to parse URL getting from") + } + + // Host on URL (returned from url.Parse) contains the port if present. + // This check ensures credentials are not passed between different + // services on different ports. + if g.opts.passCredentialsAll || (u1.Scheme == u2.Scheme && u1.Host == u2.Host) { + if g.opts.username != "" && g.opts.password != "" { + req.SetBasicAuth(g.opts.username, g.opts.password) + } } client, err := g.httpClient()
pkg/getter/httpgetter_test.go+98 −0 modified@@ -54,6 +54,7 @@ func TestHTTPGetter(t *testing.T) { // Test with options g, err = NewHTTPGetter( WithBasicAuth("I", "Am"), + WithPassCredentialsAll(false), WithUserAgent("Groot"), WithTLSClientConfig(pub, priv, ca), WithInsecureSkipVerifyTLS(insecure), @@ -76,6 +77,10 @@ func TestHTTPGetter(t *testing.T) { t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password) } + if hg.opts.passCredentialsAll != false { + t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll) + } + if hg.opts.userAgent != "Groot" { t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent) } @@ -118,6 +123,28 @@ func TestHTTPGetter(t *testing.T) { if hg.opts.insecureSkipVerifyTLS != insecure { t.Errorf("Expected NewHTTPGetter to contain %t as InsecureSkipVerifyTLs flag, got %t", insecure, hg.opts.insecureSkipVerifyTLS) } + + // Checking false by default + if hg.opts.passCredentialsAll != false { + t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", false, hg.opts.passCredentialsAll) + } + + // Test setting PassCredentialsAll + g, err = NewHTTPGetter( + WithBasicAuth("I", "Am"), + WithPassCredentialsAll(true), + ) + if err != nil { + t.Fatal(err) + } + + hg, ok = g.(*HTTPGetter) + if !ok { + t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter") + } + if hg.opts.passCredentialsAll != true { + t.Errorf("Expected NewHTTPGetter to contain %t as PassCredentialsAll, got %t", true, hg.opts.passCredentialsAll) + } } func TestDownload(t *testing.T) { @@ -163,6 +190,7 @@ func TestDownload(t *testing.T) { httpgetter, err := NewHTTPGetter( WithURL(u.String()), WithBasicAuth("username", "password"), + WithPassCredentialsAll(false), WithUserAgent(expectedUserAgent), ) if err != nil { @@ -176,6 +204,76 @@ func TestDownload(t *testing.T) { if got.String() != expect { t.Errorf("Expected %q, got %q", expect, got.String()) } + + // test with Get URL differing from withURL + crossAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if ok || username == "username" || password == "password" { + t.Errorf("Expected request to not include but got '%v', '%s', '%s'", ok, username, password) + } + fmt.Fprint(w, expect) + })) + + defer crossAuthSrv.Close() + + u, _ = url.ParseRequestURI(crossAuthSrv.URL) + + // A different host is provided for the WithURL from the one used for Get + u2, _ := url.ParseRequestURI(crossAuthSrv.URL) + host := strings.Split(u2.Host, ":") + host[0] = host[0] + "a" + u2.Host = strings.Join(host, ":") + httpgetter, err = NewHTTPGetter( + WithURL(u2.String()), + WithBasicAuth("username", "password"), + WithPassCredentialsAll(false), + ) + if err != nil { + t.Fatal(err) + } + got, err = httpgetter.Get(u.String()) + if err != nil { + t.Fatal(err) + } + + if got.String() != expect { + t.Errorf("Expected %q, got %q", expect, got.String()) + } + + // test with Get URL differing from withURL and should pass creds + crossAuthSrv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if !ok || username != "username" || password != "password" { + t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) + } + fmt.Fprint(w, expect) + })) + + defer crossAuthSrv.Close() + + u, _ = url.ParseRequestURI(crossAuthSrv.URL) + + // A different host is provided for the WithURL from the one used for Get + u2, _ = url.ParseRequestURI(crossAuthSrv.URL) + host = strings.Split(u2.Host, ":") + host[0] = host[0] + "a" + u2.Host = strings.Join(host, ":") + httpgetter, err = NewHTTPGetter( + WithURL(u2.String()), + WithBasicAuth("username", "password"), + WithPassCredentialsAll(true), + ) + if err != nil { + t.Fatal(err) + } + got, err = httpgetter.Get(u.String()) + if err != nil { + t.Fatal(err) + } + + if got.String() != expect { + t.Errorf("Expected %q, got %q", expect, got.String()) + } } func TestDownloadTLS(t *testing.T) {
pkg/repo/chartrepo.go+12 −0 modified@@ -48,6 +48,7 @@ type Entry struct { KeyFile string `json:"keyFile"` CAFile string `json:"caFile"` InsecureSkipTLSverify bool `json:"insecure_skip_tls_verify"` + PassCredentialsAll bool `json:"pass_credentials_all"` } // ChartRepository represents a chart repository @@ -129,6 +130,7 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) { getter.WithInsecureSkipVerifyTLS(r.Config.InsecureSkipTLSverify), getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile), getter.WithBasicAuth(r.Config.Username, r.Config.Password), + getter.WithPassCredentialsAll(r.Config.PassCredentialsAll), ) if err != nil { return "", err @@ -217,6 +219,15 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion // but it also receives credentials and TLS verify flag for the chart repository. // TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { + return FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, false, getters) +} + +// FindChartInAuthAndTLSAndPassRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repositories, like FindChartInRepoURL, +// but it also receives credentials, TLS verify flag, and if credentials should +// be passed on to other domains. +// TODO Helm 4, FindChartInAuthAndTLSAndPassRepoURL should be integrated into FindChartInAuthRepoURL. +func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify, passCredentialsAll bool, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location buf := make([]byte, 20) @@ -227,6 +238,7 @@ func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartV URL: repoURL, Username: username, Password: password, + PassCredentialsAll: passCredentialsAll, CertFile: certFile, KeyFile: keyFile, CAFile: caFile,
pkg/repo/chartrepo_test.go+4 −4 modified@@ -292,14 +292,14 @@ func startLocalTLSServerForTests(handler http.Handler) (*httptest.Server, error) return httptest.NewTLSServer(handler), nil } -func TestFindChartInAuthAndTLSRepoURL(t *testing.T) { +func TestFindChartInAuthAndTLSAndPassRepoURL(t *testing.T) { srv, err := startLocalTLSServerForTests(nil) if err != nil { t.Fatal(err) } defer srv.Close() - chartURL, err := FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, getter.All(&cli.EnvSettings{})) + chartURL, err := FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "", "", "", "", true, false, getter.All(&cli.EnvSettings{})) if err != nil { t.Fatalf("%v", err) } @@ -308,10 +308,10 @@ func TestFindChartInAuthAndTLSRepoURL(t *testing.T) { } // If the insecureSkipTLsverify is false, it will return an error that contains "x509: certificate signed by unknown authority". - _, err = FindChartInAuthAndTLSRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, getter.All(&cli.EnvSettings{})) + _, err = FindChartInAuthAndTLSAndPassRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", false, false, getter.All(&cli.EnvSettings{})) if !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { - t.Errorf("Expected TLS error for function FindChartInAuthAndTLSRepoURL not found, but got a different error (%v)", err) + t.Errorf("Expected TLS error for function FindChartInAuthAndTLSAndPassRepoURL not found, but got a different error (%v)", err) } }
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
6- github.com/advisories/GHSA-56hp-xqp3-w2jfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-32690ghsaADVISORY
- github.com/helm/helm/commit/61d8e8c4a6f95540c15c6a65f36a6dd0a45e7a2fghsaWEB
- github.com/helm/helm/releases/tag/v3.6.1ghsax_refsource_MISCWEB
- github.com/helm/helm/security/advisories/GHSA-56hp-xqp3-w2jfghsax_refsource_CONFIRMWEB
- pkg.go.dev/vuln/GO-2022-0384ghsaWEB
News mentions
0No linked articles in our index yet.