Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package helm for openSUSE:Factory checked in 
at 2026-04-10 17:53:08
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/helm (Old)
 and      /work/SRC/openSUSE:Factory/.helm.new.21863 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "helm"

Fri Apr 10 17:53:08 2026 rev:100 rq:1345702 version:4.1.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/helm/helm.changes        2026-03-13 
21:21:33.293099766 +0100
+++ /work/SRC/openSUSE:Factory/.helm.new.21863/helm.changes     2026-04-10 
18:02:23.165058401 +0200
@@ -1,0 +2,23 @@
+Thu Apr  9 08:43:18 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- update to 4.1.4 (CVE-2026-35204, CVE-2026-35205, CVE-2026-35206):
+  Helm v4.1.4 is a security fix patch release. Users are encouraged
+  to upgrade for the best experience.
+  * Security fixes
+    - GHSA-hr2v-4r36-88hr Helm Chart extraction output directory
+      collapse via Chart.yaml name dot-segment
+    - GHSA-q5jf-9vfq-h4h7 Plugin verification fails open when .prov
+      is missing, allowing unsigned plugin install
+    - GHSA-vmx8-mqv2-9gmg Path traversal in plugin metadata version
+      enables arbitrary file write outside Helm plugin directory
+  * Changelog
+    - fix: Plugin missing provenance bypass 05fa379 (George
+      Jenkins)
+    - fix: Chart dot-name path bug 4e7994d (George Jenkins)
+    - ignore error plugin loads (cli, getter) 2581943 (George
+      Jenkins)
+    - fix: Plugin version path traversal 36c8539 (George Jenkins)
+    - fix: pin codeql-action/upload-sarif to commit SHA in
+      scorecards workflow c61e086 (Terry Howe)
+
+-------------------------------------------------------------------

Old:
----
  helm-4.1.3.obscpio

New:
----
  helm-4.1.4.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ helm.spec ++++++
--- /var/tmp/diff_new_pack.QPqJ1x/_old  2026-04-10 18:02:27.481236330 +0200
+++ /var/tmp/diff_new_pack.QPqJ1x/_new  2026-04-10 18:02:27.481236330 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           helm
-Version:        4.1.3
+Version:        4.1.4
 Release:        0
 Summary:        The Kubernetes Package Manager
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.QPqJ1x/_old  2026-04-10 18:02:27.529238309 +0200
+++ /var/tmp/diff_new_pack.QPqJ1x/_new  2026-04-10 18:02:27.537238639 +0200
@@ -5,7 +5,7 @@
     <param name="exclude">.git</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
-    <param name="revision">v4.1.3</param>
+    <param name="revision">v4.1.4</param>
     <param name="changesgenerate">disable</param>
   </service>
   <service name="set_version" mode="manual">

++++++ helm-4.1.3.obscpio -> helm-4.1.4.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/cmd/helm/helm_test.go 
new/helm-4.1.4/cmd/helm/helm_test.go
--- old/helm-4.1.3/cmd/helm/helm_test.go        2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/cmd/helm/helm_test.go        2026-04-09 06:58:06.000000000 
+0200
@@ -67,7 +67,7 @@
 
                assert.Empty(t, stdout.String())
 
-               expectedStderr := "Error: plugin \"exitwith\" exited with 
error\n"
+               expectedStderr := "level=WARN msg=\"failed to load plugin 
(ignoring)\" 
plugin_yaml=../../pkg/cmd/testdata/helmhome/helm/plugins/noversion/plugin.yaml 
error=\"failed to load plugin 
\\\"../../pkg/cmd/testdata/helmhome/helm/plugins/noversion\\\": plugin 
`version` is required\"\nError: plugin \"exitwith\" exited with error\n"
                if stderr.String() != expectedStderr {
                        t.Errorf("Expected %q written to stderr: Got %q", 
expectedStderr, stderr.String())
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/chart/v3/metadata.go 
new/helm-4.1.4/internal/chart/v3/metadata.go
--- old/helm-4.1.3/internal/chart/v3/metadata.go        2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/metadata.go        2026-04-09 
06:58:06.000000000 +0200
@@ -112,6 +112,9 @@
                return ValidationError("chart.metadata.name is required")
        }
 
+       if md.Name == "." || md.Name == ".." {
+               return ValidationErrorf("chart.metadata.name %q is not 
allowed", md.Name)
+       }
        if md.Name != filepath.Base(md.Name) {
                return ValidationErrorf("chart.metadata.name %q is invalid", 
md.Name)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/chart/v3/metadata_test.go 
new/helm-4.1.4/internal/chart/v3/metadata_test.go
--- old/helm-4.1.3/internal/chart/v3/metadata_test.go   2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/metadata_test.go   2026-04-09 
06:58:06.000000000 +0200
@@ -41,6 +41,16 @@
                        ValidationError("chart.metadata.name is required"),
                },
                {
+                       "chart with dot name",
+                       &Metadata{Name: ".", APIVersion: "v3", Version: "1.0"},
+                       ValidationError("chart.metadata.name \".\" is not 
allowed"),
+               },
+               {
+                       "chart with dotdot name",
+                       &Metadata{Name: "..", APIVersion: "v3", Version: "1.0"},
+                       ValidationError("chart.metadata.name \"..\" is not 
allowed"),
+               },
+               {
                        "chart without name",
                        &Metadata{Name: "../../test", APIVersion: "v3", 
Version: "1.0"},
                        ValidationError("chart.metadata.name \"../../test\" is 
invalid"),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/chart/v3/util/expand.go 
new/helm-4.1.4/internal/chart/v3/util/expand.go
--- old/helm-4.1.3/internal/chart/v3/util/expand.go     2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/util/expand.go     2026-04-09 
06:58:06.000000000 +0200
@@ -52,6 +52,17 @@
                return errors.New("chart name not specified")
        }
 
+       // Reject chart names that are POSIX path dot-segments or dot-dot 
segments or contain path separators.
+       // A dot-segment name (e.g. ".") causes SecureJoin to resolve to the 
root
+       // directory and extraction then to write files directly into that 
extraction root
+       // instead of a per-chart subdirectory.
+       if chartName == "." || chartName == ".." {
+               return fmt.Errorf("chart name %q is not allowed", chartName)
+       }
+       if chartName != filepath.Base(chartName) {
+               return fmt.Errorf("chart name %q must not contain path 
separators", chartName)
+       }
+
        // Find the base directory
        // The directory needs to be cleaned prior to passing to SecureJoin or 
the location may end up
        // being wrong or returning an error. This was introduced in v0.4.0.
@@ -61,6 +72,12 @@
                return err
        }
 
+       // Defense-in-depth: the chart directory must be a subdirectory of dir,
+       // never dir itself.
+       if chartdir == dir {
+               return fmt.Errorf("chart name %q resolves to the extraction 
root", chartName)
+       }
+
        // Copy all files verbatim. We don't parse these files because parsing 
can remove
        // comments.
        for _, file := range files {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/chart/v3/util/expand_test.go 
new/helm-4.1.4/internal/chart/v3/util/expand_test.go
--- old/helm-4.1.3/internal/chart/v3/util/expand_test.go        2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/util/expand_test.go        2026-04-09 
06:58:06.000000000 +0200
@@ -17,11 +17,73 @@
 package util
 
 import (
+       "archive/tar"
+       "bytes"
+       "compress/gzip"
+       "io/fs"
        "os"
        "path/filepath"
        "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 )
 
+// makeTestChartArchive builds a gzipped tar archive from the given sourceDir 
directory, file entries are prefixed with the given chartName
+func makeTestChartArchive(t *testing.T, chartName, sourceDir string) 
*bytes.Buffer {
+       t.Helper()
+
+       var result bytes.Buffer
+       gw := gzip.NewWriter(&result)
+       tw := tar.NewWriter(gw)
+
+       dir := os.DirFS(sourceDir)
+
+       writeFile := func(relPath string) {
+               t.Helper()
+               f, err := dir.Open(relPath)
+               require.NoError(t, err)
+
+               fStat, err := f.Stat()
+               require.NoError(t, err)
+
+               err = tw.WriteHeader(&tar.Header{
+                       Name: filepath.Join(chartName, relPath),
+                       Mode: int64(fStat.Mode()),
+                       Size: fStat.Size(),
+               })
+               require.NoError(t, err)
+
+               data, err := fs.ReadFile(dir, relPath)
+               require.NoError(t, err)
+               tw.Write(data)
+       }
+
+       err := fs.WalkDir(dir, ".", func(path string, d os.DirEntry, walkErr 
error) error {
+               if walkErr != nil {
+                       return walkErr
+               }
+
+               if d.IsDir() {
+                       return nil
+               }
+
+               writeFile(path)
+
+               return nil
+       })
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = tw.Close()
+       require.NoError(t, err)
+       err = gw.Close()
+       require.NoError(t, err)
+
+       return &result
+}
+
 func TestExpand(t *testing.T) {
        dest := t.TempDir()
 
@@ -75,6 +137,28 @@
        }
 }
 
+func TestExpandError(t *testing.T) {
+       tests := map[string]struct {
+               chartName string
+               chartDir  string
+               wantErr   string
+       }{
+               "dot name":      {"dotname", "testdata/dotname", "not allowed"},
+               "dotdot name":   {"dotdotname", "testdata/dotdotname", "not 
allowed"},
+               "slash in name": {"slashinname", "testdata/slashinname", "must 
not contain path separators"},
+       }
+
+       for name, tt := range tests {
+               t.Run(name, func(t *testing.T) {
+                       archive := makeTestChartArchive(t, tt.chartName, 
tt.chartDir)
+
+                       dest := t.TempDir()
+                       err := Expand(dest, archive)
+                       assert.ErrorContains(t, err, tt.wantErr)
+               })
+       }
+}
+
 func TestExpandFile(t *testing.T) {
        dest := t.TempDir()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/internal/chart/v3/util/testdata/dotdotname/Chart.yaml 
new/helm-4.1.4/internal/chart/v3/util/testdata/dotdotname/Chart.yaml
--- old/helm-4.1.3/internal/chart/v3/util/testdata/dotdotname/Chart.yaml        
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/util/testdata/dotdotname/Chart.yaml        
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: ..
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/internal/chart/v3/util/testdata/dotname/Chart.yaml 
new/helm-4.1.4/internal/chart/v3/util/testdata/dotname/Chart.yaml
--- old/helm-4.1.3/internal/chart/v3/util/testdata/dotname/Chart.yaml   
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/util/testdata/dotname/Chart.yaml   
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: .
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/internal/chart/v3/util/testdata/slashinname/Chart.yaml 
new/helm-4.1.4/internal/chart/v3/util/testdata/slashinname/Chart.yaml
--- old/helm-4.1.3/internal/chart/v3/util/testdata/slashinname/Chart.yaml       
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/internal/chart/v3/util/testdata/slashinname/Chart.yaml       
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: a/../b
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/installer/installer.go 
new/helm-4.1.4/internal/plugin/installer/installer.go
--- old/helm-4.1.3/internal/plugin/installer/installer.go       2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/installer/installer.go       2026-04-09 
06:58:06.000000000 +0200
@@ -98,24 +98,23 @@
 
                // Check if provenance data exists
                if len(provData) == 0 {
-                       // No .prov file found - emit warning but continue 
installation
-                       fmt.Fprintf(os.Stderr, "WARNING: No provenance file 
found for plugin. Plugin is not signed and cannot be verified.\n")
-               } else {
-                       // Provenance data exists - verify the plugin
-                       verification, err := plugin.VerifyPlugin(archiveData, 
provData, filename, opts.Keyring)
-                       if err != nil {
-                               return nil, fmt.Errorf("plugin verification 
failed: %w", err)
-                       }
+                       return nil, fmt.Errorf("plugin verification failed: no 
provenance file (.prov) found")
+               }
+
+               // Provenance data exists - verify the plugin
+               verification, err := plugin.VerifyPlugin(archiveData, provData, 
filename, opts.Keyring)
+               if err != nil {
+                       return nil, fmt.Errorf("plugin verification failed: 
%w", err)
+               }
 
-                       // Collect verification info
-                       result = &VerificationResult{
-                               SignedBy:    make([]string, 0),
-                               Fingerprint: fmt.Sprintf("%X", 
verification.SignedBy.PrimaryKey.Fingerprint),
-                               FileHash:    verification.FileHash,
-                       }
-                       for name := range verification.SignedBy.Identities {
-                               result.SignedBy = append(result.SignedBy, name)
-                       }
+               // Collect verification info
+               result = &VerificationResult{
+                       SignedBy:    make([]string, 0),
+                       Fingerprint: fmt.Sprintf("%X", 
verification.SignedBy.PrimaryKey.Fingerprint),
+                       FileHash:    verification.FileHash,
+               }
+               for name := range verification.SignedBy.Identities {
+                       result.SignedBy = append(result.SignedBy, name)
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/internal/plugin/installer/verification_test.go 
new/helm-4.1.4/internal/plugin/installer/verification_test.go
--- old/helm-4.1.3/internal/plugin/installer/verification_test.go       
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/installer/verification_test.go       
2026-04-09 06:58:06.000000000 +0200
@@ -16,10 +16,8 @@
 package installer
 
 import (
-       "bytes"
        "crypto/sha256"
        "fmt"
-       "io"
        "os"
        "path/filepath"
        "strings"
@@ -44,33 +42,49 @@
        }
        defer os.RemoveAll(installer.Path())
 
-       // Capture stderr to check warning message
-       oldStderr := os.Stderr
-       r, w, _ := os.Pipe()
-       os.Stderr = w
-
-       // Install with verification enabled (should warn but succeed)
+       // Install with verification enabled should fail when .prov is missing
        result, err := InstallWithOptions(installer, Options{Verify: true, 
Keyring: "dummy"})
 
-       // Restore stderr and read captured output
-       w.Close()
-       os.Stderr = oldStderr
-       var buf bytes.Buffer
-       io.Copy(&buf, r)
-       output := buf.String()
-
-       // Should succeed with nil result (no verification performed)
-       if err != nil {
-               t.Fatalf("Expected installation to succeed despite missing 
.prov file, got error: %v", err)
+       // Should fail with a missing provenance error
+       if err == nil {
+               t.Fatal("Expected installation to fail when .prov file is 
missing and verification is enabled")
+       }
+       if !strings.Contains(err.Error(), "no provenance file") {
+               t.Errorf("Expected 'no provenance file' in error message, got: 
%v", err)
        }
        if result != nil {
                t.Errorf("Expected nil verification result when .prov file is 
missing, got: %+v", result)
        }
 
-       // Should contain warning message
-       expectedWarning := "WARNING: No provenance file found for plugin"
-       if !strings.Contains(output, expectedWarning) {
-               t.Errorf("Expected warning message '%s' in output, got: %s", 
expectedWarning, output)
+       // Plugin should NOT be installed
+       if _, err := os.Stat(installer.Path()); !os.IsNotExist(err) {
+               t.Errorf("Plugin should not be installed when verification 
fails due to missing .prov")
+       }
+}
+
+func TestInstallWithOptions_NoVerifyMissingProvenance(t *testing.T) {
+       ensure.HelmHome(t)
+
+       // Create a temporary plugin tarball without .prov file
+       pluginDir := createTestPluginDir(t)
+       pluginTgz := createTarballFromPluginDir(t, pluginDir)
+       defer os.Remove(pluginTgz)
+
+       // Create local installer
+       installer, err := NewLocalInstaller(pluginTgz)
+       if err != nil {
+               t.Fatalf("Failed to create installer: %v", err)
+       }
+       defer os.RemoveAll(installer.Path())
+
+       // Install with verification explicitly disabled should succeed without 
.prov
+       result, err := InstallWithOptions(installer, Options{Verify: false})
+
+       if err != nil {
+               t.Fatalf("Expected installation to succeed with --verify=false, 
got error: %v", err)
+       }
+       if result != nil {
+               t.Errorf("Expected nil verification result when verification is 
disabled, got: %+v", result)
        }
 
        // Plugin should be installed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/loader.go 
new/helm-4.1.4/internal/plugin/loader.go
--- old/helm-4.1.3/internal/plugin/loader.go    2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/internal/plugin/loader.go    2026-04-09 06:58:06.000000000 
+0200
@@ -19,6 +19,7 @@
        "bytes"
        "fmt"
        "io"
+       "log/slog"
        "os"
        "path/filepath"
 
@@ -158,18 +159,27 @@
        return pm.CreatePlugin(dirname, m)
 }
 
-// LoadAll loads all plugins found beneath the base directory.
+func LogIgnorePluginLoadErrorFilterFunc(pluginYAML string, err error) error {
+       slog.Warn("failed to load plugin (ignoring)", 
slog.String("plugin_yaml", pluginYAML), slog.Any("error", err))
+       return nil
+}
+
+// errorFilterFunc is a function that can filter errors during plugin loading
+type ErrorFilterFunc func(string, error) error
+
+// LoadAllDir load all plugins found beneath the base directory, using the 
provided error filter to determine whether to fail on individual plugin load 
errors.
 //
 // This scans only one directory level.
-func LoadAll(basedir string) ([]Plugin, error) {
-       var plugins []Plugin
-       // We want basedir/*/plugin.yaml
+func LoadAllDir(basedir string, errorFilter ErrorFilterFunc) ([]Plugin, error) 
{
+       // We want <basedir>/*/plugin.yaml
        scanpath := filepath.Join(basedir, "*", PluginFileName)
        matches, err := filepath.Glob(scanpath)
        if err != nil {
                return nil, fmt.Errorf("failed to search for plugins in %q: 
%w", scanpath, err)
        }
 
+       plugins := make([]Plugin, 0, len(matches))
+
        // empty dir should load
        if len(matches) == 0 {
                return plugins, nil
@@ -179,9 +189,12 @@
                dir := filepath.Dir(yamlFile)
                p, err := LoadDir(dir)
                if err != nil {
-                       return plugins, err
+                       if errNew := errorFilter(yamlFile, err); errNew != nil {
+                               return plugins, errNew
+                       }
+               } else {
+                       plugins = append(plugins, p)
                }
-               plugins = append(plugins, p)
        }
        return plugins, detectDuplicates(plugins)
 }
@@ -193,8 +206,12 @@
 type filterFunc func(Plugin) bool
 
 // FindPlugins returns a list of plugins that match the descriptor
+// Errors loading a plugin are ignored with a warning
 func FindPlugins(pluginsDirs []string, descriptor Descriptor) ([]Plugin, 
error) {
-       return findPlugins(pluginsDirs, LoadAll, 
makeDescriptorFilter(descriptor))
+       loadAllIgnoreErrors := func(pluginsDir string) ([]Plugin, error) {
+               return LoadAllDir(pluginsDir, 
LogIgnorePluginLoadErrorFilterFunc)
+       }
+       return findPlugins(pluginsDirs, loadAllIgnoreErrors, 
makeDescriptorFilter(descriptor))
 }
 
 // findPlugins is the internal implementation that uses the find and filter 
functions
@@ -237,7 +254,11 @@
 
 // FindPlugin returns a single plugin that matches the descriptor
 func FindPlugin(dirs []string, descriptor Descriptor) (Plugin, error) {
-       plugins, err := FindPlugins(dirs, descriptor)
+       loadAllIgnoreErrors := func(pluginsDir string) ([]Plugin, error) {
+               return LoadAllDir(pluginsDir, 
LogIgnorePluginLoadErrorFilterFunc)
+       }
+
+       plugins, err := findPlugins(dirs, loadAllIgnoreErrors, 
makeDescriptorFilter(descriptor))
        if err != nil {
                return nil, err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/loader_test.go 
new/helm-4.1.4/internal/plugin/loader_test.go
--- old/helm-4.1.3/internal/plugin/loader_test.go       2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/loader_test.go       2026-04-09 
06:58:06.000000000 +0200
@@ -205,16 +205,16 @@
        }
 }
 
-func TestLoadAll(t *testing.T) {
-       // Verify that empty dir loads:
-       {
-               plugs, err := LoadAll("testdata")
-               require.NoError(t, err)
-               assert.Len(t, plugs, 0)
-       }
+func TestLoadAllDir_Empty(t *testing.T) {
+       emptyDir := t.TempDir()
+       plugs, err := LoadAllDir(emptyDir, func(_ string, err error) error { 
return err })
+       require.NoError(t, err)
+       assert.Len(t, plugs, 0)
+}
 
+func TestLoadAllPluginsDir(t *testing.T) {
        basedir := "testdata/plugdir/good"
-       plugs, err := LoadAll(basedir)
+       plugs, err := LoadAllDir(basedir, func(_ string, err error) error { 
return err })
        require.NoError(t, err)
        require.NotEmpty(t, plugs, "expected plugins to be loaded from %s", 
basedir)
 
@@ -233,7 +233,7 @@
        assert.Contains(t, plugsMap, "postrenderer-v1")
 }
 
-func TestFindPlugins(t *testing.T) {
+func TestLoadAllPluginsDir_Zero(t *testing.T) {
        cases := []struct {
                name     string
                plugdirs string
@@ -241,28 +241,20 @@
        }{
                {
                        name:     "plugdirs is empty",
-                       plugdirs: "",
-                       expected: 0,
+                       plugdirs: t.TempDir(),
                },
                {
                        name:     "plugdirs isn't dir",
                        plugdirs: "./plugin_test.go",
-                       expected: 0,
                },
                {
                        name:     "plugdirs doesn't have plugin",
                        plugdirs: ".",
-                       expected: 0,
-               },
-               {
-                       name:     "normal",
-                       plugdirs: "./testdata/plugdir/good",
-                       expected: 7,
                },
        }
        for _, c := range cases {
                t.Run(t.Name(), func(t *testing.T) {
-                       plugin, err := LoadAll(c.plugdirs)
+                       plugin, err := LoadAllDir(c.plugdirs, func(_ string, 
err error) error { return err })
                        require.NoError(t, err)
                        assert.Len(t, plugin, c.expected, "expected %d plugins, 
got %d", c.expected, len(plugin))
                })
@@ -338,6 +330,7 @@
                "correct name field": {
                        yaml: `apiVersion: v1
 name: my-plugin
+version: 1.0.0
 type: cli/v1
 runtime: subprocess
 `,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata.go 
new/helm-4.1.4/internal/plugin/metadata.go
--- old/helm-4.1.3/internal/plugin/metadata.go  2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/internal/plugin/metadata.go  2026-04-09 06:58:06.000000000 
+0200
@@ -19,9 +19,17 @@
        "errors"
        "fmt"
 
+       "github.com/Masterminds/semver/v3"
+
        "helm.sh/helm/v4/internal/plugin/schema"
 )
 
+// isValidSemver checks if the given string is a valid semantic version
+func isValidSemver(v string) bool {
+       _, err := semver.StrictNewVersion(v)
+       return err == nil
+}
+
 // Metadata of a plugin, converted from the "on-disk" legacy or v1 plugin.yaml
 // Specifically, Config and RuntimeConfig are converted to their respective 
types based on the plugin type and runtime
 type Metadata struct {
@@ -57,6 +65,11 @@
                errs = append(errs, fmt.Errorf("invalid plugin name %q: must 
contain only a-z, A-Z, 0-9, _ and -", m.Name))
        }
 
+       // Require version to be valid semver if specified
+       if m.Version != "" && !isValidSemver(m.Version) {
+               errs = append(errs, fmt.Errorf("invalid plugin version %q: must 
be valid semver", m.Version))
+       }
+
        if m.APIVersion == "" {
                errs = append(errs, fmt.Errorf("empty APIVersion"))
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata_legacy.go 
new/helm-4.1.4/internal/plugin/metadata_legacy.go
--- old/helm-4.1.3/internal/plugin/metadata_legacy.go   2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/metadata_legacy.go   2026-04-09 
06:58:06.000000000 +0200
@@ -71,6 +71,11 @@
        if !validPluginName.MatchString(m.Name) {
                return fmt.Errorf("invalid plugin name %q: must contain only 
a-z, A-Z, 0-9, _ and -", m.Name)
        }
+
+       if m.Version != "" && !isValidSemver(m.Version) {
+               return fmt.Errorf("invalid plugin version %q: must be valid 
semver", m.Version)
+       }
+
        m.Usage = sanitizeString(m.Usage)
 
        if len(m.PlatformCommand) > 0 && len(m.Command) > 0 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata_legacy_test.go 
new/helm-4.1.4/internal/plugin/metadata_legacy_test.go
--- old/helm-4.1.3/internal/plugin/metadata_legacy_test.go      2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/metadata_legacy_test.go      2026-04-09 
06:58:06.000000000 +0200
@@ -26,6 +26,10 @@
                "valid metadata": {
                        Name: "myplugin",
                },
+               "valid metadata (empty version)": {
+                       Name:    "myplugin",
+                       Version: "",
+               },
                "valid with command": {
                        Name:    "myplugin",
                        Command: "echo hello",
@@ -59,6 +63,13 @@
                                },
                        },
                },
+               "valid with version": {
+                       Name:    "myplugin",
+                       Version: "1.0.0",
+               },
+               "valid with empty version": {
+                       Name: "myplugin",
+               },
        }
 
        for testName, metadata := range testsValid {
@@ -116,6 +127,14 @@
                                },
                        },
                },
+               "path traversal version": {
+                       Name:    "myplugin",
+                       Version: "../../../../tmp/evil",
+               },
+               "invalid version": {
+                       Name:    "myplugin",
+                       Version: "not-a-version",
+               },
        }
 
        for testName, metadata := range testsInvalid {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata_test.go 
new/helm-4.1.4/internal/plugin/metadata_test.go
--- old/helm-4.1.3/internal/plugin/metadata_test.go     2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/metadata_test.go     2026-04-09 
06:58:06.000000000 +0200
@@ -18,6 +18,8 @@
 import (
        "strings"
        "testing"
+
+       "github.com/stretchr/testify/assert"
 )
 
 func TestValidatePluginData(t *testing.T) {
@@ -72,6 +74,43 @@
        }
 }
 
+func TestMetadataValidateVersion(t *testing.T) {
+       testValid := map[string]struct {
+               version string
+       }{
+               "valid semver":                 {version: "1.0.0"},
+               "valid semver with prerelease": {version: 
"1.2.3-alpha.1+build.123"},
+               "empty version":                {version: ""},
+       }
+
+       testInvalid := map[string]struct {
+               version string
+       }{
+               "valid semver with v prefix": {version: "v1.0.0"},
+               "path traversal":             {version: "../../../../tmp/evil"},
+               "path traversal in version":  {version: "1.0.0/../../etc"},
+               "not a version":              {version: "not-a-version"},
+       }
+
+       for name, tc := range testValid {
+               t.Run(name, func(t *testing.T) {
+                       m := mockSubprocessCLIPlugin(t, "testplugin")
+                       m.metadata.Version = tc.version
+                       err := m.Metadata().Validate()
+                       assert.NoError(t, err)
+               })
+       }
+
+       for name, tc := range testInvalid {
+               t.Run(name, func(t *testing.T) {
+                       m := mockSubprocessCLIPlugin(t, "testplugin")
+                       m.metadata.Version = tc.version
+                       err := m.Metadata().Validate()
+                       assert.ErrorContains(t, err, "invalid plugin version")
+               })
+       }
+}
+
 func TestMetadataValidateMultipleErrors(t *testing.T) {
        // Create metadata with multiple validation issues
        metadata := Metadata{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata_v1.go 
new/helm-4.1.4/internal/plugin/metadata_v1.go
--- old/helm-4.1.3/internal/plugin/metadata_v1.go       2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/metadata_v1.go       2026-04-09 
06:58:06.000000000 +0200
@@ -51,6 +51,13 @@
                return fmt.Errorf("invalid plugin `name`")
        }
 
+       if m.Version == "" {
+               return fmt.Errorf("plugin `version` is required")
+       }
+       if !isValidSemver(m.Version) {
+               return fmt.Errorf("invalid plugin `version` %q: must be valid 
semver", m.Version)
+       }
+
        if m.APIVersion != "v1" {
                return fmt.Errorf("invalid `apiVersion`: %q", m.APIVersion)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/metadata_v1_test.go 
new/helm-4.1.4/internal/plugin/metadata_v1_test.go
--- old/helm-4.1.3/internal/plugin/metadata_v1_test.go  1970-01-01 
01:00:00.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/metadata_v1_test.go  2026-04-09 
06:58:06.000000000 +0200
@@ -0,0 +1,85 @@
+/*
+Copyright The Helm Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package plugin
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestMetadataV1ValidateVersion(t *testing.T) {
+       base := func() MetadataV1 {
+               return MetadataV1{
+                       APIVersion: "v1",
+                       Name:       "myplugin",
+                       Type:       "cli/v1",
+                       Runtime:    "subprocess",
+                       Version:    "1.0.0",
+               }
+       }
+
+       testsValid := map[string]string{
+               "simple version":  "1.0.0",
+               "with prerelease": "1.2.3-alpha.1",
+               "with build meta": "1.2.3+build.123",
+               "full prerelease": "1.2.3-alpha.1+build.123",
+       }
+
+       for name, version := range testsValid {
+               t.Run("valid/"+name, func(t *testing.T) {
+                       m := base()
+                       m.Version = version
+                       assert.NoError(t, m.Validate())
+               })
+       }
+
+       testsInvalid := map[string]struct {
+               version string
+               errMsg  string
+       }{
+               "empty version": {
+                       version: "",
+                       errMsg:  "plugin `version` is required",
+               },
+               "v prefix": {
+                       version: "v1.0.0",
+                       errMsg:  "invalid plugin `version` \"v1.0.0\": must be 
valid semver",
+               },
+               "path traversal": {
+                       version: "../../../../tmp/evil",
+                       errMsg:  "invalid plugin `version`",
+               },
+               "path traversal etc": {
+                       version: "../../../etc/passwd",
+                       errMsg:  "invalid plugin `version`",
+               },
+               "not a version": {
+                       version: "not-a-version",
+                       errMsg:  "invalid plugin `version`",
+               },
+       }
+
+       for name, tc := range testsInvalid {
+               t.Run("invalid/"+name, func(t *testing.T) {
+                       m := base()
+                       m.Version = tc.version
+                       err := m.Validate()
+                       assert.Error(t, err)
+                       assert.Contains(t, err.Error(), tc.errMsg)
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/internal/plugin/plugin_test.go 
new/helm-4.1.4/internal/plugin/plugin_test.go
--- old/helm-4.1.3/internal/plugin/plugin_test.go       2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/plugin_test.go       2026-04-09 
06:58:06.000000000 +0200
@@ -82,7 +82,7 @@
        return &SubprocessPluginRuntime{
                metadata: Metadata{
                        Name:       pluginName,
-                       Version:    "v0.1.2",
+                       Version:    "0.1.2",
                        Type:       "cli/v1",
                        APIVersion: "v1",
                        Runtime:    "subprocess",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/internal/plugin/runtime_subprocess_test.go 
new/helm-4.1.4/internal/plugin/runtime_subprocess_test.go
--- old/helm-4.1.3/internal/plugin/runtime_subprocess_test.go   2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/internal/plugin/runtime_subprocess_test.go   2026-04-09 
06:58:06.000000000 +0200
@@ -41,7 +41,7 @@
 
        md := Metadata{
                Name:       pluginName,
-               Version:    "v0.1.2",
+               Version:    "0.1.2",
                Type:       "cli/v1",
                APIVersion: "v1",
                Runtime:    "subprocess",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/chart/v2/metadata.go 
new/helm-4.1.4/pkg/chart/v2/metadata.go
--- old/helm-4.1.3/pkg/chart/v2/metadata.go     2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/chart/v2/metadata.go     2026-04-09 06:58:06.000000000 
+0200
@@ -112,6 +112,9 @@
                return ValidationError("chart.metadata.name is required")
        }
 
+       if md.Name == "." || md.Name == ".." {
+               return ValidationErrorf("chart.metadata.name %q is not 
allowed", md.Name)
+       }
        if md.Name != filepath.Base(md.Name) {
                return ValidationErrorf("chart.metadata.name %q is invalid", 
md.Name)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/chart/v2/metadata_test.go 
new/helm-4.1.4/pkg/chart/v2/metadata_test.go
--- old/helm-4.1.3/pkg/chart/v2/metadata_test.go        2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/chart/v2/metadata_test.go        2026-04-09 
06:58:06.000000000 +0200
@@ -41,6 +41,16 @@
                        ValidationError("chart.metadata.name is required"),
                },
                {
+                       "chart with dot name",
+                       &Metadata{Name: ".", APIVersion: "v2", Version: "1.0"},
+                       ValidationError("chart.metadata.name \".\" is not 
allowed"),
+               },
+               {
+                       "chart with dotdot name",
+                       &Metadata{Name: "..", APIVersion: "v2", Version: "1.0"},
+                       ValidationError("chart.metadata.name \"..\" is not 
allowed"),
+               },
+               {
                        "chart without name",
                        &Metadata{Name: "../../test", APIVersion: "v2", 
Version: "1.0"},
                        ValidationError("chart.metadata.name \"../../test\" is 
invalid"),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/chart/v2/util/expand.go 
new/helm-4.1.4/pkg/chart/v2/util/expand.go
--- old/helm-4.1.3/pkg/chart/v2/util/expand.go  2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/chart/v2/util/expand.go  2026-04-09 06:58:06.000000000 
+0200
@@ -52,6 +52,17 @@
                return errors.New("chart name not specified")
        }
 
+       // Reject chart names that are POSIX path dot-segments or dot-dot 
segments or contain path separators.
+       // A dot-segment name (e.g. ".") causes SecureJoin to resolve to the 
root
+       // directory and extraction then to write files directly into that 
extraction root
+       // instead of a per-chart subdirectory.
+       if chartName == "." || chartName == ".." {
+               return fmt.Errorf("chart name %q is not allowed", chartName)
+       }
+       if chartName != filepath.Base(chartName) {
+               return fmt.Errorf("chart name %q must not contain path 
separators", chartName)
+       }
+
        // Find the base directory
        // The directory needs to be cleaned prior to passing to SecureJoin or 
the location may end up
        // being wrong or returning an error. This was introduced in v0.4.0.
@@ -61,6 +72,12 @@
                return err
        }
 
+       // Defense-in-depth: the chart directory must be a subdirectory of dir,
+       // never dir itself.
+       if chartdir == dir {
+               return fmt.Errorf("chart name %q resolves to the extraction 
root", chartName)
+       }
+
        // Copy all files verbatim. We don't parse these files because parsing 
can remove
        // comments.
        for _, file := range files {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/chart/v2/util/expand_test.go 
new/helm-4.1.4/pkg/chart/v2/util/expand_test.go
--- old/helm-4.1.3/pkg/chart/v2/util/expand_test.go     2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/chart/v2/util/expand_test.go     2026-04-09 
06:58:06.000000000 +0200
@@ -17,11 +17,73 @@
 package util
 
 import (
+       "archive/tar"
+       "bytes"
+       "compress/gzip"
+       "io/fs"
        "os"
        "path/filepath"
        "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 )
 
+// makeTestChartArchive builds a gzipped tar archive from the given sourceDir 
directory, file entries are prefixed with the given chartName
+func makeTestChartArchive(t *testing.T, chartName, sourceDir string) 
*bytes.Buffer {
+       t.Helper()
+
+       var result bytes.Buffer
+       gw := gzip.NewWriter(&result)
+       tw := tar.NewWriter(gw)
+
+       dir := os.DirFS(sourceDir)
+
+       writeFile := func(relPath string) {
+               t.Helper()
+               f, err := dir.Open(relPath)
+               require.NoError(t, err)
+
+               fStat, err := f.Stat()
+               require.NoError(t, err)
+
+               err = tw.WriteHeader(&tar.Header{
+                       Name: filepath.Join(chartName, relPath),
+                       Mode: int64(fStat.Mode()),
+                       Size: fStat.Size(),
+               })
+               require.NoError(t, err)
+
+               data, err := fs.ReadFile(dir, relPath)
+               require.NoError(t, err)
+               tw.Write(data)
+       }
+
+       err := fs.WalkDir(dir, ".", func(path string, d os.DirEntry, walkErr 
error) error {
+               if walkErr != nil {
+                       return walkErr
+               }
+
+               if d.IsDir() {
+                       return nil
+               }
+
+               writeFile(path)
+
+               return nil
+       })
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = tw.Close()
+       require.NoError(t, err)
+       err = gw.Close()
+       require.NoError(t, err)
+
+       return &result
+}
+
 func TestExpand(t *testing.T) {
        dest := t.TempDir()
 
@@ -75,6 +137,28 @@
        }
 }
 
+func TestExpandError(t *testing.T) {
+       tests := map[string]struct {
+               chartName string
+               chartDir  string
+               wantErr   string
+       }{
+               "dot name":      {"dotname", "testdata/dotname", "not allowed"},
+               "dotdot name":   {"dotdotname", "testdata/dotdotname", "not 
allowed"},
+               "slash in name": {"slashinname", "testdata/slashinname", "must 
not contain path separators"},
+       }
+
+       for name, tt := range tests {
+               t.Run(name, func(t *testing.T) {
+                       archive := makeTestChartArchive(t, tt.chartName, 
tt.chartDir)
+
+                       dest := t.TempDir()
+                       err := Expand(dest, archive)
+                       assert.ErrorContains(t, err, tt.wantErr)
+               })
+       }
+}
+
 func TestExpandFile(t *testing.T) {
        dest := t.TempDir()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/chart/v2/util/testdata/dotdotname/Chart.yaml 
new/helm-4.1.4/pkg/chart/v2/util/testdata/dotdotname/Chart.yaml
--- old/helm-4.1.3/pkg/chart/v2/util/testdata/dotdotname/Chart.yaml     
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/pkg/chart/v2/util/testdata/dotdotname/Chart.yaml     
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: ..
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/chart/v2/util/testdata/dotname/Chart.yaml 
new/helm-4.1.4/pkg/chart/v2/util/testdata/dotname/Chart.yaml
--- old/helm-4.1.3/pkg/chart/v2/util/testdata/dotname/Chart.yaml        
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/pkg/chart/v2/util/testdata/dotname/Chart.yaml        
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: .
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/chart/v2/util/testdata/slashinname/Chart.yaml 
new/helm-4.1.4/pkg/chart/v2/util/testdata/slashinname/Chart.yaml
--- old/helm-4.1.3/pkg/chart/v2/util/testdata/slashinname/Chart.yaml    
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/pkg/chart/v2/util/testdata/slashinname/Chart.yaml    
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,4 @@
+apiVersion: v3
+name: a/../b
+description: A Helm chart for Kubernetes
+version: 0.1.0
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/cmd/plugin_install.go 
new/helm-4.1.4/pkg/cmd/plugin_install.go
--- old/helm-4.1.3/pkg/cmd/plugin_install.go    2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/cmd/plugin_install.go    2026-04-09 06:58:06.000000000 
+0200
@@ -50,11 +50,11 @@
 This command allows you to install a plugin from a url to a VCS repo or a 
local path.
 
 By default, plugin signatures are verified before installation when installing 
from
-tarballs (.tgz or .tar.gz). This requires a corresponding .prov file to be 
available
-alongside the tarball.
+tarballs (.tgz or .tar.gz). A corresponding .prov file must be available 
alongside
+the tarball; installation will fail if it is missing or invalid.
 For local development, plugins installed from local directories are 
automatically
 treated as "local dev" and do not require signatures.
-Use --verify=false to skip signature verification for remote plugins.
+Use --verify=false to explicitly skip signature verification (NOT recommended).
 `
 
 func newPluginInstallCmd(out io.Writer) *cobra.Command {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/cmd/plugin_test.go 
new/helm-4.1.4/pkg/cmd/plugin_test.go
--- old/helm-4.1.3/pkg/cmd/plugin_test.go       2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/cmd/plugin_test.go       2026-04-09 06:58:06.000000000 
+0200
@@ -117,6 +117,7 @@
                {"exitwith", "exitwith code", "This exits with the specified 
exit code", "", []string{"2"}, 2},
                {"fullenv", "show env vars", "show all env vars", 
fullEnvOutput, []string{}, 0},
                {"shortenv", "env stuff", "show the env", 
"HELM_PLUGIN_NAME=shortenv\n", []string{}, 0},
+               // "noversion": plugin is invalid, and should not be loaded
        }
 
        pluginCmds := cmd.Commands()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/cmd/plugin_uninstall.go 
new/helm-4.1.4/pkg/cmd/plugin_uninstall.go
--- old/helm-4.1.3/pkg/cmd/plugin_uninstall.go  2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/cmd/plugin_uninstall.go  2026-04-09 06:58:06.000000000 
+0200
@@ -62,7 +62,7 @@
 
 func (o *pluginUninstallOptions) run(out io.Writer) error {
        slog.Debug("loading installer plugins", "dir", 
settings.PluginsDirectory)
-       plugins, err := plugin.LoadAll(settings.PluginsDirectory)
+       plugins, err := plugin.LoadAllDir(settings.PluginsDirectory, 
plugin.LogIgnorePluginLoadErrorFilterFunc)
        if err != nil {
                return err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/cmd/plugin_update.go 
new/helm-4.1.4/pkg/cmd/plugin_update.go
--- old/helm-4.1.3/pkg/cmd/plugin_update.go     2026-03-11 22:47:44.000000000 
+0100
+++ new/helm-4.1.4/pkg/cmd/plugin_update.go     2026-04-09 06:58:06.000000000 
+0200
@@ -62,7 +62,7 @@
 
 func (o *pluginUpdateOptions) run(out io.Writer) error {
        slog.Debug("loading installed plugins", "path", 
settings.PluginsDirectory)
-       plugins, err := plugin.LoadAll(settings.PluginsDirectory)
+       plugins, err := plugin.LoadAllDir(settings.PluginsDirectory, 
plugin.LogIgnorePluginLoadErrorFilterFunc)
        if err != nil {
                return err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' "old/helm-4.1.3/pkg/cmd/testdata/helm home with 
space/helm/plugins/fullenv/plugin.yaml" "new/helm-4.1.4/pkg/cmd/testdata/helm 
home with space/helm/plugins/fullenv/plugin.yaml"
--- "old/helm-4.1.3/pkg/cmd/testdata/helm home with 
space/helm/plugins/fullenv/plugin.yaml"     2026-03-11 22:47:44.000000000 +0100
+++ "new/helm-4.1.4/pkg/cmd/testdata/helm home with 
space/helm/plugins/fullenv/plugin.yaml"     2026-04-09 06:58:06.000000000 +0200
@@ -1,6 +1,7 @@
 ---
 apiVersion: v1
 name: fullenv
+version: 0.1.0
 type: cli/v1
 runtime: subprocess
 config:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/args/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/args/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/args/plugin.yaml      
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/args/plugin.yaml      
2026-04-09 06:58:06.000000000 +0200
@@ -1,4 +1,5 @@
 name: args
+version: 0.1.0
 type: cli/v1
 apiVersion: v1
 runtime: subprocess
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/echo/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/echo/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/echo/plugin.yaml      
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/echo/plugin.yaml      
2026-04-09 06:58:06.000000000 +0200
@@ -1,4 +1,5 @@
 name: echo
+version: 0.1.0
 type: cli/v1
 apiVersion: v1
 runtime: subprocess
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/plugin.yaml  
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/exitwith/plugin.yaml  
2026-04-09 06:58:06.000000000 +0200
@@ -1,6 +1,7 @@
 ---
 apiVersion: v1
 name: exitwith
+version: 0.1.0
 type: cli/v1
 runtime: subprocess
 config:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/fullenv/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/fullenv/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/fullenv/plugin.yaml   
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/fullenv/plugin.yaml   
2026-04-09 06:58:06.000000000 +0200
@@ -1,6 +1,7 @@
 ---
 apiVersion: v1
 name: fullenv
+version: 0.1.0
 type: cli/v1
 runtime: subprocess
 config:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/noversion/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/noversion/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/noversion/plugin.yaml 
1970-01-01 01:00:00.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/noversion/plugin.yaml 
2026-04-09 06:58:06.000000000 +0200
@@ -0,0 +1,7 @@
+apiVersion: v1
+name: noversion
+type: cli/v1
+runtime: subprocess
+runtimeConfig:
+  platformCommand:
+  - command: "echo hello"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/shortenv/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/shortenv/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/helmhome/helm/plugins/shortenv/plugin.yaml  
2026-03-11 22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/helmhome/helm/plugins/shortenv/plugin.yaml  
2026-04-09 06:58:06.000000000 +0200
@@ -1,6 +1,7 @@
 ---
 apiVersion: v1
 name: shortenv
+version: 0.1.0
 type: cli/v1
 runtime: subprocess
 config:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/helm-4.1.3/pkg/cmd/testdata/testplugin/plugin.yaml 
new/helm-4.1.4/pkg/cmd/testdata/testplugin/plugin.yaml
--- old/helm-4.1.3/pkg/cmd/testdata/testplugin/plugin.yaml      2026-03-11 
22:47:44.000000000 +0100
+++ new/helm-4.1.4/pkg/cmd/testdata/testplugin/plugin.yaml      2026-04-09 
06:58:06.000000000 +0200
@@ -1,6 +1,7 @@
 ---
 apiVersion: v1
 name: testplugin
+version: 0.1.0
 type: cli/v1
 runtime: subprocess
 config:

++++++ helm.obsinfo ++++++
--- /var/tmp/diff_new_pack.QPqJ1x/_old  2026-04-10 18:02:29.885335492 +0200
+++ /var/tmp/diff_new_pack.QPqJ1x/_new  2026-04-10 18:02:29.885335492 +0200
@@ -1,5 +1,5 @@
 name: helm
-version: 4.1.3
-mtime: 1773265664
-commit: c94d381b03be117e7e57908edbf642104e00eb8f
+version: 4.1.4
+mtime: 1775710686
+commit: 05fa37973dc9e42b76e1d2883494c87174b6074f
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/helm/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.helm.new.21863/vendor.tar.gz differ: char 30, line 1

Reply via email to