From 9a7db6675c25346ebebd0d47a263f34359cc5049 Mon Sep 17 00:00:00 2001 From: Lorenz Bausch Date: Fri, 20 May 2022 16:06:25 +0200 Subject: [PATCH 1/4] Restore: validate provided patterns --- cmd/restic/cmd_restore.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 3c7baeb0f..addd36661 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -70,6 +70,28 @@ func runRestore(opts RestoreOptions, gopts GlobalOptions, args []string) error { hasExcludes := len(opts.Exclude) > 0 || len(opts.InsensitiveExclude) > 0 hasIncludes := len(opts.Include) > 0 || len(opts.InsensitiveInclude) > 0 + // Validate provided patterns + if len(opts.Exclude) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.Exclude); !valid { + return errors.Fatalf("--exclude: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + } + if len(opts.InsensitiveExclude) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.InsensitiveExclude); !valid { + return errors.Fatalf("--iexclude: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + } + if len(opts.Include) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.Include); !valid { + return errors.Fatalf("--include: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + } + if len(opts.InsensitiveInclude) > 0 { + if valid, invalidPatterns := filter.ValidatePatterns(opts.InsensitiveInclude); !valid { + return errors.Fatalf("--iinclude: invalid pattern(s) provided:\n%s", strings.Join(invalidPatterns, "\n")) + } + } + for i, str := range opts.InsensitiveExclude { opts.InsensitiveExclude[i] = strings.ToLower(str) } From be524f0b78e7963d9a5c700968fdebe33c9e5783 Mon Sep 17 00:00:00 2001 From: Lorenz Bausch Date: Fri, 20 May 2022 16:07:38 +0200 Subject: [PATCH 2/4] Add testRunRestoreAssumeFailure function --- cmd/restic/integration_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index cefea852a..f15b3d9fd 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -131,6 +131,12 @@ func testRunRestoreIncludes(t testing.TB, gopts GlobalOptions, dir string, snaps rtest.OK(t, runRestore(opts, gopts, []string{snapshotID.String()})) } +func testRunRestoreAssumeFailure(t testing.TB, snapshotID string, opts RestoreOptions, gopts GlobalOptions) error { + err := runRestore(opts, gopts, []string{snapshotID}) + + return err +} + func testRunCheck(t testing.TB, gopts GlobalOptions) { opts := CheckOptions{ ReadData: true, From 7e36ec279d0d588ccf8a5a5b7198e73e49a625b2 Mon Sep 17 00:00:00 2001 From: Lorenz Bausch Date: Fri, 20 May 2022 16:07:53 +0200 Subject: [PATCH 3/4] Test restore fails when using invalid patterns --- cmd/restic/integration_filter_pattern_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cmd/restic/integration_filter_pattern_test.go b/cmd/restic/integration_filter_pattern_test.go index e1534d841..c0c1d932f 100644 --- a/cmd/restic/integration_filter_pattern_test.go +++ b/cmd/restic/integration_filter_pattern_test.go @@ -67,3 +67,40 @@ func TestBackupFailsWhenUsingInvalidPatternsFromFile(t *testing.T) { *[._]log[.-][0-9] !*[._]log[.-][0-9]`, err.Error()) } + +func TestRestoreFailsWhenUsingInvalidPatterns(t *testing.T) { + env, cleanup := withTestEnvironment(t) + defer cleanup() + + testRunInit(t, env.gopts) + + var err error + + // Test --exclude + err = testRunRestoreAssumeFailure(t, "latest", RestoreOptions{Exclude: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts) + + rtest.Equals(t, `Fatal: --exclude: invalid pattern(s) provided: +*[._]log[.-][0-9] +!*[._]log[.-][0-9]`, err.Error()) + + // Test --iexclude + err = testRunRestoreAssumeFailure(t, "latest", RestoreOptions{InsensitiveExclude: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts) + + rtest.Equals(t, `Fatal: --iexclude: invalid pattern(s) provided: +*[._]log[.-][0-9] +!*[._]log[.-][0-9]`, err.Error()) + + // Test --include + err = testRunRestoreAssumeFailure(t, "latest", RestoreOptions{Include: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts) + + rtest.Equals(t, `Fatal: --include: invalid pattern(s) provided: +*[._]log[.-][0-9] +!*[._]log[.-][0-9]`, err.Error()) + + // Test --iinclude + err = testRunRestoreAssumeFailure(t, "latest", RestoreOptions{InsensitiveInclude: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts) + + rtest.Equals(t, `Fatal: --iinclude: invalid pattern(s) provided: +*[._]log[.-][0-9] +!*[._]log[.-][0-9]`, err.Error()) +} From b609523582999b47d0655d2cf4cd8f53b3f9b2ad Mon Sep 17 00:00:00 2001 From: Lorenz Bausch Date: Sat, 9 Jul 2022 22:24:56 +0200 Subject: [PATCH 4/4] Add changelog entry --- changelog/unreleased/pull-3819 | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog/unreleased/pull-3819 diff --git a/changelog/unreleased/pull-3819 b/changelog/unreleased/pull-3819 new file mode 100644 index 000000000..7bbce5005 --- /dev/null +++ b/changelog/unreleased/pull-3819 @@ -0,0 +1,10 @@ +Enhancement: Validate include/exclude patterns before restoring + +Patterns provided to `restic restore` via `--exclude`, `--iexclude`, +`--include` and `--iinclude` weren't validated before running the restore. +Invalid patterns would result in error messages being printed repeatedly +and possibly unwanted files being restored. +restic now validates all patterns before running the restore and aborts with +a fatal error if an invalid pattern is detected. + +https://github.com/restic/restic/pull/3819