From d4dc5a733908d2af7416718d3baea2d05417cd9e Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 26 Aug 2026 13:02:55 +0200 Subject: [PATCH] Reject standalone absolute directory filter pattern /**/ during parsing --- cmdline/elem.c | 4 ++-- cmdline/selftest.c | 41 +++++++++++++++++++++++++++++++++++++++++ doc/snapraid.1 | 1 + doc/snapraid.d | 1 + doc/snapraid.txt | 1 + 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cmdline/elem.c b/cmdline/elem.c index 380eb648..9f650efc 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -45,8 +45,8 @@ struct snapraid_filter* filter_alloc_file(int direction, const char* root, const pathimport(filter->root, sizeof(filter->root), root); filter->direction = direction; - /* reject the root directory as an invalid pattern */ - if (pathcmp(filter->pattern, "/") == 0) { + /* reject the root directory and standalone absolute directory wildcard as invalid patterns */ + if (pathcmp(filter->pattern, "/") == 0 || pathcmp(filter->pattern, "/**/") == 0) { free(filter); return 0; } diff --git a/cmdline/selftest.c b/cmdline/selftest.c index 28d2c0bc..e402d154 100644 --- a/cmdline/selftest.c +++ b/cmdline/selftest.c @@ -896,6 +896,44 @@ static void test_wnmatch(void) } } +struct { + const char* pattern; + int valid; +} FILTER_TEST[] = { + /* invalid standalone root and standalone double-star patterns */ + { "/", 0 }, + { "/**/", 0 }, + + /* valid single and multi-level patterns */ + { "/foo/", 1 }, + { "/foo/**/", 1 }, + { "/**/foo/", 1 }, + { "/foo/**/bar", 1 }, + { "/foo/**/bar/", 1 }, + { "foo/**/bar", 1 }, + { "foo/**/", 1 }, + { "**", 1 }, + { "**/", 1 }, + + { 0, 0 } +}; + +static void test_filter(void) +{ + for (int i = 0; FILTER_TEST[i].pattern; ++i) { + struct snapraid_filter* f = filter_alloc_file(1, "", FILTER_TEST[i].pattern); + if ((f != 0) != FILTER_TEST[i].valid) { + /* LCOV_EXCL_START */ + log_fatal(EINTERNAL, "Failed filter test '%s', expected %s\n", + FILTER_TEST[i].pattern, FILTER_TEST[i].valid ? "valid" : "invalid"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + if (f != 0) + filter_free(f); + } +} + static void test_parse_smartctl(void) { char smartctl[SMART_MAX]; @@ -1169,6 +1207,9 @@ void selftest(void) /* wildcard and path pattern matching */ test_wnmatch(); + /* filter pattern parsing */ + test_filter(); + /* smartctl output parsing */ test_parse_smartctl(); diff --git a/doc/snapraid.1 b/doc/snapraid.1 index 7ad62a93..4cf2293a 100644 --- a/doc/snapraid.1 +++ b/doc/snapraid.1 @@ -1988,6 +1988,7 @@ at any depth in a directory tree without knowing the exact path structure. For example, the pattern \`src/**/main.js\` matches \`src/main.js\` (skipping zero directories), \`src/ui/main.js\` (skipping one directory), and \`src/ui/components/main.js\` (skipping two directories). +The standalone form \`/**/\` is not accepted as a complete pattern. .PP Character classes using square brackets match a single character from a specified set or range. Like the other single character patterns, they do diff --git a/doc/snapraid.d b/doc/snapraid.d index 6d5692ab..42a7af60 100644 --- a/doc/snapraid.d +++ b/doc/snapraid.d @@ -1593,6 +1593,7 @@ Pattern For example, the pattern `src/**/main.js` matches `src/main.js` (skipping zero directories), `src/ui/main.js` (skipping one directory), and `src/ui/components/main.js` (skipping two directories). + The standalone form `/**/` is not accepted as a complete pattern. Character classes using square brackets match a single character from a specified set or range. Like the other single character patterns, they do diff --git a/doc/snapraid.txt b/doc/snapraid.txt index f01f78c9..2073128a 100644 --- a/doc/snapraid.txt +++ b/doc/snapraid.txt @@ -1706,6 +1706,7 @@ at any depth in a directory tree without knowing the exact path structure. For example, the pattern `src/**/main.js` matches `src/main.js` (skipping zero directories), `src/ui/main.js` (skipping one directory), and `src/ui/components/main.js` (skipping two directories). +The standalone form `/**/` is not accepted as a complete pattern. Character classes using square brackets match a single character from a specified set or range. Like the other single character patterns, they do