VYPR
Moderate severityNVD Advisory· Published Mar 29, 2024· Updated Aug 2, 2024

Uncontrolled Resource Consumption vulnerability in ArgoCD's repo server

CVE-2024-29893

Description

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. All versions of ArgoCD starting from v2.4 have a bug where the ArgoCD repo-server component is vulnerable to a Denial-of-Service attack vector. Specifically, it's possible to crash the repo server component through an out of memory error by pointing it to a malicious Helm registry. The loadRepoIndex() function in the ArgoCD's helm package, does not limit the size nor time while fetching the data. It fetches it and creates a byte slice from the retrieved data in one go. If the registry is implemented to push data continuously, the repo server will keep allocating memory until it runs out of it. A patch for this vulnerability has been released in v2.10.3, v2.9.8, and v2.8.12.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/argoproj/argo-cd/v2Go
>= 2.4.0, < 2.8.142.8.14
github.com/argoproj/argo-cd/v2Go
>= 2.9.0, < 2.9.102.9.10
github.com/argoproj/argo-cd/v2Go
>= 2.10.0, < 2.10.52.10.5

Affected products

1

Patches

3
36b8a12a38f8

Merge pull request from GHSA-jhwx-mhww-rgc3

https://github.com/argoproj/argo-cdpasha-codefreshMar 28, 2024via ghsa
5 files changed · +26 13
  • cmd/argocd-repo-server/commands/argocd_repo_server.go+6 0 modified
    @@ -83,6 +83,7 @@ func NewCommand() *cobra.Command {
     		streamedManifestMaxTarSize        string
     		streamedManifestMaxExtractedSize  string
     		helmManifestMaxExtractedSize      string
    +		helmRegistryMaxIndexSize          string
     		disableManifestMaxExtractedSize   bool
     	)
     	var command = cobra.Command{
    @@ -125,6 +126,9 @@ func NewCommand() *cobra.Command {
     			helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
     			errors.CheckError(err)
     
    +			helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize)
    +			errors.CheckError(err)
    +
     			askPassServer := askpass.NewServer()
     			metricsServer := metrics.NewMetricsServer()
     			cacheutil.CollectMetrics(redisClient, metricsServer)
    @@ -140,6 +144,7 @@ func NewCommand() *cobra.Command {
     				StreamedManifestMaxExtractedSize:             streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
     				StreamedManifestMaxTarSize:                   streamedManifestMaxTarSizeQuantity.ToDec().Value(),
     				HelmManifestMaxExtractedSize:                 helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
    +				HelmRegistryMaxIndexSize:                     helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
     			}, askPassServer)
     			errors.CheckError(err)
     
    @@ -223,6 +228,7 @@ func NewCommand() *cobra.Command {
     	command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
     	command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
     	command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
    +	command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
     	command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
     	tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
     	cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
    
  • reposerver/repository/repository.go+4 3 modified
    @@ -108,6 +108,7 @@ type RepoServerInitConstants struct {
     	StreamedManifestMaxExtractedSize             int64
     	StreamedManifestMaxTarSize                   int64
     	HelmManifestMaxExtractedSize                 int64
    +	HelmRegistryMaxIndexSize                     int64
     	DisableHelmManifestMaxExtractedSize          bool
     }
     
    @@ -2307,7 +2308,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi
     		return helmClient, version.String(), nil
     	}
     
    -	index, err := helmClient.GetIndex(noRevisionCache)
    +	index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, "", err
     	}
    @@ -2385,7 +2386,7 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo
     }
     
     func (s *Service) GetHelmCharts(ctx context.Context, q *apiclient.HelmChartsRequest) (*apiclient.HelmChartsResponse, error) {
    -	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true)
    +	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, err
     	}
    @@ -2420,7 +2421,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
     				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).TestHelmOCI()
     				return err
     			} else {
    -				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false)
    +				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize)
     				return err
     			}
     		},
    
  • util/helm/client.go+5 5 modified
    @@ -56,7 +56,7 @@ type indexCache interface {
     type Client interface {
     	CleanChartCache(chart string, version string) error
     	ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error)
    -	GetIndex(noCache bool) (*Index, error)
    +	GetIndex(noCache bool, maxIndexSize int64) (*Index, error)
     	GetTags(chart string, noCache bool) (*TagsList, error)
     	TestHelmOCI() (bool, error)
     }
    @@ -230,7 +230,7 @@ func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredent
     	}), nil
     }
     
    -func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
    +func (c *nativeHelmChart) GetIndex(noCache bool, maxIndexSize int64) (*Index, error) {
     	indexLock.Lock(c.repoURL)
     	defer indexLock.Unlock(c.repoURL)
     
    @@ -244,7 +244,7 @@ func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
     	if len(data) == 0 {
     		start := time.Now()
     		var err error
    -		data, err = c.loadRepoIndex()
    +		data, err = c.loadRepoIndex(maxIndexSize)
     		if err != nil {
     			return nil, err
     		}
    @@ -297,7 +297,7 @@ func (c *nativeHelmChart) TestHelmOCI() (bool, error) {
     	return true, nil
     }
     
    -func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
    +func (c *nativeHelmChart) loadRepoIndex(maxIndexSize int64) ([]byte, error) {
     	indexURL, err := getIndexURL(c.repoURL)
     	if err != nil {
     		return nil, err
    @@ -332,7 +332,7 @@ func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
     	if resp.StatusCode != http.StatusOK {
     		return nil, errors.New("failed to get index: " + resp.Status)
     	}
    -	return io.ReadAll(resp.Body)
    +	return io.ReadAll(io.LimitReader(resp.Body, maxIndexSize))
     }
     
     func newTLSConfig(creds Creds) (*tls.Config, error) {
    
  • util/helm/client_test.go+10 4 modified
    @@ -37,12 +37,12 @@ func (f *fakeIndexCache) GetHelmIndex(_ string, indexData *[]byte) error {
     func TestIndex(t *testing.T) {
     	t.Run("Invalid", func(t *testing.T) {
     		client := NewClient("", Creds{}, false, "")
    -		_, err := client.GetIndex(false)
    +		_, err := client.GetIndex(false, 10000)
     		assert.Error(t, err)
     	})
     	t.Run("Stable", func(t *testing.T) {
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -51,7 +51,7 @@ func TestIndex(t *testing.T) {
     			Username: "my-password",
     			Password: "my-username",
     		}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -63,12 +63,18 @@ func TestIndex(t *testing.T) {
     		require.NoError(t, err)
     
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "", WithIndexCache(&fakeIndexCache{data: data.Bytes()}))
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     
     		assert.NoError(t, err)
     		assert.Equal(t, fakeIndex, *index)
     	})
     
    +	t.Run("Limited", func(t *testing.T) {
    +		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    +		_, err := client.GetIndex(false, 100)
    +
    +		assert.ErrorContains(t, err, "unexpected end of stream")
    +	})
     }
     
     func Test_nativeHelmChart_ExtractChart(t *testing.T) {
    
  • util/helm/mocks/Client.go+1 1 modified
    @@ -59,7 +59,7 @@ func (_m *Client) ExtractChart(chart string, version string, passCredentials boo
     }
     
     // GetIndex provides a mock function with given fields: noCache
    -func (_m *Client) GetIndex(noCache bool) (*helm.Index, error) {
    +func (_m *Client) GetIndex(noCache bool, maxIndexSize int64) (*helm.Index, error) {
     	ret := _m.Called(noCache)
     
     	var r0 *helm.Index
    
3e5a878f6e30

Merge pull request from GHSA-jhwx-mhww-rgc3

https://github.com/argoproj/argo-cdpasha-codefreshMar 28, 2024via ghsa
6 files changed · +27 14
  • cmd/argocd-repo-server/commands/argocd_repo_server.go+6 0 modified
    @@ -68,6 +68,7 @@ func NewCommand() *cobra.Command {
     		streamedManifestMaxTarSize        string
     		streamedManifestMaxExtractedSize  string
     		helmManifestMaxExtractedSize      string
    +		helmRegistryMaxIndexSize          string
     		disableManifestMaxExtractedSize   bool
     	)
     	var command = cobra.Command{
    @@ -110,6 +111,9 @@ func NewCommand() *cobra.Command {
     			helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
     			errors.CheckError(err)
     
    +			helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize)
    +			errors.CheckError(err)
    +
     			askPassServer := askpass.NewServer()
     			metricsServer := metrics.NewMetricsServer()
     			cacheutil.CollectMetrics(redisClient, metricsServer)
    @@ -125,6 +129,7 @@ func NewCommand() *cobra.Command {
     				StreamedManifestMaxExtractedSize:             streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
     				StreamedManifestMaxTarSize:                   streamedManifestMaxTarSizeQuantity.ToDec().Value(),
     				HelmManifestMaxExtractedSize:                 helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
    +				HelmRegistryMaxIndexSize:                     helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
     			}, askPassServer)
     			errors.CheckError(err)
     
    @@ -208,6 +213,7 @@ func NewCommand() *cobra.Command {
     	command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
     	command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
     	command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
    +	command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
     	command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
     	tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
     	cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
    
  • reposerver/repository/repository.go+4 3 modified
    @@ -107,6 +107,7 @@ type RepoServerInitConstants struct {
     	StreamedManifestMaxExtractedSize             int64
     	StreamedManifestMaxTarSize                   int64
     	HelmManifestMaxExtractedSize                 int64
    +	HelmRegistryMaxIndexSize                     int64
     	DisableHelmManifestMaxExtractedSize          bool
     }
     
    @@ -2356,7 +2357,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi
     		return helmClient, version.String(), nil
     	}
     
    -	index, err := helmClient.GetIndex(noRevisionCache)
    +	index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, "", err
     	}
    @@ -2434,7 +2435,7 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo
     }
     
     func (s *Service) GetHelmCharts(ctx context.Context, q *apiclient.HelmChartsRequest) (*apiclient.HelmChartsResponse, error) {
    -	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true)
    +	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, err
     	}
    @@ -2469,7 +2470,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
     				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).TestHelmOCI()
     				return err
     			} else {
    -				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false)
    +				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize)
     				return err
     			}
     		},
    
  • reposerver/repository/repository_test.go+1 1 modified
    @@ -113,7 +113,7 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git
     		chart := "my-chart"
     		oobChart := "out-of-bounds-chart"
     		version := "1.1.0"
    -		helmClient.On("GetIndex", mock.AnythingOfType("bool")).Return(&helm.Index{Entries: map[string]helm.Entries{
    +		helmClient.On("GetIndex", mock.AnythingOfType("bool"), mock.Anything).Return(&helm.Index{Entries: map[string]helm.Entries{
     			chart:    {{Version: "1.0.0"}, {Version: version}},
     			oobChart: {{Version: "1.0.0"}, {Version: version}},
     		}}, nil)
    
  • util/helm/client.go+5 5 modified
    @@ -56,7 +56,7 @@ type indexCache interface {
     type Client interface {
     	CleanChartCache(chart string, version string) error
     	ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error)
    -	GetIndex(noCache bool) (*Index, error)
    +	GetIndex(noCache bool, maxIndexSize int64) (*Index, error)
     	GetTags(chart string, noCache bool) (*TagsList, error)
     	TestHelmOCI() (bool, error)
     }
    @@ -230,7 +230,7 @@ func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredent
     	}), nil
     }
     
    -func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
    +func (c *nativeHelmChart) GetIndex(noCache bool, maxIndexSize int64) (*Index, error) {
     	indexLock.Lock(c.repoURL)
     	defer indexLock.Unlock(c.repoURL)
     
    @@ -244,7 +244,7 @@ func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
     	if len(data) == 0 {
     		start := time.Now()
     		var err error
    -		data, err = c.loadRepoIndex()
    +		data, err = c.loadRepoIndex(maxIndexSize)
     		if err != nil {
     			return nil, err
     		}
    @@ -297,7 +297,7 @@ func (c *nativeHelmChart) TestHelmOCI() (bool, error) {
     	return true, nil
     }
     
    -func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
    +func (c *nativeHelmChart) loadRepoIndex(maxIndexSize int64) ([]byte, error) {
     	indexURL, err := getIndexURL(c.repoURL)
     	if err != nil {
     		return nil, err
    @@ -332,7 +332,7 @@ func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
     	if resp.StatusCode != http.StatusOK {
     		return nil, errors.New("failed to get index: " + resp.Status)
     	}
    -	return io.ReadAll(resp.Body)
    +	return io.ReadAll(io.LimitReader(resp.Body, maxIndexSize))
     }
     
     func newTLSConfig(creds Creds) (*tls.Config, error) {
    
  • util/helm/client_test.go+10 4 modified
    @@ -37,12 +37,12 @@ func (f *fakeIndexCache) GetHelmIndex(_ string, indexData *[]byte) error {
     func TestIndex(t *testing.T) {
     	t.Run("Invalid", func(t *testing.T) {
     		client := NewClient("", Creds{}, false, "")
    -		_, err := client.GetIndex(false)
    +		_, err := client.GetIndex(false, 10000)
     		assert.Error(t, err)
     	})
     	t.Run("Stable", func(t *testing.T) {
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -51,7 +51,7 @@ func TestIndex(t *testing.T) {
     			Username: "my-password",
     			Password: "my-username",
     		}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -63,12 +63,18 @@ func TestIndex(t *testing.T) {
     		require.NoError(t, err)
     
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "", WithIndexCache(&fakeIndexCache{data: data.Bytes()}))
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     
     		assert.NoError(t, err)
     		assert.Equal(t, fakeIndex, *index)
     	})
     
    +	t.Run("Limited", func(t *testing.T) {
    +		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    +		_, err := client.GetIndex(false, 100)
    +
    +		assert.ErrorContains(t, err, "unexpected end of stream")
    +	})
     }
     
     func Test_nativeHelmChart_ExtractChart(t *testing.T) {
    
  • util/helm/mocks/Client.go+1 1 modified
    @@ -59,7 +59,7 @@ func (_m *Client) ExtractChart(chart string, version string, passCredentials boo
     }
     
     // GetIndex provides a mock function with given fields: noCache
    -func (_m *Client) GetIndex(noCache bool) (*helm.Index, error) {
    +func (_m *Client) GetIndex(noCache bool, maxIndexSize int64) (*helm.Index, error) {
     	ret := _m.Called(noCache)
     
     	var r0 *helm.Index
    
14f681e3ee7c

Merge pull request from GHSA-jhwx-mhww-rgc3

https://github.com/argoproj/argo-cdpasha-codefreshMar 28, 2024via ghsa
6 files changed · +27 14
  • cmd/argocd-repo-server/commands/argocd_repo_server.go+6 0 modified
    @@ -66,6 +66,7 @@ func NewCommand() *cobra.Command {
     		streamedManifestMaxTarSize        string
     		streamedManifestMaxExtractedSize  string
     		helmManifestMaxExtractedSize      string
    +		helmRegistryMaxIndexSize          string
     		disableManifestMaxExtractedSize   bool
     	)
     	var command = cobra.Command{
    @@ -108,6 +109,9 @@ func NewCommand() *cobra.Command {
     			helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
     			errors.CheckError(err)
     
    +			helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize)
    +			errors.CheckError(err)
    +
     			askPassServer := askpass.NewServer()
     			metricsServer := metrics.NewMetricsServer()
     			cacheutil.CollectMetrics(redisClient, metricsServer)
    @@ -123,6 +127,7 @@ func NewCommand() *cobra.Command {
     				StreamedManifestMaxExtractedSize:             streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
     				StreamedManifestMaxTarSize:                   streamedManifestMaxTarSizeQuantity.ToDec().Value(),
     				HelmManifestMaxExtractedSize:                 helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
    +				HelmRegistryMaxIndexSize:                     helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
     			}, askPassServer)
     			errors.CheckError(err)
     
    @@ -204,6 +209,7 @@ func NewCommand() *cobra.Command {
     	command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
     	command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
     	command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
    +	command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
     	command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
     	tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
     	cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
    
  • reposerver/repository/repository.go+4 3 modified
    @@ -107,6 +107,7 @@ type RepoServerInitConstants struct {
     	StreamedManifestMaxExtractedSize             int64
     	StreamedManifestMaxTarSize                   int64
     	HelmManifestMaxExtractedSize                 int64
    +	HelmRegistryMaxIndexSize                     int64
     	DisableHelmManifestMaxExtractedSize          bool
     }
     
    @@ -2345,7 +2346,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi
     		return helmClient, version.String(), nil
     	}
     
    -	index, err := helmClient.GetIndex(noRevisionCache)
    +	index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, "", err
     	}
    @@ -2423,7 +2424,7 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo
     }
     
     func (s *Service) GetHelmCharts(ctx context.Context, q *apiclient.HelmChartsRequest) (*apiclient.HelmChartsResponse, error) {
    -	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true)
    +	index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize)
     	if err != nil {
     		return nil, err
     	}
    @@ -2458,7 +2459,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
     				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).TestHelmOCI()
     				return err
     			} else {
    -				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false)
    +				_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize)
     				return err
     			}
     		},
    
  • reposerver/repository/repository_test.go+1 1 modified
    @@ -113,7 +113,7 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git
     		chart := "my-chart"
     		oobChart := "out-of-bounds-chart"
     		version := "1.1.0"
    -		helmClient.On("GetIndex", mock.AnythingOfType("bool")).Return(&helm.Index{Entries: map[string]helm.Entries{
    +		helmClient.On("GetIndex", mock.AnythingOfType("bool"), mock.Anything).Return(&helm.Index{Entries: map[string]helm.Entries{
     			chart:    {{Version: "1.0.0"}, {Version: version}},
     			oobChart: {{Version: "1.0.0"}, {Version: version}},
     		}}, nil)
    
  • util/helm/client.go+5 5 modified
    @@ -56,7 +56,7 @@ type indexCache interface {
     type Client interface {
     	CleanChartCache(chart string, version string) error
     	ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error)
    -	GetIndex(noCache bool) (*Index, error)
    +	GetIndex(noCache bool, maxIndexSize int64) (*Index, error)
     	GetTags(chart string, noCache bool) (*TagsList, error)
     	TestHelmOCI() (bool, error)
     }
    @@ -230,7 +230,7 @@ func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredent
     	}), nil
     }
     
    -func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
    +func (c *nativeHelmChart) GetIndex(noCache bool, maxIndexSize int64) (*Index, error) {
     	indexLock.Lock(c.repoURL)
     	defer indexLock.Unlock(c.repoURL)
     
    @@ -244,7 +244,7 @@ func (c *nativeHelmChart) GetIndex(noCache bool) (*Index, error) {
     	if len(data) == 0 {
     		start := time.Now()
     		var err error
    -		data, err = c.loadRepoIndex()
    +		data, err = c.loadRepoIndex(maxIndexSize)
     		if err != nil {
     			return nil, err
     		}
    @@ -297,7 +297,7 @@ func (c *nativeHelmChart) TestHelmOCI() (bool, error) {
     	return true, nil
     }
     
    -func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
    +func (c *nativeHelmChart) loadRepoIndex(maxIndexSize int64) ([]byte, error) {
     	indexURL, err := getIndexURL(c.repoURL)
     	if err != nil {
     		return nil, err
    @@ -332,7 +332,7 @@ func (c *nativeHelmChart) loadRepoIndex() ([]byte, error) {
     	if resp.StatusCode != http.StatusOK {
     		return nil, errors.New("failed to get index: " + resp.Status)
     	}
    -	return io.ReadAll(resp.Body)
    +	return io.ReadAll(io.LimitReader(resp.Body, maxIndexSize))
     }
     
     func newTLSConfig(creds Creds) (*tls.Config, error) {
    
  • util/helm/client_test.go+10 4 modified
    @@ -37,12 +37,12 @@ func (f *fakeIndexCache) GetHelmIndex(_ string, indexData *[]byte) error {
     func TestIndex(t *testing.T) {
     	t.Run("Invalid", func(t *testing.T) {
     		client := NewClient("", Creds{}, false, "")
    -		_, err := client.GetIndex(false)
    +		_, err := client.GetIndex(false, 10000)
     		assert.Error(t, err)
     	})
     	t.Run("Stable", func(t *testing.T) {
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -51,7 +51,7 @@ func TestIndex(t *testing.T) {
     			Username: "my-password",
     			Password: "my-username",
     		}, false, "")
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     		assert.NoError(t, err)
     		assert.NotNil(t, index)
     	})
    @@ -63,12 +63,18 @@ func TestIndex(t *testing.T) {
     		require.NoError(t, err)
     
     		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "", WithIndexCache(&fakeIndexCache{data: data.Bytes()}))
    -		index, err := client.GetIndex(false)
    +		index, err := client.GetIndex(false, 10000)
     
     		assert.NoError(t, err)
     		assert.Equal(t, fakeIndex, *index)
     	})
     
    +	t.Run("Limited", func(t *testing.T) {
    +		client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
    +		_, err := client.GetIndex(false, 100)
    +
    +		assert.ErrorContains(t, err, "unexpected end of stream")
    +	})
     }
     
     func Test_nativeHelmChart_ExtractChart(t *testing.T) {
    
  • util/helm/mocks/Client.go+1 1 modified
    @@ -59,7 +59,7 @@ func (_m *Client) ExtractChart(chart string, version string, passCredentials boo
     }
     
     // GetIndex provides a mock function with given fields: noCache
    -func (_m *Client) GetIndex(noCache bool) (*helm.Index, error) {
    +func (_m *Client) GetIndex(noCache bool, maxIndexSize int64) (*helm.Index, error) {
     	ret := _m.Called(noCache)
     
     	var r0 *helm.Index
    

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.