From 7ad7ea3e8be566b5dbe80062f8b936a48e989465 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 2 May 2023 16:08:13 +0300 Subject: [PATCH] lib: event-filter - Support escaping wildcards in values --- src/lib/event-filter-lexer.l | 4 +-- src/lib/event-filter-parser.y | 31 +++++++++++++-------- src/lib/event-filter.c | 39 ++++++++++++++++----------- src/lib/test-event-filter-parser.c | 4 +-- src/lib/test-event-filter.c | 43 ++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/src/lib/event-filter-lexer.l b/src/lib/event-filter-lexer.l index 8ff73e1acb..8c28400a02 100644 --- a/src/lib/event-filter-lexer.l +++ b/src/lib/event-filter-lexer.l @@ -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 */ [^\\"]+ { str_append(str_buf, yytext); } -\\\\ { str_append_c(str_buf, '\\'); } -\\\" { str_append_c(str_buf, '"'); } +\\\\ { str_append(str_buf, yytext); } +\\\" { str_append(str_buf, yytext); } \\. { str_append(str_buf, yytext); } [Aa][Nn][Dd] { return AND; } diff --git a/src/lib/event-filter-parser.y b/src/lib/event-filter-parser.y index 11d7746cf7..21d18be397 100644 --- a/src/lib/event-filter-parser.y +++ b/src/lib/event-filter-parser.y @@ -12,6 +12,7 @@ #include #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; } diff --git a/src/lib/event-filter.c b/src/lib/event-filter.c index 795f54fe28..59a5792d05 100644 --- a/src/lib/event-filter.c +++ b/src/lib/event-filter.c @@ -10,10 +10,6 @@ #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) || diff --git a/src/lib/test-event-filter-parser.c b/src/lib/test-event-filter-parser.c index 63137d89c9..b0bc137574 100644 --- a/src/lib/test-event-filter-parser.c +++ b/src/lib/test-event-filter-parser.c @@ -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)); } diff --git a/src/lib/test-event-filter.c b/src/lib/test-event-filter.c index 6fdfd238b8..a3cc72cff6 100644 --- a/src/lib/test-event-filter.c +++ b/src/lib/test-event-filter.c @@ -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(); -- 2.47.3