Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c
google.golang.org/grpc v1.79.2
google.golang.org/protobuf v1.36.11
helm.sh/helm/v4 v4.1.3
helm.sh/helm/v4 v4.1.4
k8s.io/api v0.35.2
k8s.io/apiextensions-apiserver v0.35.2
k8s.io/apimachinery v0.35.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions vendor/helm.sh/helm/v4/internal/chart/v3/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func (md *Metadata) Validate() error {
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)
}
Expand Down
37 changes: 29 additions & 8 deletions vendor/helm.sh/helm/v4/internal/plugin/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"bytes"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"

Expand Down Expand Up @@ -158,18 +159,27 @@ func LoadDir(dirname string) (Plugin, error) {
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
Expand All @@ -179,9 +189,12 @@ func LoadAll(basedir string) ([]Plugin, error) {
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)
}
Expand All @@ -193,8 +206,12 @@ type findFunc func(pluginsDir string) ([]Plugin, error)
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
Expand Down Expand Up @@ -237,7 +254,11 @@ func makeDescriptorFilter(descriptor Descriptor) filterFunc {

// 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
}
Expand Down
13 changes: 13 additions & 0 deletions vendor/helm.sh/helm/v4/internal/plugin/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ import (
"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 {
Expand Down Expand Up @@ -57,6 +65,11 @@ func (m Metadata) Validate() error {
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"))
}
Expand Down
5 changes: 5 additions & 0 deletions vendor/helm.sh/helm/v4/internal/plugin/metadata_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ func (m *MetadataLegacy) Validate() error {
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 {
Expand Down
7 changes: 7 additions & 0 deletions vendor/helm.sh/helm/v4/internal/plugin/metadata_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ func (m *MetadataV1) Validate() error {
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)
}
Expand Down
3 changes: 3 additions & 0 deletions vendor/helm.sh/helm/v4/pkg/chart/v2/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func (md *Metadata) Validate() error {
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)
}
Expand Down
17 changes: 17 additions & 0 deletions vendor/helm.sh/helm/v4/pkg/chart/v2/util/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ func Expand(dir string, r io.Reader) error {
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.
Expand All @@ -61,6 +72,12 @@ func Expand(dir string, r io.Reader) error {
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 {
Expand Down
2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2075,7 +2075,7 @@ gopkg.in/yaml.v2
gopkg.in/yaml.v3
# gotest.tools/v3 v3.5.2
## explicit; go 1.17
# helm.sh/helm/v4 v4.1.3
# helm.sh/helm/v4 v4.1.4
## explicit; go 1.25.0
helm.sh/helm/v4/internal/chart/v3
helm.sh/helm/v4/internal/chart/v3/loader
Expand Down