]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: event-filter - Support escaping wildcards in values
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 2 May 2023 13:08:13 +0000 (16:08 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Sun, 21 May 2023 17:41:38 +0000 (17:41 +0000)
src/lib/event-filter-lexer.l
src/lib/event-filter-parser.y
src/lib/event-filter.c
src/lib/test-event-filter-parser.c
src/lib/test-event-filter.c

index 8ff73e1acb3c4fe5d21f8c52add23a8ba72415f4..8c28400a02ec0ebd3e4e8e0b9830774db590570f 100644 (file)
@@ -56,8 +56,8 @@ static size_t event_filter_parser_input_proc(char *buf, size_t size, yyscan_t sc
                                }
        /* Note: these have to match the event_filter_append_escaped() behavior */
 <string>[^\\"]+                        { str_append(str_buf, yytext); }
-<string>\\\\                   { str_append_c(str_buf, '\\'); }
-<string>\\\"                   { str_append_c(str_buf, '"'); }
+<string>\\\\                   { str_append(str_buf, yytext); }
+<string>\\\"                   { str_append(str_buf, yytext); }
 <string>\\.                    { str_append(str_buf, yytext); }
 
 [Aa][Nn][Dd]                   { return AND; }
index 11d7746cf726c21b9dbd4f83aa5e7ffe14fab5a0..21d18be397826cff3997baed1ee79db0f935b8b0 100644 (file)
@@ -12,6 +12,7 @@
 #include <ctype.h>
 
 #include "lib.h"
+#include "strescape.h"
 #include "str-parse.h"
 #include "wildcard-match.h"
 #include "lib-event-private.h"
@@ -60,14 +61,18 @@ static struct event_filter_node *key_value(struct event_filter_parser_state *sta
        switch (type) {
        case EVENT_FILTER_NODE_TYPE_LOGIC:
                i_unreached();
-       case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
-               node->str = p_strdup(state->pool, b);
-               if (wildcard_is_literal(node->str))
+       case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD: {
+               if (wildcard_is_escaped_literal(b)) {
                        node->type = EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT;
+                       node->str = str_unescape(p_strdup(state->pool, b));
+               } else {
+                       node->str = p_strdup(state->pool, b);
+               }
                break;
+       }
        case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION: {
                const char *colon = strrchr(b, ':');
-               const char *file;
+               char *file;
                uintmax_t line;
 
                /* split "filename:line-number", but also handle "filename" */
@@ -83,19 +88,20 @@ static struct event_filter_node *key_value(struct event_filter_parser_state *sta
                        line = 0;
                }
 
-               node->str = file;
+               node->str = str_unescape(file);
                node->intmax = line;
                break;
        }
        case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
                if (!event_filter_category_to_log_type(b, &node->category.log_type)) {
-                       node->category.name = p_strdup(state->pool, b);
+                       node->category.name = str_unescape(p_strdup(state->pool, b));
                        node->category.ptr = event_category_find_registered(b);
                }
                break;
-       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
+       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD: {
+               char *b_duped = p_strdup(state->pool, b);
                node->field.key = p_strdup(state->pool, a);
-               node->field.value.str = p_strdup(state->pool, b);
+               node->field.value.str = b_duped;
                node->field.value_type = EVENT_FIELD_VALUE_TYPE_STR;
 
                /* Filter currently supports only comparing strings
@@ -147,13 +153,16 @@ static struct event_filter_node *key_value(struct event_filter_parser_state *sta
                                break;
                        }
 
-                       if (wildcard_is_literal(node->field.value.str))
+                       if (wildcard_is_escaped_literal(b)) {
                                node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT;
-                       else if (strspn(b, "0123456789*?") == strlen(b))
+                               str_unescape(b_duped);
+                       } else if (strspn(b, "0123456789*?") == strlen(b)) {
                                node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD;
+                       }
                }
 
                break;
+       }
        case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD:
@@ -226,7 +235,7 @@ key_value : key op value    {
          ;
 
 key : TOKEN                    { $$ = $1; }
-    | STRING                   { $$ = $1; }
+    | STRING                   { $$ = str_unescape(t_strdup_noconst($1)); }
     ;
 
 value : TOKEN                  { $$ = $1; }
index 795f54fe2854d784697f8edad444b97185f575d7..59a5792d05b8f2c49ed3be68dd21a1c681c02304 100644 (file)
 #include "event-filter.h"
 #include "event-filter-private.h"
 
-/* Note: this has to match the regexp behavior in the event filter lexer file */
-#define event_filter_append_escaped(dst, str) \
-       str_append_escaped((dst), (str), strlen(str))
-
 enum event_filter_code {
        EVENT_FILTER_CODE_NAME          = 'n',
        EVENT_FILTER_CODE_SOURCE        = 's',
@@ -319,6 +315,17 @@ void event_filter_merge_with_context(struct event_filter *dest,
        event_filter_merge_with_context_internal(dest, src, new_context, TRUE);
 }
 
+static void
+event_filter_append_escaped(string_t *dest, const char *src, bool wildcard)
+{
+       if (!wildcard)
+               str_append_escaped(dest, src, strlen(src));
+       else {
+               /* src is already escaped */
+               str_append(dest, src);
+       }
+}
+
 static const char *
 event_filter_export_query_expr_op(enum event_filter_node_op op)
 {
@@ -379,14 +386,15 @@ event_filter_export_query_expr(const struct event_filter_query_internal *query,
                str_append(dest, "event");
                str_append(dest, event_filter_export_query_expr_op(node->op));
                str_append_c(dest, '"');
-               event_filter_append_escaped(dest, node->str);
+               event_filter_append_escaped(dest, node->str,
+                       node->type == EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD);
                str_append_c(dest, '"');
                break;
        case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
                str_append(dest, "source_location");
                str_append(dest, event_filter_export_query_expr_op(node->op));
                str_append_c(dest, '"');
-               event_filter_append_escaped(dest, node->str);
+               event_filter_append_escaped(dest, node->str, FALSE);
                if (node->intmax != 0)
                        str_printfa(dest, ":%ju", node->intmax);
                str_append_c(dest, '"');
@@ -396,7 +404,7 @@ event_filter_export_query_expr(const struct event_filter_query_internal *query,
                str_append(dest, event_filter_export_query_expr_op(node->op));
                if (node->category.name != NULL) {
                        str_append_c(dest, '"');
-                       event_filter_append_escaped(dest, node->category.name);
+                       event_filter_append_escaped(dest, node->category.name, FALSE);
                        str_append_c(dest, '"');
                } else
                        str_append(dest, event_filter_category_from_log_type(node->category.log_type));
@@ -405,11 +413,12 @@ event_filter_export_query_expr(const struct event_filter_query_internal *query,
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_NUMERIC_WILDCARD:
                str_append_c(dest, '"');
-               event_filter_append_escaped(dest, node->field.key);
+               event_filter_append_escaped(dest, node->field.key, FALSE);
                str_append_c(dest, '"');
                str_append(dest, event_filter_export_query_expr_op(node->op));
                str_append_c(dest, '"');
-               event_filter_append_escaped(dest, node->field.value.str);
+               event_filter_append_escaped(dest, node->field.value.str,
+                       node->type != EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT);
                str_append_c(dest, '"');
                break;
        }
@@ -523,7 +532,7 @@ event_match_strlist_recursive(struct event *event,
                array_foreach_elem(&field->value.strlist, value) {
                        *seen = TRUE;
                        match = use_strcmp ? strcasecmp(value, wanted_value) == 0 :
-                               wildcard_match_icase(value, wanted_value);
+                               wildcard_match_escaped_icase(value, wanted_value);
                        if (match)
                                return TRUE;
                }
@@ -627,7 +636,7 @@ event_match_field(struct event *event, struct event_filter_node *node,
                if (use_strcmp)
                        return strcasecmp(field->value.str, wanted_field->value.str) == 0;
                else
-                       return wildcard_match_icase(field->value.str, wanted_field->value.str);
+                       return wildcard_match_escaped_icase(field->value.str, wanted_field->value.str);
        case EVENT_FIELD_VALUE_TYPE_INTMAX:
                if (node->ambiguous_unit) {
                        if (!node->warned_ambiguous_unit) {
@@ -687,7 +696,7 @@ event_match_field(struct event *event, struct event_filter_node *node,
                } else {
                        char tmp[MAX_INT_STRLEN];
                        i_snprintf(tmp, sizeof(tmp), "%jd", field->value.intmax);
-                       return wildcard_match_icase(tmp, wanted_field->value.str);
+                       return wildcard_match_escaped_icase(tmp, wanted_field->value.str);
                }
        case EVENT_FIELD_VALUE_TYPE_TIMEVAL: {
                /* Filtering for timeval fields is not implemented. */
@@ -745,8 +754,8 @@ event_match_field(struct event *event, struct event_filter_node *node,
                }
                bool ret;
                T_BEGIN {
-                       ret = wildcard_match_icase(net_ip2addr(&field->value.ip),
-                                                  wanted_field->value.str);
+                       ret = wildcard_match_escaped_icase(net_ip2addr(&field->value.ip),
+                                                          wanted_field->value.str);
                } T_END;
                return ret;
        case EVENT_FIELD_VALUE_TYPE_STRLIST:
@@ -792,7 +801,7 @@ event_filter_query_match_cmp(struct event_filter_node *node,
                               strcmp(event->sending_name, node->str) == 0;
                case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
                        return (event->sending_name != NULL) &&
-                              wildcard_match(event->sending_name, node->str);
+                              wildcard_match_escaped(event->sending_name, node->str);
                case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
                        return !((source_linenum != node->intmax &&
                                  node->intmax != 0) ||
index 63137d89c9b8a281cb6782153812cd582c09d464..b0bc137574b6d4ffc27e928c02d3aa9c6943d4c9 100644 (file)
@@ -241,10 +241,10 @@ static void testcase(const char *name, const char *input, const char *exp,
 
                event_filter_export(filter, tmp);
 
-               test_out_quiet(t_strdup_printf("input: %s", input),
+               test_out_quiet(t_strdup_printf("input: %s (%s)", input, str_c(tmp)),
                               strcmp(exp, str_c(tmp)) == 0);
        } else {
-               test_out_quiet(t_strdup_printf("input: %s", input),
+               test_out_quiet(t_strdup_printf("input: %s (%s)", input, error),
                               str_begins_with(error, exp));
        }
 
index 6fdfd238b838c3c343c365b2ba71b51acd365e18..a3cc72cff66f34be06b8b346ef242f82a10b9edf 100644 (file)
@@ -4,6 +4,48 @@
 #include "ioloop.h"
 #include "event-filter-private.h"
 
+static void test_event_filter_strings(void)
+{
+       struct event_filter *filter;
+       const char *error;
+       const struct failure_context failure_ctx = {
+               .type = LOG_TYPE_DEBUG
+       };
+
+       test_begin("event filter: strings");
+
+       struct event *e = event_create(NULL);
+       event_add_str(e, "str", "hello \\world");
+
+       struct event *e2 = event_create(NULL);
+       event_add_str(e2, "str", "hello *world");
+
+       /* "quoting" works with \escaping */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str = \"hello \\\\world\"", filter, &error) == 0);
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       test_assert(!event_filter_match(filter, e2, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* wildcards work inside quotes */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str = \"hello *world\"", filter, &error) == 0);
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       test_assert(event_filter_match(filter, e2, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* escaped wildcard is an exact string */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str = \"hello \\*world\"", filter, &error) == 0);
+       test_assert(!event_filter_match(filter, e, &failure_ctx));
+       test_assert(event_filter_match(filter, e2, &failure_ctx));
+       event_filter_unref(&filter);
+
+       event_unref(&e);
+       event_unref(&e2);
+       test_end();
+}
+
 static void test_event_filter_override_parent_fields(void)
 {
        struct event_filter *filter;
@@ -1074,6 +1116,7 @@ static void test_event_filter_timeval_values(void)
 
 void test_event_filter(void)
 {
+       test_event_filter_strings();
        test_event_filter_override_parent_fields();
        test_event_filter_override_global_fields();
        test_event_filter_clear_parent_fields();