diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 7ce4f52de..bf1e17a47 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -20,6 +20,7 @@ import ( "github.com/restic/restic/internal/archiver" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/filter" "github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/restic" @@ -298,6 +299,11 @@ func collectRejectByNameFuncs(opts BackupOptions, repo *repository.Repository, t if err != nil { return nil, err } + + if valid, invalidPatterns := filter.ValidatePatterns(excludes); !valid { + return nil, errors.Fatalf("--exclude-file: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + opts.Excludes = append(opts.Excludes, excludes...) } @@ -306,14 +312,27 @@ func collectRejectByNameFuncs(opts BackupOptions, repo *repository.Repository, t if err != nil { return nil, err } + + if valid, invalidPatterns := filter.ValidatePatterns(excludes); !valid { + return nil, errors.Fatalf("--iexclude-file: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + opts.InsensitiveExcludes = append(opts.InsensitiveExcludes, excludes...) } if len(opts.InsensitiveExcludes) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.InsensitiveExcludes); !valid { + return nil, errors.Fatalf("--iexclude: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + fs = append(fs, rejectByInsensitivePattern(opts.InsensitiveExcludes)) } if len(opts.Excludes) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.Excludes); !valid { + return nil, errors.Fatalf("--exclude: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + fs = append(fs, rejectByPattern(opts.Excludes)) } diff --git a/internal/filter/filter.go b/internal/filter/filter.go index ddfa3ebaf..cfacb8cc5 100644 --- a/internal/filter/filter.go +++ b/internal/filter/filter.go @@ -220,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)