diff options
Diffstat (limited to 'internal/plan')
| -rw-r--r-- | internal/plan/fuzz_test.go | 69 | ||||
| -rw-r--r-- | internal/plan/testdata/fuzz/FuzzExpand/a8c52dd03776dd2c | 3 |
2 files changed, 64 insertions, 8 deletions
diff --git a/internal/plan/fuzz_test.go b/internal/plan/fuzz_test.go index 6deb225..b17d3c4 100644 --- a/internal/plan/fuzz_test.go +++ b/internal/plan/fuzz_test.go @@ -3,6 +3,7 @@ package plan import ( + "path" "path/filepath" "strings" "testing" @@ -10,6 +11,7 @@ import ( "krino/internal/config" "krino/internal/scan" + "krino/internal/xdg" ) // FuzzExpand: expanding any template against any file name never panics, @@ -17,7 +19,8 @@ import ( // capture groups there are - MaxIndex and Expand read placeholders alike. // Used as a destination - relative, in the home directory, or bare - a // template never plans a move outside the directory its text names before -// the first placeholder (review M1). +// the first placeholder (review M1), checked against textDir, not the +// planner's own staticDir. func FuzzExpand(f *testing.F) { for _, s := range []string{"{name}", "{stem}{ext}", "{mtime:%Y/%m}", "{1}_{2}", "{{literal}}", "{", "}", "{now:%", "{0}", "{9}", "{99999999999999999999}"} { f.Add(s, "a.b.pdf") @@ -37,15 +40,65 @@ func FuzzExpand(f *testing.F) { if n, err := MaxIndex(tmpl); err == nil && n > 2 && errA == nil { t.Fatalf("Expand(%q) = %q, though it uses {%d} and only 2 groups exist", tmpl, a, n) } + if name == "" || name == "." || name == ".." || strings.ContainsAny(name, "/\x00") { + return // not a name a directory entry can have + } for _, dest := range []string{tmpl, "Out/" + tmpl, "~/docs/" + tmpl} { - in := []Input{{ - File: scan.File{Path: "/r/x.pdf", Rel: "x.pdf", Name: "x.pdf", ModTime: facts.ModTime}, - Rules: []RuleMatch{{Name: "a", Captures: facts.Captures, Actions: []config.Action{{Kind: config.Move, Arg: dest}}}}, - }} - s := Build("/r", in, facts.Now, NoDisk{}, NewClaims())[0].Steps[0] - if strings.ContainsRune(dest, '{') && s.Skip == "" && !within(filepath.Dir(s.Dst), staticDir(dest, "/r")) { - t.Fatalf("destination %q planned %q, outside %q", dest, s.Dst, staticDir(dest, "/r")) + if !strings.ContainsRune(dest, '{') { + continue + } + got := plannedDir(dest, name, facts.Captures, facts) + if got == "" { + continue + } + if base := textDir(dest, "/r", xdg.Home()); !underDir(got, base) { + t.Fatalf("destination %q planned %q for %q, outside %q, the directory its text names", dest, got, name, base) } } }) } + +// plannedDir builds a move of a file named name to dest with captures and +// returns the directory the step would put it in, or "" when the step is +// skipped. +func plannedDir(dest, name string, captures []string, facts Facts) string { + in := []Input{{ + File: scan.File{Path: "/r/" + name, Rel: name, Name: name, ModTime: facts.ModTime}, + Rules: []RuleMatch{{Name: "a", Captures: captures, Actions: []config.Action{{Kind: config.Move, Arg: dest}}}}, + }} + s := Build("/r", in, facts.Now, NoDisk{}, NewClaims())[0].Steps[0] + if s.Skip != "" { + return "" + } + return filepath.Dir(s.Dst) +} + +// textDir is the directory a destination's text names before its first +// '{' (spec 15.1), worked out here by hand rather than by staticDir: the +// text up to the last '/' before the brace, "~" as the home directory, a +// relative path under root. +func textDir(dest, root, home string) string { + prefix := dest[:strings.IndexByte(dest, '{')] + switch cut := strings.LastIndexByte(prefix, '/'); { + case cut < 0: + prefix = "" + case cut == 0: + return "/" + default: + prefix = prefix[:cut] + } + switch { + case prefix == "~": + return home + case strings.HasPrefix(prefix, "~/"): + return path.Join(home, prefix[2:]) + case strings.HasPrefix(prefix, "/"): + return path.Clean(prefix) + } + return path.Join(root, prefix) +} + +// underDir reports whether path is dir or lies under it, by whole segments. +func underDir(path, dir string) bool { + return dir == "/" || path == dir || strings.HasPrefix(path, dir+"/") +} diff --git a/internal/plan/testdata/fuzz/FuzzExpand/a8c52dd03776dd2c b/internal/plan/testdata/fuzz/FuzzExpand/a8c52dd03776dd2c new file mode 100644 index 0000000..0201563 --- /dev/null +++ b/internal/plan/testdata/fuzz/FuzzExpand/a8c52dd03776dd2c @@ -0,0 +1,3 @@ +go test fuzz v1 +string("{{/{2}") +string("0") |
