diff --git a/README.md b/README.md index 21929a9..e857c8f 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,9 @@ If SplitPattern cannot find somewhere to split the pattern (for example, `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in this example). +Note that SplitPattern will also unescape any meta characters in the returned +base string, so that it can be passed straight to os.DirFS(). + Of course, it is your responsibility to decide if the returned base path is "safe" in the context of your application. Perhaps you could use Match() to validate against a list of approved base directories? diff --git a/doublestar_test.go b/doublestar_test.go index 56c49eb..686f83e 100644 --- a/doublestar_test.go +++ b/doublestar_test.go @@ -142,6 +142,7 @@ var matchTests = []MatchTest{ {"**/*.txt", "abc/【test】.txt", true, true, nil, !onWindows, false, false, true, 1, 1}, {"**/【*", "abc/【test】.txt", true, true, nil, !onWindows, false, false, true, 1, 1}, {"**/{a,b}", "a/b", true, true, nil, !onWindows, false, false, true, 5, 5}, + {"a/*/*/d", "a/b/c/d", true, true, nil, false, false, true, true, 1, 1}, // unfortunately, io/fs can't handle this, so neither can Glob =( {"broken-symlink", "broken-symlink", true, true, nil, false, false, true, false, 1, 1}, {"broken-symlink/*", "a", false, false, nil, false, true, true, true, 0, 0}, @@ -150,16 +151,17 @@ var matchTests = []MatchTest{ {"working-sym*/*", "working-symlink/c", true, true, nil, false, false, true, !onWindows, 1, 1}, {"b/**/f", "b/symlink-dir/f", true, true, nil, false, false, false, !onWindows, 2, 2}, {"*/symlink-dir/*", "b/symlink-dir/f", true, true, nil, !onWindows, false, true, !onWindows, 2, 2}, - {"e/\\[owner\\]/*", "e/[owner]/p.tsx", true, true, nil, false, false, true, !onWindows, 1, 0}, - {"e/**", "e/**", true, true, nil, false, false, false, !onWindows, 13, 6}, - {"e/**", "e/*", true, true, nil, false, false, false, !onWindows, 13, 6}, - {"e/**", "e/?", true, true, nil, false, false, false, !onWindows, 13, 6}, - {"e/**", "e/[", true, true, nil, false, false, false, true, 13, 6}, - {"e/**", "e/]", true, true, nil, false, false, false, true, 13, 6}, - {"e/**", "e/[]", true, true, nil, false, false, false, true, 13, 6}, - {"e/**", "e/{", true, true, nil, false, false, false, true, 13, 6}, - {"e/**", "e/}", true, true, nil, false, false, false, true, 13, 6}, - {"e/**", "e/\\", true, true, nil, false, false, false, !onWindows, 13, 6}, + {"e/\\[x\\]/*", "e/[x]/[y]", true, true, nil, false, false, true, true, 1, 1}, + {"e/\\[x\\]/*/z", "e/[x]/[y]/z", true, true, nil, false, false, true, true, 1, 1}, + {"e/**", "e/**", true, true, nil, false, false, false, !onWindows, 14, 6}, + {"e/**", "e/*", true, true, nil, false, false, false, !onWindows, 14, 6}, + {"e/**", "e/?", true, true, nil, false, false, false, !onWindows, 14, 6}, + {"e/**", "e/[", true, true, nil, false, false, false, true, 14, 6}, + {"e/**", "e/]", true, true, nil, false, false, false, true, 14, 6}, + {"e/**", "e/[]", true, true, nil, false, false, false, true, 14, 6}, + {"e/**", "e/{", true, true, nil, false, false, false, true, 14, 6}, + {"e/**", "e/}", true, true, nil, false, false, false, true, 14, 6}, + {"e/**", "e/\\", true, true, nil, false, false, false, !onWindows, 14, 6}, {"e/*", "e/*", true, true, nil, false, false, true, !onWindows, 11, 5}, {"e/?", "e/?", true, true, nil, false, false, true, !onWindows, 7, 4}, {"e/?", "e/*", true, true, nil, false, false, true, !onWindows, 7, 4}, @@ -794,7 +796,7 @@ func TestMain(m *testing.M) { mkdirp("test", "axbxcxdxe", "xxx") mkdirp("test", "axbxcxdxexxx") mkdirp("test", "b") - mkdirp("test", "e") + mkdirp("test", "e", "[x]", "[y]") // create test files touch("test", "a", "abc") @@ -823,6 +825,7 @@ func TestMain(m *testing.M) { touch("test", "e", "{") touch("test", "e", "}") touch("test", "e", "[]") + touch("test", "e", "[x]", "[y]", "z") touch("test", "}") diff --git a/glob.go b/glob.go index bd37a99..5d8b75e 100644 --- a/glob.go +++ b/glob.go @@ -110,7 +110,7 @@ func (g *glob) doGlob(fsys fs.FS, pattern string, m []string, firstSegment, befo } var dirs []string - dirs, err = g.doGlob(fsys, unescapeMeta(dir), matches, false, beforeMeta) + dirs, err = g.doGlob(fsys, dir, matches, false, beforeMeta) if err != nil { return } diff --git a/globwalk.go b/globwalk.go index 923e7f4..3c77c85 100644 --- a/globwalk.go +++ b/globwalk.go @@ -115,7 +115,7 @@ func (g *glob) doGlobWalk(fsys fs.FS, pattern string, firstSegment, beforeMeta b return g.globDirWalk(fsys, unescapeMeta(dir), pattern, firstSegment, beforeMeta, fn) } - return g.doGlobWalk(fsys, unescapeMeta(dir), false, beforeMeta, func(p string, d fs.DirEntry) error { + return g.doGlobWalk(fsys, dir, false, beforeMeta, func(p string, d fs.DirEntry) error { if err := g.globDirWalk(fsys, p, pattern, firstSegment, false, fn); err != nil { return err } diff --git a/utils.go b/utils.go index 49f0ac4..7831e5c 100644 --- a/utils.go +++ b/utils.go @@ -29,6 +29,9 @@ import ( // `meta*/**`), it will return "." and the unaltered pattern (`meta*/**` in // this example). // +// Note that SplitPattern will also unescape any meta characters in the +// returned base string, so that it can be passed straight to os.DirFS(). +// // Of course, it is your responsibility to decide if the returned base path is // "safe" in the context of your application. Perhaps you could use Match() to // validate against a list of approved base directories? @@ -52,7 +55,7 @@ func SplitPattern(p string) (base, pattern string) { if splitIdx == 0 { return "/", p[1:] } else if splitIdx > 0 { - return p[:splitIdx], p[splitIdx+1:] + return unescapeMeta(p[:splitIdx]), p[splitIdx+1:] } return @@ -117,14 +120,14 @@ func FilepathGlob(pattern string, opts ...GlobOption) (matches []string, err err return []string{filepath.FromSlash(pattern)}, nil } - fs := os.DirFS(unescapeMeta(base)) + fs := os.DirFS(base) if matches, err = Glob(fs, f, opts...); err != nil { return nil, err } for i := range matches { // use path.Join because we used ToSlash above to ensure our paths are made // of forward slashes, no matter what the system uses - matches[i] = filepath.FromSlash(path.Join(unescapeMeta(base), matches[i])) + matches[i] = filepath.FromSlash(path.Join(base, matches[i])) } return }