diff --git a/paths.go b/paths.go index 1006516..b8b18c9 100644 --- a/paths.go +++ b/paths.go @@ -30,6 +30,7 @@ package paths import ( + "errors" "fmt" "io" "io/ioutil" @@ -37,6 +38,7 @@ import ( "path/filepath" "strings" "time" + "unicode/utf8" ) // Path represents a path @@ -60,6 +62,24 @@ func New(path ...string) *Path { return res } +// SafeNew behaves as New except if the provided path is not +// a valid UTF8 string: in that case an error is returned. +func SafeNew(paths ...string) (*Path, error) { + if err := pathIsSafe(paths...); err != nil { + return nil, err + } + return New(paths...), nil +} + +func pathIsSafe(paths ...string) error { + for _, path := range paths { + if !utf8.ValidString(path) { + return errors.New("invalid utf8 encoding in path") + } + } + return nil +} + // NewFromFile creates a new Path object using the path name // obtained from the File object (see os.File.Name function). func NewFromFile(file *os.File) *Path { @@ -83,6 +103,15 @@ func (p *Path) Join(paths ...string) *Path { return New(filepath.Join(p.path, filepath.Join(paths...))) } +// SafeJoin behaves as Join except if the provided path is not +// a valid UTF8 string: in that case an error is returned. +func (p *Path) SafeJoin(paths ...string) (*Path, error) { + if err := pathIsSafe(paths...); err != nil { + return nil, err + } + return p.Join(paths...), nil +} + // JoinPath create a new Path by joining the provided paths func (p *Path) JoinPath(paths ...*Path) *Path { res := p.Clone() diff --git a/paths_test.go b/paths_test.go index 7fcf963..fd06111 100644 --- a/paths_test.go +++ b/paths_test.go @@ -50,6 +50,36 @@ func TestPathNew(t *testing.T) { require.Nil(t, test4) } +func TestPathSafetyFeatures(t *testing.T) { + test1, err1 := SafeNew("path") + require.Equal(t, "path", test1.String()) + require.NoError(t, err1) + + test2, err2 := SafeNew("path", "path") + require.Equal(t, filepath.Join("path", "path"), test2.String()) + require.NoError(t, err2) + + test3, err3 := SafeNew("path" + string([]byte{0xC0, 0xAF}) + "path") + require.Nil(t, test3) + require.Error(t, err3) + + test4, err4 := SafeNew("path", "path"+string([]byte{0xC0, 0xAF})+"path") + require.Nil(t, test4) + require.Error(t, err4) + + test5, err5 := test1.SafeJoin("path") + require.Equal(t, filepath.Join("path", "path"), test5.String()) + require.NoError(t, err5) + + test6, err6 := test1.SafeJoin("path" + string([]byte{0xC0, 0xAF}) + "path") + require.Nil(t, test6) + require.Error(t, err6) + + test7, err7 := test1.SafeJoin("path", "path"+string([]byte{0xC0, 0xAF})+"path") + require.Nil(t, test7) + require.Error(t, err7) +} + func TestPath(t *testing.T) { testPath := New("_testdata") require.Equal(t, "_testdata", testPath.String())