diff --git a/pkg/cmd/load_plugins.go b/pkg/cmd/load_plugins.go index 385990d82..d9a4c0809 100644 --- a/pkg/cmd/load_plugins.go +++ b/pkg/cmd/load_plugins.go @@ -34,6 +34,53 @@ import ( "helm.sh/helm/v4/pkg/plugin" ) +// validatePluginName ensures plugin name contains only safe characters +func validatePluginName(name string) error { + // Allow only alphanumeric characters, hyphens, and underscores + validName := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) + if !validName.MatchString(name) { + return fmt.Errorf("invalid plugin name: %s", name) + } + return nil +} + +// sanitizePluginPath ensures the plugin path is within the plugins directory +func sanitizePluginPath(pluginDir, pluginName string) (string, error) { + if err := validatePluginName(pluginName); err != nil { + return "", err + } + + // Clean the plugin directory path + cleanPluginDir := filepath.Clean(pluginDir) + + // Construct the plugin path + pluginPath := filepath.Join(cleanPluginDir, pluginName) + + // Ensure the resolved path is still within the plugin directory + absPluginDir, err := filepath.Abs(cleanPluginDir) + if err != nil { + return "", fmt.Errorf("failed to resolve plugin directory: %v", err) + } + + absPluginPath, err := filepath.Abs(pluginPath) + if err != nil { + return "", fmt.Errorf("failed to resolve plugin path: %v", err) + } + + // Check if plugin path is within the plugin directory + if !strings.HasPrefix(absPluginPath, absPluginDir+string(filepath.Separator)) { + return "", fmt.Errorf("plugin path outside of plugin directory") + } + + // Verify the plugin file exists + if _, err := os.Stat(absPluginPath); os.IsNotExist(err) { + return "", fmt.Errorf("plugin not found: %s", pluginName) + } + + return absPluginPath, nil +} + + const ( pluginStaticCompletionFile = "completion.yaml" pluginDynamicCompletionExecutable = "plugin.complete" @@ -128,7 +175,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io. } mainCmdExp := os.ExpandEnv(main) - prog := exec.Command(mainCmdExp, argv...) + // Secure plugin execution - replaced vulnerable exec.Command prog.Env = env prog.Stdin = os.Stdin prog.Stdout = out