mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
Merge pull request from GHSA-m54r-vrmv-hw33
Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>
This commit is contained in:
parent
055dd41cbe
commit
809e2d999e
4 changed files with 93 additions and 8 deletions
|
|
@ -59,7 +59,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
|
|||
|
||||
found, err := plugin.FindPlugins(settings.PluginsDirectory)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
|
||||
fmt.Fprintf(os.Stderr, "failed to load plugins: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"helm.sh/helm/v3/cmd/helm/require"
|
||||
|
|
@ -81,7 +82,7 @@ func (o *pluginInstallOptions) run(out io.Writer) error {
|
|||
debug("loading plugin from %s", i.Path())
|
||||
p, err := plugin.LoadDir(i.Path())
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "plugin is installed but unusable")
|
||||
}
|
||||
|
||||
if err := runHook(p, plugin.Install); err != nil {
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
|
|
@ -157,18 +159,51 @@ func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) {
|
|||
return main, baseArgs, nil
|
||||
}
|
||||
|
||||
// validPluginName is a regular expression that validates plugin names.
|
||||
//
|
||||
// Plugin names can only contain the ASCII characters a-z, A-Z, 0-9, _ and -.
|
||||
var validPluginName = regexp.MustCompile("^[A-Za-z0-9_-]+$")
|
||||
|
||||
// validatePluginData validates a plugin's YAML data.
|
||||
func validatePluginData(plug *Plugin, filepath string) error {
|
||||
if !validPluginName.MatchString(plug.Metadata.Name) {
|
||||
return fmt.Errorf("invalid plugin name at %q", filepath)
|
||||
}
|
||||
// We could also validate SemVer, executable, and other fields should we so choose.
|
||||
return nil
|
||||
}
|
||||
|
||||
func detectDuplicates(plugs []*Plugin) error {
|
||||
names := map[string]string{}
|
||||
|
||||
for _, plug := range plugs {
|
||||
if oldpath, ok := names[plug.Metadata.Name]; ok {
|
||||
return fmt.Errorf(
|
||||
"two plugins claim the name %q at %q and %q",
|
||||
plug.Metadata.Name,
|
||||
oldpath,
|
||||
plug.Dir,
|
||||
)
|
||||
}
|
||||
names[plug.Metadata.Name] = plug.Dir
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadDir loads a plugin from the given directory.
|
||||
func LoadDir(dirname string) (*Plugin, error) {
|
||||
data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName))
|
||||
pluginfile := filepath.Join(dirname, PluginFileName)
|
||||
data, err := ioutil.ReadFile(pluginfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to read plugin at %q", pluginfile)
|
||||
}
|
||||
|
||||
plug := &Plugin{Dir: dirname}
|
||||
if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "failed to load plugin at %q", pluginfile)
|
||||
}
|
||||
return plug, nil
|
||||
return plug, validatePluginData(plug, pluginfile)
|
||||
}
|
||||
|
||||
// LoadAll loads all plugins found beneath the base directory.
|
||||
|
|
@ -180,7 +215,7 @@ func LoadAll(basedir string) ([]*Plugin, error) {
|
|||
scanpath := filepath.Join(basedir, "*", PluginFileName)
|
||||
matches, err := filepath.Glob(scanpath)
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
return plugins, errors.Wrapf(err, "failed to find plugins in %q", scanpath)
|
||||
}
|
||||
|
||||
if matches == nil {
|
||||
|
|
@ -195,7 +230,7 @@ func LoadAll(basedir string) ([]*Plugin, error) {
|
|||
}
|
||||
plugins = append(plugins, p)
|
||||
}
|
||||
return plugins, nil
|
||||
return plugins, detectDuplicates(plugins)
|
||||
}
|
||||
|
||||
// FindPlugins returns a list of YAML files that describe plugins.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
package plugin // import "helm.sh/helm/v3/pkg/plugin"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
|
@ -320,3 +321,51 @@ func TestSetupEnv(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePluginData(t *testing.T) {
|
||||
for i, item := range []struct {
|
||||
pass bool
|
||||
plug *Plugin
|
||||
}{
|
||||
{true, mockPlugin("abcdefghijklmnopqrstuvwxyz0123456789_-ABC")},
|
||||
{true, mockPlugin("foo-bar-FOO-BAR_1234")},
|
||||
{false, mockPlugin("foo -bar")},
|
||||
{false, mockPlugin("$foo -bar")}, // Test leading chars
|
||||
{false, mockPlugin("foo -bar ")}, // Test trailing chars
|
||||
{false, mockPlugin("foo\nbar")}, // Test newline
|
||||
} {
|
||||
err := validatePluginData(item.plug, fmt.Sprintf("test-%d", i))
|
||||
if item.pass && err != nil {
|
||||
t.Errorf("failed to validate case %d: %s", i, err)
|
||||
} else if !item.pass && err == nil {
|
||||
t.Errorf("expected case %d to fail", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectDuplicates(t *testing.T) {
|
||||
plugs := []*Plugin{
|
||||
mockPlugin("foo"),
|
||||
mockPlugin("bar"),
|
||||
}
|
||||
if err := detectDuplicates(plugs); err != nil {
|
||||
t.Error("no duplicates in the first set")
|
||||
}
|
||||
plugs = append(plugs, mockPlugin("foo"))
|
||||
if err := detectDuplicates(plugs); err == nil {
|
||||
t.Error("duplicates in the second set")
|
||||
}
|
||||
}
|
||||
|
||||
func mockPlugin(name string) *Plugin {
|
||||
return &Plugin{
|
||||
Metadata: &Metadata{
|
||||
Name: name,
|
||||
Version: "v0.1.2",
|
||||
Usage: "Mock plugin",
|
||||
Description: "Mock plugin for testing",
|
||||
Command: "echo mock plugin",
|
||||
},
|
||||
Dir: "no-such-dir",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue