Reject unterminated wildcard character classes in filter allocation

This commit is contained in:
Andrea Mazzoleni 2026-08-26 16:46:10 +02:00
parent dee434f09b
commit 527bd69129
2 changed files with 75 additions and 0 deletions

View file

@ -31,6 +31,38 @@ void content_free(void* void_content)
free(content);
}
/**
* Check that all wildcard character classes are terminated.
*
* Escaped '[' characters are literals and don't start a class.
* Escape characters inside a class are literals, consistently with
* match_class().
*/
static int filter_pattern_class_valid(const char* pattern)
{
const char* i;
int in_class = 0;
for (i = pattern; *i; ++i) {
if (in_class) {
if (*i == ']')
in_class = 0;
} else {
#ifdef _WIN32
if (*i == '^' && i[1] != 0) {
#else
if (*i == '\\' && i[1] != 0) {
#endif
++i;
} else if (*i == '[') {
in_class = 1;
}
}
}
return !in_class;
}
struct snapraid_filter* filter_alloc_file(int direction, const char* root, const char* pattern)
{
struct snapraid_filter* filter;
@ -46,6 +78,12 @@ struct snapraid_filter* filter_alloc_file(int direction, const char* root, const
pathimport(filter->root, sizeof(filter->root), root);
filter->direction = direction;
/* reject unterminated wildcard character classes */
if (!filter_pattern_class_valid(filter->pattern)) {
free(filter);
return 0;
}
/* reject the root directory and standalone absolute directory wildcard as invalid patterns */
if (pathcmp(filter->pattern, "/") == 0 || pathcmp(filter->pattern, "/**/") == 0) {
free(filter);
@ -128,6 +166,12 @@ struct snapraid_filter* filter_alloc_disk(int direction, const char* pattern)
pathimport(filter->pattern, sizeof(filter->pattern), pattern);
filter->direction = direction;
/* reject unterminated wildcard character classes */
if (!filter_pattern_class_valid(filter->pattern)) {
free(filter);
return 0;
}
/* it's a disk filter */
filter->is_disk = 1;
filter->is_abs = 0;

View file

@ -1031,6 +1031,34 @@ struct {
{ "foo/.hidden", 1 },
{ "foo/..hidden", 1 },
/* invalid unterminated character classes */
{ "[", 0 },
{ "foo[", 0 },
{ "[abc", 0 },
{ "foo/[ab", 0 },
{ "/foo/[ab/bar", 0 },
{ "foo/[a-z", 0 },
/* valid character classes */
{ "[abc]", 1 },
{ "foo[abc]", 1 },
{ "foo/[ab]/bar", 1 },
{ "foo/[a-z]/bar", 1 },
#ifdef _WIN32
/* escaped '[' is literal */
{ "foo^[bar", 1 },
/* escaped '^' followed by '[' starts a class */
{ "foo^^[bar", 0 },
#else
/* escaped '[' is literal */
{ "foo\\[bar", 1 },
/* escaped '\' followed by '[' starts a class */
{ "foo\\\\[bar", 0 },
#endif
{ 0, 0 }
};
@ -1043,6 +1071,9 @@ static const struct {
{ "disk", 1 },
{ "disk?", 1 },
{ "data*", 1 },
{ "[ab", 0 },
{ "disk[12", 0 },
{ "disk[12]", 1 },
{ 0, 0 }
};