From f1670b8ac880280c015eb269aff6e154c4880581 Mon Sep 17 00:00:00 2001 From: yuri <1969yuri1969@gmail.com> Date: Wed, 19 Feb 2025 09:06:48 +0100 Subject: [PATCH] fix(webserver): allow special chars in label key (#7419) --- .../converters/QueryFilterFormatBinder.java | 48 +++++++++++-------- .../QueryFilterFormatBinderTest.java | 10 ++-- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/webserver/src/main/java/io/kestra/webserver/converters/QueryFilterFormatBinder.java b/webserver/src/main/java/io/kestra/webserver/converters/QueryFilterFormatBinder.java index 5ed2ce20bd2..3f79787aea9 100644 --- a/webserver/src/main/java/io/kestra/webserver/converters/QueryFilterFormatBinder.java +++ b/webserver/src/main/java/io/kestra/webserver/converters/QueryFilterFormatBinder.java @@ -20,7 +20,7 @@ @Singleton public class QueryFilterFormatBinder implements AnnotatedRequestArgumentBinder> { - private static final Pattern FILTER_PATTERN = Pattern.compile("filters\\[(.*?)\\]\\[(.*?)](?:\\[(\\w+)])?"); + private static final Pattern FILTER_PATTERN = Pattern.compile("filters\\[(.*?)]\\[(.*?)](?:\\[(.+)])?"); @VisibleForTesting static List getQueryFilters(Map> queryParams) { @@ -32,26 +32,7 @@ static List getQueryFilters(Map> queryParams) Matcher matcher = FILTER_PATTERN.matcher(key); if (matcher.matches()) { - String fieldStr = matcher.group(1); - String operationStr = matcher.group(2); - String nestedKey = matcher.group(3); // Extract nested key if present - - QueryFilter.Field field = QueryFilter.Field.fromString(fieldStr); - QueryFilter.Op operation = QueryFilter.Op.fromString(operationStr); - - Object value = nestedKey != null ? Map.of(nestedKey, values.getFirst()) : - switch (field) { - case SCOPE -> RequestUtils.toFlowScopes(values); - default -> (operation == QueryFilter.Op.IN || operation == QueryFilter.Op.NOT_IN) - ? List.of(URLDecoder.decode(values.getFirst(), StandardCharsets.UTF_8).replaceAll("[\\[\\]]", "").split(",")) - : values.size() == 1 ? values.getFirst() : values; - }; - - filters.add(QueryFilter.builder() - .field(field) - .operation(operation) - .value(value) - .build()); + parseFilters(values, matcher, filters); } }); @@ -72,4 +53,29 @@ public BindingResult> bind(ArgumentConversionContext Optional.of(filters); } + private static void parseFilters(List values, Matcher matcher, List filters) { + String fieldStr = matcher.group(1); + String operationStr = matcher.group(2); + String nestedKey = matcher.group(3); // Extract nested key if present + + QueryFilter.Field field = QueryFilter.Field.fromString(fieldStr); + QueryFilter.Op operation = QueryFilter.Op.fromString(operationStr); + + Object value = nestedKey != null ? Map.of(nestedKey, values.getFirst()) : getFlatValue(values, field, operation); + + filters.add(QueryFilter.builder() + .field(field) + .operation(operation) + .value(value) + .build()); + } + + private static Object getFlatValue(List values, QueryFilter.Field field, QueryFilter.Op operation) { + return switch (field) { + case SCOPE -> RequestUtils.toFlowScopes(values); + default -> (operation == QueryFilter.Op.IN || operation == QueryFilter.Op.NOT_IN) + ? List.of(URLDecoder.decode(values.getFirst(), StandardCharsets.UTF_8).replaceAll("[\\[\\]]", "").split(",")) + : values.size() == 1 ? values.getFirst() : values; + }; + } } \ No newline at end of file diff --git a/webserver/src/test/java/io/kestra/webserver/converters/QueryFilterFormatBinderTest.java b/webserver/src/test/java/io/kestra/webserver/converters/QueryFilterFormatBinderTest.java index 35c8b151b96..f311102d12a 100644 --- a/webserver/src/test/java/io/kestra/webserver/converters/QueryFilterFormatBinderTest.java +++ b/webserver/src/test/java/io/kestra/webserver/converters/QueryFilterFormatBinderTest.java @@ -45,7 +45,7 @@ void testGetQueryFiltersWithSimpleFilters() { void testGetQueryFiltersWithNestedFilters() { // GIVEN Map> queryParams = Map.of( - "filters[labels][$eq][key]", List.of("value") + "filters[labels][$eq][key with special chars [(_-|&/*^)]]", List.of("value with special chars [(_-|&/*^)]") ); // WHEN @@ -54,10 +54,10 @@ void testGetQueryFiltersWithNestedFilters() { // THEN assertEquals(1, filters.size()); - QueryFilter filter = filters.get(0); + QueryFilter filter = filters.getFirst(); assertEquals(QueryFilter.Field.LABELS, filter.field()); assertEquals(QueryFilter.Op.EQUALS, filter.operation()); - assertEquals(Map.of("key", "value"), filter.value()); + assertEquals(Map.of("key with special chars [(_-|&/*^)]", "value with special chars [(_-|&/*^)]"), filter.value()); } @Test @@ -70,8 +70,8 @@ void testGetQueryFiltersWithScopeParsing() { List filters = QueryFilterFormatBinder.getQueryFilters(queryParams); // THEN assertEquals(1, filters.size()); - assertEquals(QueryFilter.Field.SCOPE, filters.get(0).field()); - assertEquals(RequestUtils.toFlowScopes(List.of("USER,SYSTEM")), filters.get(0).value()); + assertEquals(QueryFilter.Field.SCOPE, filters.getFirst().field()); + assertEquals(RequestUtils.toFlowScopes(List.of("USER,SYSTEM")), filters.getFirst().value()); } @Test