fix(loader): skip broken symlinks matched by .helmignore

A dangling symlink in a chart directory made loading fail, even when
a .helmignore rule excluded it. The walker returned an error from
symwalk before the loader could apply its ignore rules, so the entry
was never checked against .helmignore.

Pass the symlink resolution error to the walk callback instead of
returning it directly. In loadDir, if the entry is a symlink that
cannot be resolved and a .helmignore rule matches it, skip it. A
broken symlink that is not ignored still fails with "error evaluating
symlink", as before.

Apply the change to both the v2 and v3 chart loaders, and add a test
covering the ignored and non-ignored cases.

Signed-off-by: Vimal Kumar Yadav <174954713+vimalyad@users.noreply.github.com>
pull/32705/head
Vimal Kumar Yadav 3 days ago
parent 8a9993eff0
commit e0a2b9e26f

@ -82,6 +82,11 @@ func loadDir(dir string, budget int64) (*chart.Chart, error) {
n = filepath.ToSlash(n) n = filepath.ToSlash(n)
if err != nil { if err != nil {
// A symlink that cannot be resolved is skipped only if a
// .helmignore rule matches it.
if fi != nil && sympath.IsSymlink(fi) && rules.Ignore(n, fi) {
return nil
}
return err return err
} }
if fi.IsDir() { if fi.IsDir() {

@ -69,7 +69,7 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
if IsSymlink(info) { if IsSymlink(info) {
resolved, err := filepath.EvalSymlinks(path) resolved, err := filepath.EvalSymlinks(path)
if err != nil { if err != nil {
return fmt.Errorf("error evaluating symlink %s: %w", path, err) return walkFn(path, info, fmt.Errorf("error evaluating symlink %s: %w", path, err))
} }
// This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons. // This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons.
slog.Info("found symbolic link in path. Contents of linked file included and used", "path", path, "resolved", resolved) slog.Info("found symbolic link in path. Contents of linked file included and used", "path", path, "resolved", resolved)

@ -82,6 +82,11 @@ func loadDir(dir string, budget int64) (*chart.Chart, error) {
n = filepath.ToSlash(n) n = filepath.ToSlash(n)
if err != nil { if err != nil {
// A symlink that cannot be resolved is skipped only if a
// .helmignore rule matches it.
if fi != nil && sympath.IsSymlink(fi) && rules.Ignore(n, fi) {
return nil
}
return err return err
} }
if fi.IsDir() { if fi.IsDir() {

@ -90,6 +90,36 @@ func TestLoadDirWithSymlink(t *testing.T) {
verifyDependenciesLock(t, c) verifyDependenciesLock(t, c)
} }
func TestLoadDirWithBrokenSymlink(t *testing.T) {
tests := []struct {
name string
helmignore string
wantErr bool
}{
{"not ignored", "", true},
{"ignored by .helmignore", "bar\n", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(dir, "Chart.yaml"), []byte("apiVersion: v2\nname: test\nversion: 0.1.0\n"), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(dir, ".helmignore"), []byte(tt.helmignore), 0o644))
require.NoError(t, os.Mkdir(filepath.Join(dir, "templates"), 0o755))
require.NoError(t, os.Symlink("foo", filepath.Join(dir, "templates", "bar")))
c, err := LoadDir(dir)
if tt.wantErr {
require.Error(t, err)
assert.Contains(t, err.Error(), "error evaluating symlink")
return
}
require.NoError(t, err)
assert.Equal(t, "test", c.Name())
assert.Empty(t, c.Templates)
})
}
}
func TestBomTestData(t *testing.T) { func TestBomTestData(t *testing.T) {
testFiles := []string{"frobnitz_with_bom/.helmignore", "frobnitz_with_bom/templates/template.tpl", "frobnitz_with_bom/Chart.yaml"} testFiles := []string{"frobnitz_with_bom/.helmignore", "frobnitz_with_bom/templates/template.tpl", "frobnitz_with_bom/Chart.yaml"}
for _, file := range testFiles { for _, file := range testFiles {

Loading…
Cancel
Save