mirror of
https://github.com/restic/restic.git
synced 2025-12-04 00:21:46 +00:00
Merge pull request #3734 from lbausch/validate-patterns
Validate exclude patterns
This commit is contained in:
@@ -18,6 +18,7 @@ type patternPart struct {
|
||||
|
||||
// Pattern represents a preparsed filter pattern
|
||||
type Pattern struct {
|
||||
original string
|
||||
parts []patternPart
|
||||
isNegated bool
|
||||
}
|
||||
@@ -31,6 +32,9 @@ func prepareStr(str string) ([]string, error) {
|
||||
|
||||
func preparePattern(patternStr string) Pattern {
|
||||
var negate bool
|
||||
|
||||
originalPattern := patternStr
|
||||
|
||||
if patternStr[0] == '!' {
|
||||
negate = true
|
||||
patternStr = patternStr[1:]
|
||||
@@ -48,7 +52,7 @@ func preparePattern(patternStr string) Pattern {
|
||||
parts[i] = patternPart{part, isSimple}
|
||||
}
|
||||
|
||||
return Pattern{parts, negate}
|
||||
return Pattern{originalPattern, parts, negate}
|
||||
}
|
||||
|
||||
// Split p into path components. Assuming p has been Cleaned, no component
|
||||
@@ -130,7 +134,7 @@ func childMatch(pattern Pattern, strs []string) (matched bool, err error) {
|
||||
} else {
|
||||
l = len(strs)
|
||||
}
|
||||
return match(Pattern{pattern.parts[0:l], pattern.isNegated}, strs)
|
||||
return match(Pattern{pattern.original, pattern.parts[0:l], pattern.isNegated}, strs)
|
||||
}
|
||||
|
||||
func hasDoubleWildcard(list Pattern) (ok bool, pos int) {
|
||||
@@ -158,7 +162,7 @@ func match(pattern Pattern, strs []string) (matched bool, err error) {
|
||||
}
|
||||
newPat = append(newPat, pattern.parts[pos+1:]...)
|
||||
|
||||
matched, err := match(Pattern{newPat, pattern.isNegated}, strs)
|
||||
matched, err := match(Pattern{pattern.original, newPat, pattern.isNegated}, strs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -216,6 +220,27 @@ func match(pattern Pattern, strs []string) (matched bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ValidatePatterns validates a slice of patterns.
|
||||
// Returns true if all patterns are valid - false otherwise, along with the invalid patterns.
|
||||
func ValidatePatterns(patterns []string) (allValid bool, invalidPatterns []string) {
|
||||
invalidPatterns = make([]string, 0)
|
||||
|
||||
for _, Pattern := range ParsePatterns(patterns) {
|
||||
// Validate all pattern parts
|
||||
for _, part := range Pattern.parts {
|
||||
// Validate the pattern part by trying to match it against itself
|
||||
if _, validErr := filepath.Match(part.pattern, part.pattern); validErr != nil {
|
||||
invalidPatterns = append(invalidPatterns, Pattern.original)
|
||||
|
||||
// If a single part is invalid, stop processing this pattern
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(invalidPatterns) == 0, invalidPatterns
|
||||
}
|
||||
|
||||
// ParsePatterns prepares a list of patterns for use with List.
|
||||
func ParsePatterns(pattern []string) []Pattern {
|
||||
patpat := make([]Pattern, 0)
|
||||
|
||||
57
internal/filter/filter_patterns_test.go
Normal file
57
internal/filter/filter_patterns_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
// Before Go 1.16 filepath.Match returned early on a failed match,
|
||||
// and thus did not report any later syntax error in the pattern.
|
||||
// https://go.dev/doc/go1.16#path/filepath
|
||||
|
||||
package filter_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/restic/restic/internal/filter"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func TestValidPatterns(t *testing.T) {
|
||||
// Test invalid patterns are detected and returned
|
||||
t.Run("detect-invalid-patterns", func(t *testing.T) {
|
||||
allValid, invalidPatterns := filter.ValidatePatterns([]string{"*.foo", "*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"})
|
||||
|
||||
rtest.Assert(t, allValid == false, "Expected invalid patterns to be detected")
|
||||
|
||||
rtest.Equals(t, invalidPatterns, []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"})
|
||||
})
|
||||
|
||||
// Test all patterns defined in matchTests are valid
|
||||
patterns := make([]string, 0)
|
||||
|
||||
for _, data := range matchTests {
|
||||
patterns = append(patterns, data.pattern)
|
||||
}
|
||||
|
||||
t.Run("validate-patterns", func(t *testing.T) {
|
||||
allValid, invalidPatterns := filter.ValidatePatterns(patterns)
|
||||
|
||||
if !allValid {
|
||||
t.Errorf("Found invalid pattern(s):\n%s", strings.Join(invalidPatterns, "\n"))
|
||||
}
|
||||
})
|
||||
|
||||
// Test all patterns defined in childMatchTests are valid
|
||||
childPatterns := make([]string, 0)
|
||||
|
||||
for _, data := range childMatchTests {
|
||||
childPatterns = append(childPatterns, data.pattern)
|
||||
}
|
||||
|
||||
t.Run("validate-child-patterns", func(t *testing.T) {
|
||||
allValid, invalidPatterns := filter.ValidatePatterns(childPatterns)
|
||||
|
||||
if !allValid {
|
||||
t.Errorf("Found invalid child pattern(s):\n%s", strings.Join(invalidPatterns, "\n"))
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user