Skip to content

Commit ff3de21

Browse files
authored
Merge pull request #445 from jfly/relative-and-absolute-path-handling
fix: correct handling of absolute and relative paths
2 parents 659aa0f + 0ee561e commit ff3de21

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

cmd/format/format.go

+22-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"runtime"
1212
"runtime/pprof"
13+
"strings"
1314
"syscall"
1415
"time"
1516

@@ -173,11 +174,27 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
173174
return fmt.Errorf("exactly one path should be specified when using the --stdin flag")
174175
}
175176
} else {
176-
// checks all paths are contained within the tree root
177-
for _, path := range paths {
178-
rootPath := filepath.Join(cfg.TreeRoot, path)
179-
if _, err = os.Stat(rootPath); err != nil {
180-
return fmt.Errorf("path %s not found within the tree root %s", path, cfg.TreeRoot)
177+
// checks all paths are contained within the tree root and exist
178+
// also "normalize" paths so they're relative to cfg.TreeRoot
179+
for i, path := range paths {
180+
absolutePath, err := filepath.Abs(path)
181+
if err != nil {
182+
return fmt.Errorf("error computing absolute path of %s: %w", path, err)
183+
}
184+
185+
relativePath, err := filepath.Rel(cfg.TreeRoot, absolutePath)
186+
if err != nil {
187+
return fmt.Errorf("error computing relative path from %s to %s: %s", cfg.TreeRoot, absolutePath, err)
188+
}
189+
190+
if strings.HasPrefix(relativePath, "..") {
191+
return fmt.Errorf("path %s not inside the tree root %s", path, cfg.TreeRoot)
192+
}
193+
194+
paths[i] = relativePath
195+
196+
if _, err = os.Stat(absolutePath); err != nil {
197+
return fmt.Errorf("path %s not found", path)
181198
}
182199
}
183200
}
@@ -188,8 +205,6 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
188205
return fmt.Errorf("failed to create walker: %w", err)
189206
}
190207

191-
//
192-
193208
files := make([]*walk.File, BatchSize)
194209
for {
195210
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)

cmd/root_test.go

+40-11
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ func TestGitWorktree(t *testing.T) {
620620

621621
// try with a bad path
622622
_, _, err = treefmt(t, "-C", tempDir, "-c", "haskell", "foo")
623-
as.ErrorContains(err, "path foo not found within the tree root")
623+
as.ErrorContains(err, "path foo not found")
624624

625625
// try with a path not in the git index, e.g. it is skipped
626626
_, err = os.Create(filepath.Join(tempDir, "foo.txt"))
@@ -647,11 +647,20 @@ func TestPathsArg(t *testing.T) {
647647
as.NoError(os.Chdir(cwd))
648648
})
649649

650-
tempDir := test.TempExamples(t)
651-
configPath := filepath.Join(tempDir, "/treefmt.toml")
650+
// create a project root under a temp dir, in order verify behavior with
651+
// files inside of temp dir, but outside of the project root
652+
tempDir := t.TempDir()
653+
treeRoot := filepath.Join(tempDir, "tree-root")
654+
test.TempExamplesInDir(t, treeRoot)
652655

653-
// change working directory to temp root
654-
as.NoError(os.Chdir(tempDir))
656+
configPath := filepath.Join(treeRoot, "/treefmt.toml")
657+
658+
// create a file outside of treeRoot
659+
externalFile, err := os.Create(filepath.Join(tempDir, "outside_tree.go"))
660+
as.NoError(err)
661+
662+
// change working directory to project root
663+
as.NoError(os.Chdir(treeRoot))
655664

656665
// basic config
657666
cfg := &config.Config{
@@ -675,14 +684,29 @@ func TestPathsArg(t *testing.T) {
675684
as.NoError(err)
676685
assertStats(t, as, statz, 2, 2, 2, 0)
677686

687+
// specify an absolute path
688+
absoluteInternalPath, err := filepath.Abs("elm/elm.json")
689+
as.NoError(err)
690+
_, statz, err = treefmt(t, "-c", absoluteInternalPath)
691+
as.NoError(err)
692+
assertStats(t, as, statz, 1, 1, 1, 0)
693+
678694
// specify a bad path
679695
_, _, err = treefmt(t, "-c", "elm/elm.json", "haskell/Nested/Bar.hs")
680-
as.ErrorContains(err, "path haskell/Nested/Bar.hs not found within the tree root")
696+
as.Errorf(err, "path haskell/Nested/Bar.hs not found")
681697

682-
// specify a path outside the tree root
683-
externalPath := filepath.Join(cwd, "go.mod")
684-
_, _, err = treefmt(t, "-c", externalPath)
685-
as.ErrorContains(err, fmt.Sprintf("path %s not found within the tree root", externalPath))
698+
// specify an absolute path outside the tree root
699+
absoluteExternalPath, err := filepath.Abs(externalFile.Name())
700+
as.NoError(err)
701+
as.FileExists(absoluteExternalPath, "exernal file must exist")
702+
_, _, err = treefmt(t, "-c", absoluteExternalPath)
703+
as.Errorf(err, "path %s not found within the tree root", absoluteExternalPath)
704+
705+
// specify a relative path outside the tree root
706+
relativeExternalPath := "../outside_tree.go"
707+
as.FileExists(relativeExternalPath, "exernal file must exist")
708+
_, _, err = treefmt(t, "-c", relativeExternalPath)
709+
as.Errorf(err, "path %s not found within the tree root", relativeExternalPath)
686710
}
687711

688712
func TestStdin(t *testing.T) {
@@ -852,7 +876,12 @@ func TestRunInSubdir(t *testing.T) {
852876
assertStats(t, as, statz, 32, 32, 32, 0)
853877

854878
// specify some explicit paths, relative to the tree root
855-
_, statz, err = treefmt(t, "-c", "elm/elm.json", "haskell/Nested/Foo.hs")
879+
// this should not work, as we're in a subdirectory
880+
_, _, err = treefmt(t, "-c", "elm/elm.json", "haskell/Nested/Foo.hs")
881+
as.ErrorContains(err, "path elm/elm.json not found")
882+
883+
// specify some explicit paths, relative to the current directory
884+
_, statz, err = treefmt(t, "-c", "elm.json", "../haskell/Nested/Foo.hs")
856885
as.NoError(err)
857886
assertStats(t, as, statz, 2, 2, 2, 0)
858887
}

test/temp.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ func WriteConfig(t *testing.T, path string, cfg *config.Config) {
2727

2828
func TempExamples(t *testing.T) string {
2929
tempDir := t.TempDir()
30-
require.NoError(t, cp.Copy("../test/examples", tempDir), "failed to copy test data to temp dir")
30+
TempExamplesInDir(t, tempDir)
31+
return tempDir
32+
}
33+
34+
func TempExamplesInDir(t *testing.T, dir string) {
35+
require.NoError(t, cp.Copy("../test/examples", dir), "failed to copy test data to dir")
3136

3237
// we have second precision mod time tracking, so we wait a second before returning, so we don't trigger false
3338
//positives for things like fail on change
3439
time.Sleep(time.Second)
35-
36-
return tempDir
3740
}
3841

3942
func TempFile(t *testing.T, dir string, pattern string, contents *string) *os.File {

0 commit comments

Comments
 (0)