VYPR
Moderate severityNVD Advisory· Published Jun 16, 2021· Updated Aug 3, 2024

Repository credentials passed to alternate domain

CVE-2021-32690

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.

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

Affected products

1

Patches

1
61d8e8c4a6f9

tweak basic handling

https://github.com/helm/helmMatt FarinaJun 11, 2021via ghsa
15 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

News mentions

0

No linked articles in our index yet.