Uncontrolled Resource Consumption vulnerability in ArgoCD's repo server
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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/argoproj/argo-cd/v2Go | >= 2.4.0, < 2.8.14 | 2.8.14 |
github.com/argoproj/argo-cd/v2Go | >= 2.9.0, < 2.9.10 | 2.9.10 |
github.com/argoproj/argo-cd/v2Go | >= 2.10.0, < 2.10.5 | 2.10.5 |
Affected products
1Patches
336b8a12a38f8Merge pull request from GHSA-jhwx-mhww-rgc3
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
3e5a878f6e30Merge pull request from GHSA-jhwx-mhww-rgc3
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
14f681e3ee7cMerge pull request from GHSA-jhwx-mhww-rgc3
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- github.com/advisories/GHSA-jhwx-mhww-rgc3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-29893ghsaADVISORY
- github.com/argoproj/argo-cd/commit/14f681e3ee7c38731943b98f92277e88a3db109dghsax_refsource_MISCWEB
- github.com/argoproj/argo-cd/commit/36b8a12a38f8d92d55bffc81deed44389bf6eb59ghsax_refsource_MISCWEB
- github.com/argoproj/argo-cd/commit/3e5a878f6e30d935fa149723ea2a2e93748fcdddghsax_refsource_MISCWEB
- github.com/argoproj/argo-cd/security/advisories/GHSA-jhwx-mhww-rgc3ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.