diff options
Diffstat (limited to 'internal/trash/fuzz_test.go')
| -rw-r--r-- | internal/trash/fuzz_test.go | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/internal/trash/fuzz_test.go b/internal/trash/fuzz_test.go new file mode 100644 index 0000000..ffda6d3 --- /dev/null +++ b/internal/trash/fuzz_test.go @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package trash + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +// FuzzPercentRoundTrip: percentEncode leaves only unreserved characters, +// '/' and %XX escapes, and percentDecode gives back exactly the path +// encoded - a newline or invalid byte in a name included. +func FuzzPercentRoundTrip(f *testing.F) { + for _, s := range []string{"/home/x/a b.pdf", "/dl/zażółć.pdf", "/dl/%41", "/dl/new\nline", "/dl/\xff", "%", "%%4", "/dl/[Trash Info]"} { + f.Add(s) + } + f.Fuzz(func(t *testing.T, s string) { + e := percentEncode(s) + for i := 0; i < len(e); i++ { + if c := e[i]; !isUnreserved(c) && c != '/' && c != '%' { + t.Fatalf("percentEncode(%q) = %q holds %q", s, e, c) + } + } + if got := percentDecode(e); got != s { + t.Fatalf("percentDecode(percentEncode(%q)) = %q", s, got) + } + }) +} + +// FuzzParsePath: a trashinfo of any content gives an absolute path or an +// error - never a panic, and never a relative path that Restore would +// resolve against the working directory. +func FuzzParsePath(f *testing.F) { + f.Add("[Trash Info]\nPath=/home/x/a.pdf\nDeletionDate=2026-09-14T10:00:00\n") + f.Add("[Trash Info]\nPath=relative/a.pdf\n") + f.Add("Path=") + f.Add("Path=%2F..%2Fetc") + f.Fuzz(func(t *testing.T, info string) { + p, err := parsePath(info) + if err == nil && !filepath.IsAbs(p) { + t.Fatalf("parsePath(%q) = %q, which is not absolute", info, p) + } + }) +} + +// TestRestoreRefusesUnsafeEntry: an entry name that is not a plain name +// inside the Trash - as a damaged or hand-edited log could hold - is +// refused before anything is read or moved. "../../outside" would +// otherwise reach a trashinfo and a file beside the Trash directory. +func TestRestoreRefusesUnsafeEntry(t *testing.T) { + h := sandbox(t) + share := filepath.Join(h, "share") + write(t, filepath.Join(share, "outside.trashinfo"), "[Trash Info]\nPath="+filepath.Join(h, "restored.txt")+"\n") + write(t, filepath.Join(share, "outside"), "not in the trash") + for _, entry := range []string{"", ".", "..", "../../outside", "a/b"} { + if _, err := Restore(entry); err == nil || !strings.Contains(err.Error(), "bad entry name") { + t.Errorf("Restore(%q) = %v, want a bad entry name error", entry, err) + } + } + if _, err := os.Stat(filepath.Join(h, "restored.txt")); !os.IsNotExist(err) { + t.Errorf("a file outside the Trash was restored: %v", err) + } +} |
