package fileio import ( "os" "path/filepath" "strings" "testing" ) func TestSafePathRejectsUnsafeNames(t *testing.T) { for _, name := range []string{"/tmp/x", "a/../x", "a//x", `a\x`, `a\\x`} { if _, err := SafePath(t.TempDir(), name); err == nil { t.Fatalf("SafePath(%q) accepted unsafe path", name) } } } func TestEncodePathComponentIsInjectiveAndSafe(t *testing.T) { root := t.TempDir() seen := make(map[string]string) for _, test := range []struct { value string want string }{ {value: "", want: "%"}, {value: ".", want: "%2E"}, {value: "..", want: "%2E%2E"}, {value: "_", want: "_"}, {value: "a..b", want: "a%2E%2Eb"}, {value: "safe.identifier-9", want: "safe.identifier-9"}, {value: "left/right", want: "left%2Fright"}, {value: "%", want: "%25"}, {value: "~", want: "%7E"}, {value: " a ", want: "%20a%20"}, {value: "é", want: "%C3%A9"}, } { got := EncodePathComponent(test.value) if got != test.want { t.Errorf("EncodePathComponent(%q) = %q, want %q", test.value, got, test.want) } if previous, ok := seen[got]; ok { t.Errorf("EncodePathComponent(%q) = %q, collides with %q", test.value, got, previous) } seen[got] = test.value if _, err := SafePath(root, "components/"+got); err != nil { t.Errorf("EncodePathComponent(%q) produced unsafe component %q: %v", test.value, got, err) } } } func TestWriteBytesIsAtomicAndUsesRequestedModes(t *testing.T) { root := t.TempDir() if err := WriteBytes(root, "nested/value", []byte("value"), 0o700, 0o600); err != nil { t.Fatal(err) } for path, want := range map[string]os.FileMode{filepath.Join(root, "nested"): 0o700, filepath.Join(root, "nested", "value"): 0o600} { info, err := os.Stat(path) if err != nil { t.Fatal(err) } if info.Mode().Perm() != want { t.Fatalf("%s mode=%#o want %#o", path, info.Mode().Perm(), want) } } entries, err := os.ReadDir(filepath.Join(root, "nested")) if err != nil { t.Fatal(err) } for _, entry := range entries { if strings.Contains(entry.Name(), ".tmp-") { t.Fatalf("temporary file remains: %s", entry.Name()) } } } func TestWriteBytesRejectsSymlinkedComponents(t *testing.T) { root := t.TempDir() outside := t.TempDir() if err := os.Symlink(outside, filepath.Join(root, "link")); err != nil { t.Skipf("symbolic links unavailable: %v", err) } if err := WriteBytes(root, "link/value", []byte("value"), 0o700, 0o600); err == nil { t.Fatal("WriteBytes accepted a symlinked directory") } if _, err := os.Stat(filepath.Join(outside, "value")); !os.IsNotExist(err) { t.Fatalf("write escaped through symlink: %v", err) } }