VYPR
Low severityNVD Advisory· Published Sep 17, 2020· Updated May 29, 2025

Duplicate plugin entries in Helm

CVE-2020-15187

Description

In Helm before versions 2.16.11 and 3.3.2, a Helm plugin can contain duplicates of the same entry, with the last one always used. If a plugin is compromised, this lowers the level of access that an attacker needs to modify a plugin's install hooks, causing a local execution attack. To perform this attack, an attacker must have write access to the git repository or plugin archive (.tgz) while being downloaded (which can occur during a MITM attack on a non-SSL connection). This issue has been patched in Helm 2.16.11 and Helm 3.3.2. As a possible workaround make sure to install plugins using a secure connection protocol like SSL.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
helm.sh/helm/v3Go
>= 3.0.0, < 3.3.23.3.2
helm.sh/helmGo
>= 2.0.0, < 2.16.112.16.11

Affected products

1

Patches

6
d9ef5ce8bad5

Merge pull request from GHSA-c52f-pq47-2r9j

https://github.com/helm/helmMatthew FisherSep 17, 2020via ghsa
9 files changed · +31 8
  • pkg/plugin/installer/local_installer_test.go+1 1 modified
    @@ -37,7 +37,7 @@ func TestLocalInstaller(t *testing.T) {
     		t.Fatal(err)
     	}
     
    -	source := "../testdata/plugdir/echo"
    +	source := "../testdata/plugdir/good/echo"
     	i, err := NewForSource(source, "")
     	if err != nil {
     		t.Fatalf("unexpected error: %s", err)
    
  • pkg/plugin/installer/vcs_installer_test.go+1 1 modified
    @@ -56,7 +56,7 @@ func TestVCSInstaller(t *testing.T) {
     	}
     
     	source := "https://github.com/adamreese/helm-env"
    -	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
    +	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
     	repo := &testRepo{
     		local: testRepoPath,
     		tags:  []string{"0.1.0", "0.1.1"},
    
  • pkg/plugin/plugin.go+7 1 modified
    @@ -96,6 +96,12 @@ type Metadata struct {
     	// Downloaders field is used if the plugin supply downloader mechanism
     	// for special protocols.
     	Downloaders []Downloaders `json:"downloaders"`
    +
    +	// UseTunnelDeprecated indicates that this command needs a tunnel.
    +	// Setting this will cause a number of side effects, such as the
    +	// automatic setting of HELM_HOST.
    +	// DEPRECATED and unused, but retained for backwards compatibility with Helm 2 plugins. Remove in Helm 4
    +	UseTunnelDeprecated bool `json:"useTunnel,omitempty"`
     }
     
     // Plugin represents a plugin.
    @@ -200,7 +206,7 @@ func LoadDir(dirname string) (*Plugin, error) {
     	}
     
     	plug := &Plugin{Dir: dirname}
    -	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
    +	if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil {
     		return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile)
     	}
     	return plug, validatePluginData(plug, pluginfile)
    
  • pkg/plugin/plugin_test.go+11 4 modified
    @@ -178,7 +178,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
     }
     
     func TestLoadDir(t *testing.T) {
    -	dirname := "testdata/plugdir/hello"
    +	dirname := "testdata/plugdir/good/hello"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -205,8 +205,15 @@ func TestLoadDir(t *testing.T) {
     	}
     }
     
    +func TestLoadDirDuplicateEntries(t *testing.T) {
    +	dirname := "testdata/plugdir/bad/duplicate-entries"
    +	if _, err := LoadDir(dirname); err == nil {
    +		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
    +	}
    +}
    +
     func TestDownloader(t *testing.T) {
    -	dirname := "testdata/plugdir/downloader"
    +	dirname := "testdata/plugdir/good/downloader"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -244,7 +251,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("expected empty dir to have 0 plugins")
     	}
     
    -	basedir := "testdata/plugdir"
    +	basedir := "testdata/plugdir/good"
     	plugs, err := LoadAll(basedir)
     	if err != nil {
     		t.Fatalf("Could not load %q: %s", basedir, err)
    @@ -288,7 +295,7 @@ func TestFindPlugins(t *testing.T) {
     		},
     		{
     			name:     "normal",
    -			plugdirs: "./testdata/plugdir",
    +			plugdirs: "./testdata/plugdir/good",
     			expected: 3,
     		},
     	}
    
  • pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml+11 0 added
    @@ -0,0 +1,11 @@
    +name: "duplicate-entries"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "echo hello"
    +ignoreFlags: true
    +hooks:
    +  install: "echo installing..."
    +hooks:
    +  install: "echo installing something different"
    
  • pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/echo/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/hello.sh+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/plugin.yaml+0 1 renamed
    @@ -5,6 +5,5 @@ description: |-
       description
     command: "$HELM_PLUGIN_SELF/hello.sh"
     ignoreFlags: true
    -install: "echo installing..."
     hooks:
       install: "echo installing..."
    
f2ede29480b5

Merge pull request from GHSA-c52f-pq47-2r9j

https://github.com/helm/helmMatthew FisherSep 17, 2020via ghsa
9 files changed · +35 8
  • pkg/plugin/installer/local_installer_test.go+1 1 modified
    @@ -48,7 +48,7 @@ func TestLocalInstaller(t *testing.T) {
     		t.Fatal(err)
     	}
     
    -	source := "../testdata/plugdir/echo"
    +	source := "../testdata/plugdir/good/echo"
     	i, err := NewForSource(source, "", home)
     	if err != nil {
     		t.Errorf("unexpected error: %s", err)
    
  • pkg/plugin/installer/vcs_installer_test.go+1 1 modified
    @@ -61,7 +61,7 @@ func TestVCSInstaller(t *testing.T) {
     	}
     
     	source := "https://github.com/adamreese/helm-env"
    -	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
    +	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
     	repo := &testRepo{
     		local: testRepoPath,
     		tags:  []string{"0.1.0", "0.1.1"},
    
  • pkg/plugin/plugin.go+11 2 modified
    @@ -22,9 +22,10 @@ import (
     	"path/filepath"
     	"strings"
     
    -	helm_env "k8s.io/helm/pkg/helm/environment"
    -
     	"github.com/ghodss/yaml"
    +	yaml2 "gopkg.in/yaml.v2"
    +
    +	helm_env "k8s.io/helm/pkg/helm/environment"
     )
     
     const pluginFileName = "plugin.yaml"
    @@ -120,12 +121,20 @@ func LoadDir(dirname string) (*Plugin, error) {
     	}
     
     	plug := &Plugin{Dir: dirname}
    +	if err := validateMeta(data); err != nil {
    +		return nil, err
    +	}
     	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
     		return nil, err
     	}
     	return plug, nil
     }
     
    +func validateMeta(data []byte) error {
    +	// This is done ONLY for validation. We need to use ghodss/yaml for the actual parsing.
    +	return yaml2.UnmarshalStrict(data, &Metadata{})
    +}
    +
     // LoadAll loads all plugins found beneath the base directory.
     //
     // This scans only one directory level.
    
  • pkg/plugin/plugin_test.go+10 3 modified
    @@ -64,7 +64,7 @@ func TestPrepareCommand(t *testing.T) {
     }
     
     func TestLoadDir(t *testing.T) {
    -	dirname := "testdata/plugdir/hello"
    +	dirname := "testdata/plugdir/good/hello"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -92,8 +92,15 @@ func TestLoadDir(t *testing.T) {
     	}
     }
     
    +func TestLoadDirDuplicateEntries(t *testing.T) {
    +	dirname := "testdata/plugdir/bad/duplicate-entries"
    +	if _, err := LoadDir(dirname); err == nil {
    +		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
    +	}
    +}
    +
     func TestDownloader(t *testing.T) {
    -	dirname := "testdata/plugdir/downloader"
    +	dirname := "testdata/plugdir/good/downloader"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -131,7 +138,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("expected empty dir to have 0 plugins")
     	}
     
    -	basedir := "testdata/plugdir"
    +	basedir := "testdata/plugdir/good"
     	plugs, err := LoadAll(basedir)
     	if err != nil {
     		t.Fatalf("Could not load %q: %s", basedir, err)
    
  • pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml+12 0 added
    @@ -0,0 +1,12 @@
    +name: "duplicate-entries"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "echo hello"
    +useTunnel: true
    +ignoreFlags: true
    +hooks:
    +  install: "echo installing..."
    +hooks:
    +  install: "echo installing something different"
    
  • pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/echo/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/hello.sh+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/plugin.yaml+0 1 renamed
    @@ -6,6 +6,5 @@ description: |-
     command: "$HELM_PLUGIN_SELF/hello.sh"
     useTunnel: true
     ignoreFlags: true
    -install: "echo installing..."
     hooks:
       install: "echo installing..."
    
b0296c0522e8

validate plugin metadata before loading

https://github.com/helm/helmMatthew FisherSep 17, 2020via ghsa
9 files changed · +35 8
  • pkg/plugin/installer/local_installer_test.go+1 1 modified
    @@ -48,7 +48,7 @@ func TestLocalInstaller(t *testing.T) {
     		t.Fatal(err)
     	}
     
    -	source := "../testdata/plugdir/echo"
    +	source := "../testdata/plugdir/good/echo"
     	i, err := NewForSource(source, "", home)
     	if err != nil {
     		t.Errorf("unexpected error: %s", err)
    
  • pkg/plugin/installer/vcs_installer_test.go+1 1 modified
    @@ -61,7 +61,7 @@ func TestVCSInstaller(t *testing.T) {
     	}
     
     	source := "https://github.com/adamreese/helm-env"
    -	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
    +	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
     	repo := &testRepo{
     		local: testRepoPath,
     		tags:  []string{"0.1.0", "0.1.1"},
    
  • pkg/plugin/plugin.go+11 2 modified
    @@ -21,9 +21,10 @@ import (
     	"path/filepath"
     	"strings"
     
    -	helm_env "k8s.io/helm/pkg/helm/environment"
    -
     	"github.com/ghodss/yaml"
    +	yaml2 "gopkg.in/yaml.v2"
    +
    +	helm_env "k8s.io/helm/pkg/helm/environment"
     )
     
     const pluginFileName = "plugin.yaml"
    @@ -119,12 +120,20 @@ func LoadDir(dirname string) (*Plugin, error) {
     	}
     
     	plug := &Plugin{Dir: dirname}
    +	if err := validateMeta(data); err != nil {
    +		return nil, err
    +	}
     	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
     		return nil, err
     	}
     	return plug, nil
     }
     
    +func validateMeta(data []byte) error {
    +	// This is done ONLY for validation. We need to use ghodss/yaml for the actual parsing.
    +	return yaml2.UnmarshalStrict(data, &Metadata{})
    +}
    +
     // LoadAll loads all plugins found beneath the base directory.
     //
     // This scans only one directory level.
    
  • pkg/plugin/plugin_test.go+10 3 modified
    @@ -64,7 +64,7 @@ func TestPrepareCommand(t *testing.T) {
     }
     
     func TestLoadDir(t *testing.T) {
    -	dirname := "testdata/plugdir/hello"
    +	dirname := "testdata/plugdir/good/hello"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -92,8 +92,15 @@ func TestLoadDir(t *testing.T) {
     	}
     }
     
    +func TestLoadDirDuplicateEntries(t *testing.T) {
    +	dirname := "testdata/plugdir/bad/duplicate-entries"
    +	if _, err := LoadDir(dirname); err == nil {
    +		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
    +	}
    +}
    +
     func TestDownloader(t *testing.T) {
    -	dirname := "testdata/plugdir/downloader"
    +	dirname := "testdata/plugdir/good/downloader"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -131,7 +138,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("expected empty dir to have 0 plugins")
     	}
     
    -	basedir := "testdata/plugdir"
    +	basedir := "testdata/plugdir/good"
     	plugs, err := LoadAll(basedir)
     	if err != nil {
     		t.Fatalf("Could not load %q: %s", basedir, err)
    
  • pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml+12 0 added
    @@ -0,0 +1,12 @@
    +name: "duplicate-entries"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "echo hello"
    +useTunnel: true
    +ignoreFlags: true
    +hooks:
    +  install: "echo installing..."
    +hooks:
    +  install: "echo installing something different"
    
  • pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/echo/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/hello.sh+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/plugin.yaml+0 1 renamed
    @@ -6,6 +6,5 @@ description: |-
     command: "$HELM_PLUGIN_SELF/hello.sh"
     useTunnel: true
     ignoreFlags: true
    -install: "echo installing..."
     hooks:
       install: "echo installing..."
    
c8d6b01d72c9

validate plugin metadata before loading

https://github.com/helm/helmMatthew FisherSep 17, 2020via ghsa
9 files changed · +35 8
  • pkg/plugin/installer/local_installer_test.go+1 1 modified
    @@ -48,7 +48,7 @@ func TestLocalInstaller(t *testing.T) {
     		t.Fatal(err)
     	}
     
    -	source := "../testdata/plugdir/echo"
    +	source := "../testdata/plugdir/good/echo"
     	i, err := NewForSource(source, "", home)
     	if err != nil {
     		t.Errorf("unexpected error: %s", err)
    
  • pkg/plugin/installer/vcs_installer_test.go+1 1 modified
    @@ -61,7 +61,7 @@ func TestVCSInstaller(t *testing.T) {
     	}
     
     	source := "https://github.com/adamreese/helm-env"
    -	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
    +	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
     	repo := &testRepo{
     		local: testRepoPath,
     		tags:  []string{"0.1.0", "0.1.1"},
    
  • pkg/plugin/plugin.go+11 2 modified
    @@ -22,9 +22,10 @@ import (
     	"path/filepath"
     	"strings"
     
    -	helm_env "k8s.io/helm/pkg/helm/environment"
    -
     	"github.com/ghodss/yaml"
    +	yaml2 "gopkg.in/yaml.v2"
    +
    +	helm_env "k8s.io/helm/pkg/helm/environment"
     )
     
     const pluginFileName = "plugin.yaml"
    @@ -120,12 +121,20 @@ func LoadDir(dirname string) (*Plugin, error) {
     	}
     
     	plug := &Plugin{Dir: dirname}
    +	if err := validateMeta(data); err != nil {
    +		return nil, err
    +	}
     	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
     		return nil, err
     	}
     	return plug, nil
     }
     
    +func validateMeta(data []byte) error {
    +	// This is done ONLY for validation. We need to use ghodss/yaml for the actual parsing.
    +	return yaml2.UnmarshalStrict(data, &Metadata{})
    +}
    +
     // LoadAll loads all plugins found beneath the base directory.
     //
     // This scans only one directory level.
    
  • pkg/plugin/plugin_test.go+10 3 modified
    @@ -64,7 +64,7 @@ func TestPrepareCommand(t *testing.T) {
     }
     
     func TestLoadDir(t *testing.T) {
    -	dirname := "testdata/plugdir/hello"
    +	dirname := "testdata/plugdir/good/hello"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -92,8 +92,15 @@ func TestLoadDir(t *testing.T) {
     	}
     }
     
    +func TestLoadDirDuplicateEntries(t *testing.T) {
    +	dirname := "testdata/plugdir/bad/duplicate-entries"
    +	if _, err := LoadDir(dirname); err == nil {
    +		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
    +	}
    +}
    +
     func TestDownloader(t *testing.T) {
    -	dirname := "testdata/plugdir/downloader"
    +	dirname := "testdata/plugdir/good/downloader"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -131,7 +138,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("expected empty dir to have 0 plugins")
     	}
     
    -	basedir := "testdata/plugdir"
    +	basedir := "testdata/plugdir/good"
     	plugs, err := LoadAll(basedir)
     	if err != nil {
     		t.Fatalf("Could not load %q: %s", basedir, err)
    
  • pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml+12 0 added
    @@ -0,0 +1,12 @@
    +name: "duplicate-entries"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "echo hello"
    +useTunnel: true
    +ignoreFlags: true
    +hooks:
    +  install: "echo installing..."
    +hooks:
    +  install: "echo installing something different"
    
  • pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/echo/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/hello.sh+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/plugin.yaml+0 1 renamed
    @@ -6,6 +6,5 @@ description: |-
     command: "$HELM_PLUGIN_SELF/hello.sh"
     useTunnel: true
     ignoreFlags: true
    -install: "echo installing..."
     hooks:
       install: "echo installing..."
    
ac7c07c37d87

switched to stricter YAML parsing on plugin metadata files

https://github.com/helm/helmMatthew FisherSep 17, 2020via ghsa
9 files changed · +31 8
  • pkg/plugin/installer/local_installer_test.go+1 1 modified
    @@ -37,7 +37,7 @@ func TestLocalInstaller(t *testing.T) {
     		t.Fatal(err)
     	}
     
    -	source := "../testdata/plugdir/echo"
    +	source := "../testdata/plugdir/good/echo"
     	i, err := NewForSource(source, "")
     	if err != nil {
     		t.Fatalf("unexpected error: %s", err)
    
  • pkg/plugin/installer/vcs_installer_test.go+1 1 modified
    @@ -56,7 +56,7 @@ func TestVCSInstaller(t *testing.T) {
     	}
     
     	source := "https://github.com/adamreese/helm-env"
    -	testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo")
    +	testRepoPath, _ := filepath.Abs("../testdata/plugdir/good/echo")
     	repo := &testRepo{
     		local: testRepoPath,
     		tags:  []string{"0.1.0", "0.1.1"},
    
  • pkg/plugin/plugin.go+7 1 modified
    @@ -96,6 +96,12 @@ type Metadata struct {
     	// Downloaders field is used if the plugin supply downloader mechanism
     	// for special protocols.
     	Downloaders []Downloaders `json:"downloaders"`
    +
    +	// UseTunnelDeprecated indicates that this command needs a tunnel.
    +	// Setting this will cause a number of side effects, such as the
    +	// automatic setting of HELM_HOST.
    +	// DEPRECATED and unused, but retained for backwards compatibility with Helm 2 plugins. Remove in Helm 4
    +	UseTunnelDeprecated bool `json:"useTunnel,omitempty"`
     }
     
     // Plugin represents a plugin.
    @@ -200,7 +206,7 @@ func LoadDir(dirname string) (*Plugin, error) {
     	}
     
     	plug := &Plugin{Dir: dirname}
    -	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
    +	if err := yaml.UnmarshalStrict(data, &plug.Metadata); err != nil {
     		return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile)
     	}
     	return plug, validatePluginData(plug, pluginfile)
    
  • pkg/plugin/plugin_test.go+11 4 modified
    @@ -178,7 +178,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
     }
     
     func TestLoadDir(t *testing.T) {
    -	dirname := "testdata/plugdir/hello"
    +	dirname := "testdata/plugdir/good/hello"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -205,8 +205,15 @@ func TestLoadDir(t *testing.T) {
     	}
     }
     
    +func TestLoadDirDuplicateEntries(t *testing.T) {
    +	dirname := "testdata/plugdir/bad/duplicate-entries"
    +	if _, err := LoadDir(dirname); err == nil {
    +		t.Errorf("successfully loaded plugin with duplicate entries when it should've failed")
    +	}
    +}
    +
     func TestDownloader(t *testing.T) {
    -	dirname := "testdata/plugdir/downloader"
    +	dirname := "testdata/plugdir/good/downloader"
     	plug, err := LoadDir(dirname)
     	if err != nil {
     		t.Fatalf("error loading Hello plugin: %s", err)
    @@ -244,7 +251,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("expected empty dir to have 0 plugins")
     	}
     
    -	basedir := "testdata/plugdir"
    +	basedir := "testdata/plugdir/good"
     	plugs, err := LoadAll(basedir)
     	if err != nil {
     		t.Fatalf("Could not load %q: %s", basedir, err)
    @@ -288,7 +295,7 @@ func TestFindPlugins(t *testing.T) {
     		},
     		{
     			name:     "normal",
    -			plugdirs: "./testdata/plugdir",
    +			plugdirs: "./testdata/plugdir/good",
     			expected: 3,
     		},
     	}
    
  • pkg/plugin/testdata/plugdir/bad/duplicate-entries/plugin.yaml+11 0 added
    @@ -0,0 +1,11 @@
    +name: "duplicate-entries"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "echo hello"
    +ignoreFlags: true
    +hooks:
    +  install: "echo installing..."
    +hooks:
    +  install: "echo installing something different"
    
  • pkg/plugin/testdata/plugdir/good/downloader/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/echo/plugin.yaml+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/hello.sh+0 0 renamed
  • pkg/plugin/testdata/plugdir/good/hello/plugin.yaml+0 1 renamed
    @@ -5,6 +5,5 @@ description: |-
       description
     command: "$HELM_PLUGIN_SELF/hello.sh"
     ignoreFlags: true
    -install: "echo installing..."
     hooks:
       install: "echo installing..."
    
6aab63765f99

backported fixes from helm3

https://github.com/helm/helmMatt ButcherSep 15, 2020via ghsa
8 files changed · +109 0
  • pkg/chartutil/requirements.go+13 0 modified
    @@ -17,7 +17,9 @@ package chartutil
     
     import (
     	"errors"
    +	"fmt"
     	"log"
    +	"regexp"
     	"strings"
     	"time"
     
    @@ -219,6 +221,9 @@ func ProcessRequirementsTags(reqs *Requirements, cvals Values) {
     
     }
     
    +// Validate alias names against this regexp
    +var aliasRegexp = regexp.MustCompile("^[a-zA-Z0-9-_]+$")
    +
     func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Chart {
     	var chartFound chart.Chart
     	for _, existingChart := range charts {
    @@ -237,6 +242,11 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch
     		chartFound = *existingChart
     		newMetadata := *existingChart.Metadata
     		if aliasChart.Alias != "" {
    +			// Make sure Alias is well-formed
    +			if !aliasRegexp.MatchString(aliasChart.Alias) {
    +				fmt.Printf("Invalid alias in dependency %q. Skipping.", aliasChart.Name)
    +				continue
    +			}
     			newMetadata.Name = aliasChart.Alias
     		}
     		chartFound.Metadata = &newMetadata
    @@ -286,6 +296,9 @@ func doProcessRequirementsEnabled(c *chart.Chart, v *chart.Config, path string)
     			chartDependencies = append(chartDependencies, chartDependency)
     		}
     		if req.Alias != "" {
    +			if !aliasRegexp.MatchString(req.Alias) {
    +				return fmt.Errorf("illegal alias name in %q", req.Name)
    +			}
     			req.Name = req.Alias
     		}
     	}
    
  • pkg/chartutil/requirements_test.go+21 0 modified
    @@ -370,11 +370,19 @@ func TestGetAliasDependency(t *testing.T) {
     	}
     
     	// Failure case
    +	resetName := req.Dependencies[0].Name
     	req.Dependencies[0].Name = "something-else"
     	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
     		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
     	}
     
    +	// Add a bad alias name
    +	req.Dependencies[0].Name = resetName
    +	req.Dependencies[0].Alias = "$foobar"
    +	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
    +		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
    +	}
    +
     	req.Dependencies[0].Version = "something else which is not in the compatible range"
     	if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
     		t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
    @@ -516,3 +524,16 @@ func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
     	}
     
     }
    +
    +func TestAliasRegexp(t *testing.T) {
    +	for name, shouldPass := range map[string]bool{
    +		"abcdefghijklmnopqrstuvwxyzABCDEFG0987654321_-": true,
    +		"$foo":     false,
    +		"bar$":     false,
    +		"foo\nbar": false,
    +	} {
    +		if aliasRegexp.MatchString(name) != shouldPass {
    +			t.Errorf("name %q failed to pass its test", name)
    +		}
    +	}
    +}
    
  • pkg/plugin/plugin.go+10 0 modified
    @@ -16,6 +16,7 @@ limitations under the License.
     package plugin // import "k8s.io/helm/pkg/plugin"
     
     import (
    +	"fmt"
     	"io/ioutil"
     	"os"
     	"path/filepath"
    @@ -141,13 +142,22 @@ func LoadAll(basedir string) ([]*Plugin, error) {
     		return plugins, nil
     	}
     
    +	loaded := map[string]bool{}
     	for _, yaml := range matches {
     		dir := filepath.Dir(yaml)
     		p, err := LoadDir(dir)
    +		pname := p.Metadata.Name
     		if err != nil {
     			return plugins, err
     		}
    +
    +		if _, ok := loaded[pname]; ok {
    +			fmt.Fprintf(os.Stderr, "A plugin named %q already exists. Skipping.", pname)
    +			continue
    +		}
    +
     		plugins = append(plugins, p)
    +		loaded[pname] = true
     	}
     	return plugins, nil
     }
    
  • pkg/plugin/plugin_test.go+1 0 modified
    @@ -137,6 +137,7 @@ func TestLoadAll(t *testing.T) {
     		t.Fatalf("Could not load %q: %s", basedir, err)
     	}
     
    +	// This would fail if the duplicate plugin were loaded.
     	if l := len(plugs); l != 3 {
     		t.Fatalf("expected 3 plugins, found %d", l)
     	}
    
  • pkg/plugin/testdata/plugdir/hello2/hello.sh+13 0 added
    @@ -0,0 +1,13 @@
    +#!/bin/bash
    +
    +echo "Hello from a Helm plugin"
    +
    +echo "PARAMS"
    +echo $*
    +
    +echo "ENVIRONMENT"
    +echo $TILLER_HOST
    +echo $HELM_HOME
    +
    +$HELM_BIN --host $TILLER_HOST ls --all
    +
    
  • pkg/plugin/testdata/plugdir/hello2/plugin.yaml+11 0 added
    @@ -0,0 +1,11 @@
    +name: "hello"
    +version: "0.1.0"
    +usage: "usage"
    +description: |-
    +  description
    +command: "$HELM_PLUGIN_SELF/hello.sh"
    +useTunnel: true
    +ignoreFlags: true
    +install: "echo installing..."
    +hooks:
    +  install: "echo installing..."
    
  • pkg/repo/index.go+24 0 modified
    @@ -30,6 +30,7 @@ import (
     
     	"github.com/Masterminds/semver"
     	"github.com/ghodss/yaml"
    +	yaml2 "gopkg.in/yaml.v2"
     
     	"k8s.io/helm/pkg/chartutil"
     	"k8s.io/helm/pkg/proto/hapi/chart"
    @@ -83,6 +84,14 @@ type IndexFile struct {
     	PublicKeys []string                 `json:"publicKeys,omitempty"`
     }
     
    +// IndexValidation is used to validate the integrity of an index file
    +type IndexValidation struct {
    +	APIVersion string                 `yaml:"apiVersion"`
    +	Generated  time.Time              `yaml:"generated"`
    +	Entries    map[string]interface{} `yaml:"entries"`
    +	PublicKeys []string               `yaml:"publicKeys,omitempty"`
    +}
    +
     // NewIndexFile initializes an index.
     func NewIndexFile() *IndexFile {
     	return &IndexFile{
    @@ -283,9 +292,14 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
     // This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
     func loadIndex(data []byte) (*IndexFile, error) {
     	i := &IndexFile{}
    +	if err := validateIndex(data); err != nil {
    +		return i, err
    +	}
    +
     	if err := yaml.Unmarshal(data, i); err != nil {
     		return i, err
     	}
    +
     	i.SortEntries()
     	if i.APIVersion == "" {
     		// When we leave Beta, we should remove legacy support and just
    @@ -296,6 +310,16 @@ func loadIndex(data []byte) (*IndexFile, error) {
     	return i, nil
     }
     
    +// validateIndex validates that the index is well-formed.
    +func validateIndex(data []byte) error {
    +	// This is done ONLY for validation. We need to use ghodss/yaml for the actual parsing.
    +	validation := &IndexValidation{}
    +	if err := yaml2.UnmarshalStrict(data, validation); err != nil {
    +		return err
    +	}
    +	return nil
    +}
    +
     // unversionedEntry represents a deprecated pre-Alpha.5 format.
     //
     // This will be removed prior to v2.0.0
    
  • pkg/repo/index_test.go+16 0 modified
    @@ -422,3 +422,19 @@ func TestIndexAdd(t *testing.T) {
     		t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0])
     	}
     }
    +
    +const mockDuplicateIndex = `
    +entries:
    +  foo: {}
    +  bar: {}
    +  baz: {}
    +  bar: {}
    +`
    +
    +func TestValidateIndex(t *testing.T) {
    +	expect := `key "bar" already set in map`
    +	err := validateIndex([]byte(mockDuplicateIndex))
    +	if strings.Contains(expect, err.Error()) {
    +		t.Errorf("Unexpected error: %s", 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

9

News mentions

0

No linked articles in our index yet.