diff --git a/internal/chart/v3/loader/load_test.go b/internal/chart/v3/loader/load_test.go index a493104bf..9fe9f612e 100644 --- a/internal/chart/v3/loader/load_test.go +++ b/internal/chart/v3/loader/load_test.go @@ -90,6 +90,56 @@ func TestLoadDirWithSymlink(t *testing.T) { verifyDependenciesLock(t, c) } +func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("symlink tests require unix") + } + + tmpDir := t.TempDir() + + // Create minimal chart structure + chartYAML := `apiVersion: v2 +name: test +version: 0.1.0 +` + valuesYAML := `{} +` + tpl := `config: {} +` + if err := os.WriteFile(filepath.Join(tmpDir, "Chart.yaml"), []byte(chartYAML), 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(tmpDir, "values.yaml"), []byte(valuesYAML), 0644); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Join(tmpDir, "templates"), 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(tmpDir, "templates", "config.yaml"), []byte(tpl), 0644); err != nil { + t.Fatal(err) + } + + // Create a broken symlink in templates/ + brokenLink := filepath.Join(tmpDir, "templates", "broken") + if err := os.Symlink("nonexistent", brokenLink); err != nil { + t.Fatal(err) + } + + // Add the broken symlink to .helmignore + if err := os.WriteFile(filepath.Join(tmpDir, ".helmignore"), []byte("templates/broken\n"), 0644); err != nil { + t.Fatal(err) + } + + // Loading should succeed despite the broken symlink + l, err := Loader(tmpDir) + if err != nil { + t.Fatal(err) + } + if _, err := l.Load(); err != nil { + t.Fatalf("loading chart with broken symlink in .helmignore should not fail: %s", err) + } +} + func TestBomTestData(t *testing.T) { testFiles := []string{"frobnitz_with_bom/.helmignore", "frobnitz_with_bom/templates/template.tpl", "frobnitz_with_bom/Chart.yaml"} for _, file := range testFiles { diff --git a/internal/sympath/walk.go b/internal/sympath/walk.go index 812bb68ce..4aca38693 100644 --- a/internal/sympath/walk.go +++ b/internal/sympath/walk.go @@ -21,7 +21,6 @@ limitations under the License. package sympath import ( - "fmt" "log/slog" "os" "path/filepath" @@ -68,7 +67,14 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { if IsSymlink(info) { resolved, err := filepath.EvalSymlinks(path) if err != nil { - return fmt.Errorf("error evaluating symlink %s: %w", path, err) + if os.IsNotExist(err) { + // If a symlink's target does not exist (broken symlink), + // silently skip it. Broken symlinks do not contribute + // content and should not cause errors, especially when + // they match .helmignore patterns. + return nil + } + return err } // 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) diff --git a/pkg/chart/v2/loader/load_test.go b/pkg/chart/v2/loader/load_test.go index e422c30de..7f0530c28 100644 --- a/pkg/chart/v2/loader/load_test.go +++ b/pkg/chart/v2/loader/load_test.go @@ -90,6 +90,56 @@ func TestLoadDirWithSymlink(t *testing.T) { verifyDependenciesLock(t, c) } +func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("symlink tests require unix") + } + + tmpDir := t.TempDir() + + // Create minimal chart structure + chartYAML := `apiVersion: v2 +name: test +version: 0.1.0 +` + valuesYAML := `{} +` + tpl := `config: {} +` + if err := os.WriteFile(filepath.Join(tmpDir, "Chart.yaml"), []byte(chartYAML), 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(tmpDir, "values.yaml"), []byte(valuesYAML), 0644); err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Join(tmpDir, "templates"), 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(tmpDir, "templates", "config.yaml"), []byte(tpl), 0644); err != nil { + t.Fatal(err) + } + + // Create a broken symlink in templates/ + brokenLink := filepath.Join(tmpDir, "templates", "broken") + if err := os.Symlink("nonexistent", brokenLink); err != nil { + t.Fatal(err) + } + + // Add the broken symlink to .helmignore + if err := os.WriteFile(filepath.Join(tmpDir, ".helmignore"), []byte("templates/broken\n"), 0644); err != nil { + t.Fatal(err) + } + + // Loading should succeed despite the broken symlink + l, err := Loader(tmpDir) + if err != nil { + t.Fatal(err) + } + if _, err := l.Load(); err != nil { + t.Fatalf("loading chart with broken symlink in .helmignore should not fail: %s", err) + } +} + func TestBomTestData(t *testing.T) { testFiles := []string{"frobnitz_with_bom/.helmignore", "frobnitz_with_bom/templates/template.tpl", "frobnitz_with_bom/Chart.yaml"} for _, file := range testFiles {