diff --git a/find_replace.go b/find_replace.go index 9bdbe79..535d225 100644 --- a/find_replace.go +++ b/find_replace.go @@ -124,6 +124,9 @@ func (fr *findReplace) WalkDir(f *File) { } for _, file := range files { + if file.Type()&os.ModeSymlink != 0 { + continue + } childPath := filepath.Join(f.Path, file.Name()) childFile, err := NewFile(childPath) if err != nil { @@ -151,6 +154,12 @@ func (fr *findReplace) WalkDir(f *File) { // the rename step; the failure is returned so the walker can log it and // continue with siblings. func (fr *findReplace) HandleFile(f *File) error { + if li, err := os.Lstat(f.Path); err != nil { + return err + } else if li.Mode()&os.ModeSymlink != 0 { + return nil + } + info, err := f.Info() if err != nil { return err diff --git a/find_replace_test.go b/find_replace_test.go index 9a54564..e13e5c7 100644 --- a/find_replace_test.go +++ b/find_replace_test.go @@ -660,6 +660,37 @@ func withWorkingDir(t *testing.T, dir string) { t.Cleanup(func() { _ = os.Chdir(prev) }) } + +func TestHandleFileSkipsSymlink(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("symlink test requires unix") + } + dir := t.TempDir() + target := filepath.Join(dir, "target.txt") + if err := os.WriteFile(target, []byte("secret"), 0o644); err != nil { + t.Fatal(err) + } + link := filepath.Join(dir, "link.txt") + if err := os.Symlink(target, link); err != nil { + t.Fatal(err) + } + f, err := NewFile(link) + if err != nil { + t.Fatal(err) + } + fr := findReplace{find: "secret", replace: "public"} + if err := fr.HandleFile(f); err != nil { + t.Fatal(err) + } + got, err := os.ReadFile(target) + if err != nil { + t.Fatal(err) + } + if string(got) != "secret" { + t.Fatalf("symlink target modified: %q", got) + } +} + func CloneRepoToTestDir(b *testing.B, repoUrl string) *File { b.Helper() d := newTestDir(b, "", "*")