From 67924eb5fbcaa7de7e8c7b83773c12d04ed89288 Mon Sep 17 00:00:00 2001 From: Viet Nguyen Duc Date: Fri, 20 Jun 2025 15:16:29 +0700 Subject: [PATCH] =?UTF-8?q?Revert=20"[grid]=20Add=20config=20`blocked-rout?= =?UTF-8?q?es`=20and=20specific=20`blocked-delete-sessi=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 406427b3150321f324eb4ecfbb4c4bc3e2a01e11. --- .../openqa/selenium/grid/commands/Hub.java | 10 - .../selenium/grid/commands/Standalone.java | 10 - .../grid/router/httpd/BlockedRoute.java | 221 ---------- .../router/httpd/BlockedRoutesFilter.java | 86 ---- .../grid/router/httpd/RouterFlags.java | 23 - .../grid/router/httpd/RouterOptions.java | 50 --- .../grid/router/httpd/RouterServer.java | 8 - .../selenium/grid/router/httpd/BUILD.bazel | 18 - .../grid/router/httpd/BlockedRouteTest.java | 166 ------- .../router/httpd/BlockedRoutesFilterTest.java | 213 --------- .../grid/router/httpd/RouterOptionsTest.java | 417 ------------------ 11 files changed, 1222 deletions(-) delete mode 100644 java/src/org/openqa/selenium/grid/router/httpd/BlockedRoute.java delete mode 100644 java/src/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilter.java delete mode 100644 java/test/org/openqa/selenium/grid/router/httpd/BUILD.bazel delete mode 100644 java/test/org/openqa/selenium/grid/router/httpd/BlockedRouteTest.java delete mode 100644 java/test/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilterTest.java delete mode 100644 java/test/org/openqa/selenium/grid/router/httpd/RouterOptionsTest.java diff --git a/java/src/org/openqa/selenium/grid/commands/Hub.java b/java/src/org/openqa/selenium/grid/commands/Hub.java index 864978c55b07a..86733f0524fa9 100644 --- a/java/src/org/openqa/selenium/grid/commands/Hub.java +++ b/java/src/org/openqa/selenium/grid/commands/Hub.java @@ -31,7 +31,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,8 +48,6 @@ import org.openqa.selenium.grid.log.LoggingOptions; import org.openqa.selenium.grid.router.ProxyWebsocketsIntoGrid; import org.openqa.selenium.grid.router.Router; -import org.openqa.selenium.grid.router.httpd.BlockedRoute; -import org.openqa.selenium.grid.router.httpd.BlockedRoutesFilter; import org.openqa.selenium.grid.router.httpd.RouterOptions; import org.openqa.selenium.grid.security.BasicAuthenticationFilter; import org.openqa.selenium.grid.security.Secret; @@ -210,13 +207,6 @@ protected Handlers createHandlers(Config config) { httpHandler = httpHandler.with(new BasicAuthenticationFilter(uap.username(), uap.password())); } - // Apply blocked routes filter - List blockedRoutes = routerOptions.getBlockedRoutes(); - if (!blockedRoutes.isEmpty()) { - LOG.info("Blocking " + blockedRoutes.size() + " route(s): " + blockedRoutes); - httpHandler = BlockedRoutesFilter.with(httpHandler, blockedRoutes); - } - // Allow the liveness endpoint to be reached, since k8s doesn't make it easy to authenticate // these checks httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck)); diff --git a/java/src/org/openqa/selenium/grid/commands/Standalone.java b/java/src/org/openqa/selenium/grid/commands/Standalone.java index 994043b765405..83875cbb59f5f 100644 --- a/java/src/org/openqa/selenium/grid/commands/Standalone.java +++ b/java/src/org/openqa/selenium/grid/commands/Standalone.java @@ -32,7 +32,6 @@ import java.net.URI; import java.net.URL; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,8 +53,6 @@ import org.openqa.selenium.grid.node.ProxyNodeWebsockets; import org.openqa.selenium.grid.node.config.NodeOptions; import org.openqa.selenium.grid.router.Router; -import org.openqa.selenium.grid.router.httpd.BlockedRoute; -import org.openqa.selenium.grid.router.httpd.BlockedRoutesFilter; import org.openqa.selenium.grid.router.httpd.RouterOptions; import org.openqa.selenium.grid.security.BasicAuthenticationFilter; import org.openqa.selenium.grid.security.Secret; @@ -216,13 +213,6 @@ protected Handlers createHandlers(Config config) { httpHandler = httpHandler.with(new BasicAuthenticationFilter(uap.username(), uap.password())); } - // Apply blocked routes filter - List blockedRoutes = routerOptions.getBlockedRoutes(); - if (!blockedRoutes.isEmpty()) { - LOG.info("Blocking " + blockedRoutes.size() + " route(s): " + blockedRoutes); - httpHandler = BlockedRoutesFilter.with(httpHandler, blockedRoutes); - } - // Allow the liveness endpoint to be reached, since k8s doesn't make it easy to authenticate // these checks httpHandler = combine(httpHandler, Route.get("/readyz").to(() -> readinessCheck)); diff --git a/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoute.java b/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoute.java deleted file mode 100644 index 2d74fea08deb8..0000000000000 --- a/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoute.java +++ /dev/null @@ -1,221 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.router.httpd; - -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -/** Represents a blocked route with HTTP method and path. */ -public class BlockedRoute { - private final String method; - private final String path; - - public BlockedRoute(String method, String path) { - this.method = method.toUpperCase(); - this.path = path; - } - - public String getMethod() { - return method; - } - - public String getPath() { - return path; - } - - /** - * Creates a BlockedRoute from a string in the format "METHOD:path". - * - * @param routeStr String representation of blocked route - * @return BlockedRoute instance - * @throws IllegalArgumentException if the format is invalid - */ - public static BlockedRoute fromString(String routeStr) { - if (routeStr == null || routeStr.trim().isEmpty()) { - throw new IllegalArgumentException("Route string cannot be null or empty"); - } - - String[] parts = routeStr.split(":", 2); - if (parts.length != 2) { - throw new IllegalArgumentException( - "Invalid route format. Expected 'METHOD:path', got: " + routeStr); - } - - String method = parts[0].trim().toUpperCase(); - String path = parts[1].trim(); - - if (method.isEmpty() || path.isEmpty()) { - throw new IllegalArgumentException("Method and path cannot be empty. Got: " + routeStr); - } - - return new BlockedRoute(method, path); - } - - /** - * Checks if the given HTTP method and request path match this blocked route. - * - * @param requestMethod HTTP method of the request - * @param requestPath Path of the request - * @return true if the route should be blocked - */ - public boolean matches(String requestMethod, String requestPath) { - if (!method.equals(requestMethod.toUpperCase())) { - return false; - } - - // Use safe string-based path matching instead of regex to prevent ReDoS attacks - return matchesPathPattern(path, requestPath); - } - - /** - * Safely matches a path pattern against a request path without using regex. Handles path - * parameters like {session-id} by treating them as wildcards. Both paths are normalized to - * prevent path traversal attacks. - * - * @param pattern The path pattern to match against - * @param requestPath The actual request path - * @return true if the paths match - */ - private boolean matchesPathPattern(String pattern, String requestPath) { - // Normalize both paths to prevent path traversal attacks - String normalizedPattern = normalizePath(pattern); - String normalizedRequestPath = normalizePath(requestPath); - - // Split both paths into segments - String[] patternSegments = normalizedPattern.split("/", -1); // keep trailing empty segments - String[] requestSegments = normalizedRequestPath.split("/", -1); - - // Paths must have the same number of segments - if (patternSegments.length != requestSegments.length) { - return false; - } - - // Compare each segment - for (int i = 0; i < patternSegments.length; i++) { - String patternSegment = patternSegments[i]; - String requestSegment = requestSegments[i]; - - // If both are empty (leading/trailing slash), continue - if (patternSegment.isEmpty() && requestSegment.isEmpty()) { - continue; - } - // If pattern segment is a path parameter (enclosed in {}), it matches any non-empty segment - if (isPathParameter(patternSegment)) { - if (requestSegment.isEmpty()) { - return false; - } - } else { - // For literal segments, they must match exactly - if (!patternSegment.equals(requestSegment)) { - return false; - } - } - } - - return true; - } - - /** - * Normalizes a path to prevent path traversal attacks. This method: 1. URL decodes - * percent-encoded characters 2. Normalizes multiple consecutive slashes to single slashes 3. - * Resolves path traversal sequences (../) 4. Ensures the path doesn't escape the root directory - * - * @param path The path to normalize - * @return The normalized path - * @throws IllegalArgumentException if the path contains invalid traversal sequences - */ - private String normalizePath(String path) { - if (path == null || path.isEmpty()) { - return "/"; - } - - try { - // URL decode the path to handle percent-encoded characters like %2F - String decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8); - - // Normalize multiple consecutive slashes to single slashes - String normalizedPath = decodedPath.replaceAll("/+", "/"); - - // Split into segments and resolve path traversal - String[] segments = normalizedPath.split("/"); - List resolvedSegments = new ArrayList<>(); - - for (String segment : segments) { - if (segment.isEmpty() || ".".equals(segment)) { - // Skip empty segments and current directory references - continue; - } else if ("..".equals(segment)) { - // Go up one directory level - if (!resolvedSegments.isEmpty()) { - resolvedSegments.remove(resolvedSegments.size() - 1); - } else { - // Attempting to go above root - this is a security violation - throw new IllegalArgumentException("Path traversal attack detected: " + path); - } - } else { - // Add normal segment - resolvedSegments.add(segment); - } - } - - // Reconstruct the path - StringBuilder result = new StringBuilder(); - for (String segment : resolvedSegments) { - result.append("/").append(segment); - } - - // Ensure the result starts with / and handle empty path case - String finalPath = result.toString(); - return finalPath.isEmpty() ? "/" : finalPath; - - } catch (Exception e) { - // If URL decoding fails or any other error occurs, throw security exception - throw new IllegalArgumentException("Invalid path format: " + path, e); - } - } - - /** - * Checks if a path segment is a path parameter (enclosed in curly braces). - * - * @param segment The path segment to check - * @return true if it's a path parameter - */ - private boolean isPathParameter(String segment) { - return segment.startsWith("{") && segment.endsWith("}") && segment.length() > 2; - } - - @Override - public String toString() { - return method + ":" + path; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - BlockedRoute that = (BlockedRoute) obj; - return method.equals(that.method) && path.equals(that.path); - } - - @Override - public int hashCode() { - return method.hashCode() * 31 + path.hashCode(); - } -} diff --git a/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilter.java b/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilter.java deleted file mode 100644 index 4a302e58d65d0..0000000000000 --- a/java/src/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilter.java +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.router.httpd; - -import java.net.URI; -import java.util.List; -import java.util.logging.Logger; -import org.openqa.selenium.remote.http.Contents; -import org.openqa.selenium.remote.http.HttpHandler; -import org.openqa.selenium.remote.http.HttpRequest; -import org.openqa.selenium.remote.http.HttpResponse; -import org.openqa.selenium.remote.http.Routable; - -/** Filter that blocks requests matching specified routes. */ -public class BlockedRoutesFilter implements HttpHandler { - - private static final Logger LOG = Logger.getLogger(BlockedRoutesFilter.class.getName()); - private final List blockedRoutes; - private final HttpHandler delegate; - - public BlockedRoutesFilter(List blockedRoutes, HttpHandler delegate) { - this.blockedRoutes = blockedRoutes; - this.delegate = delegate; - } - - @Override - public HttpResponse execute(HttpRequest request) { - String method = request.getMethod().toString(); - String path = URI.create(request.getUri()).getPath(); - - // Check if the request matches any blocked route - for (BlockedRoute blockedRoute : blockedRoutes) { - if (blockedRoute.matches(method, path)) { - LOG.warning( - "Blocked request: " - + method - + " " - + path - + " (matches blocked route: " - + blockedRoute - + ")"); - return new HttpResponse() - .setStatus(403) // Forbidden - .setContent( - Contents.utf8String("Route blocked by configuration: " + method + " " + path)); - } - } - - // If not blocked, delegate to the next handler - return delegate.execute(request); - } - - /** Creates a Routable that applies the blocked routes filter. */ - public static Routable with(Routable routable, List blockedRoutes) { - if (blockedRoutes == null || blockedRoutes.isEmpty()) { - return routable; - } - - return new Routable() { - @Override - public HttpResponse execute(HttpRequest req) { - return new BlockedRoutesFilter(blockedRoutes, routable).execute(req); - } - - @Override - public boolean matches(HttpRequest req) { - return routable.matches(req); - } - }; - } -} diff --git a/java/src/org/openqa/selenium/grid/router/httpd/RouterFlags.java b/java/src/org/openqa/selenium/grid/router/httpd/RouterFlags.java index febaeb0cb74cf..3960c59d00d31 100644 --- a/java/src/org/openqa/selenium/grid/router/httpd/RouterFlags.java +++ b/java/src/org/openqa/selenium/grid/router/httpd/RouterFlags.java @@ -24,7 +24,6 @@ import com.beust.jcommander.Parameter; import com.google.auto.service.AutoService; import java.util.Collections; -import java.util.List; import java.util.Set; import org.openqa.selenium.grid.config.ConfigValue; import org.openqa.selenium.grid.config.HasRoles; @@ -75,28 +74,6 @@ public class RouterFlags implements HasRoles { @ConfigValue(section = ROUTER_SECTION, name = "disable-ui", example = "true") public boolean disableUi = false; - @Parameter( - names = {"--blocked-routes"}, - description = - "Route to block in format 'METHOD:path'. Can be specified multiple times." - + " Example: --blocked-routes DELETE:/session/{session-id} --blocked-routes" - + " DELETE:/se/grid/distributor/node/{node-id}") - @ConfigValue( - section = ROUTER_SECTION, - name = "blocked-routes", - example = - "[\"DELETE:/session/{session-id}\", \"DELETE:/se/grid/distributor/node/{node-id}\"]") - public List blockedRoutes; - - @Parameter( - names = {"--blocked-delete-session"}, - arity = 1, - description = - "A flag to prevent deleting a session proactively by blocking DELETE requests to" - + " /session/{session-id} route") - @ConfigValue(section = ROUTER_SECTION, name = "blocked-delete-session", example = "true") - public boolean blockedDeleteSession = false; - @Override public Set getRoles() { return Collections.singleton(ROUTER_ROLE); diff --git a/java/src/org/openqa/selenium/grid/router/httpd/RouterOptions.java b/java/src/org/openqa/selenium/grid/router/httpd/RouterOptions.java index b2be3ead493c5..47d2e902a107a 100644 --- a/java/src/org/openqa/selenium/grid/router/httpd/RouterOptions.java +++ b/java/src/org/openqa/selenium/grid/router/httpd/RouterOptions.java @@ -17,9 +17,6 @@ package org.openqa.selenium.grid.router.httpd; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.openqa.selenium.grid.config.Config; public class RouterOptions { @@ -54,51 +51,4 @@ public String subPath() { public boolean disableUi() { return config.get(ROUTER_SECTION, "disable-ui").map(Boolean::parseBoolean).orElse(false); } - - /** - * Returns a list of blocked routes from the configuration. Each blocked route should be specified - * in the format "METHOD:path" (e.g., "DELETE:/session/{session-id}"). Multiple routes can be - * specified as a list. If the blocked-delete-session flag is enabled, - * DELETE:/session/{session-id} will be automatically added. - * - * @return List of blocked routes - */ - public List getBlockedRoutes() { - List routes = - config - .getAll(ROUTER_SECTION, "blocked-routes") - .map( - blockedRoutesList -> { - if (blockedRoutesList.isEmpty()) { - return List.of(); - } - - return blockedRoutesList.stream() - .map(String::trim) - .filter(s -> !s.isEmpty()) - .map(BlockedRoute::fromString) - .collect(Collectors.toList()); - }) - .orElse(List.of()); - - // Add DELETE session route if the flag is enabled - boolean blockedDeleteSession = - config.getBool(ROUTER_SECTION, "blocked-delete-session").orElse(false); - - if (blockedDeleteSession) { - BlockedRoute deleteSessionRoute = new BlockedRoute("DELETE", "/session/{session-id}"); - // Only add if not already present - if (routes.stream() - .noneMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath()))) { - routes = - Stream.concat(routes.stream(), Stream.of(deleteSessionRoute)) - .collect(Collectors.toList()); - } - } - - return routes; - } } diff --git a/java/src/org/openqa/selenium/grid/router/httpd/RouterServer.java b/java/src/org/openqa/selenium/grid/router/httpd/RouterServer.java index 8f0f8218a6743..7ff73e655cb88 100644 --- a/java/src/org/openqa/selenium/grid/router/httpd/RouterServer.java +++ b/java/src/org/openqa/selenium/grid/router/httpd/RouterServer.java @@ -37,7 +37,6 @@ import java.net.URL; import java.time.Duration; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -174,13 +173,6 @@ protected Handlers createHandlers(Config config) { route = route.with(new BasicAuthenticationFilter(uap.username(), uap.password())); } - // Apply blocked routes filter - List blockedRoutes = routerOptions.getBlockedRoutes(); - if (!blockedRoutes.isEmpty()) { - LOG.info("Blocking " + blockedRoutes.size() + " route(s): " + blockedRoutes); - route = BlockedRoutesFilter.with(route, blockedRoutes); - } - HttpHandler readinessCheck = req -> { boolean ready = router.isReady(); diff --git a/java/test/org/openqa/selenium/grid/router/httpd/BUILD.bazel b/java/test/org/openqa/selenium/grid/router/httpd/BUILD.bazel deleted file mode 100644 index bb3eb61fe3811..0000000000000 --- a/java/test/org/openqa/selenium/grid/router/httpd/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@rules_jvm_external//:defs.bzl", "artifact") -load("//java:defs.bzl", "JUNIT5_DEPS", "java_test_suite") - -java_test_suite( - name = "httpd-tests", - size = "small", - srcs = glob(["*Test.java"]), - deps = [ - "//java/src/org/openqa/selenium/grid/config", - "//java/src/org/openqa/selenium/grid/router/httpd", - "//java/src/org/openqa/selenium/remote/http", - "//java/test/org/openqa/selenium/testing:test-base", - artifact("com.google.guava:guava"), - artifact("org.junit.jupiter:junit-jupiter-api"), - artifact("org.assertj:assertj-core"), - artifact("org.mockito:mockito-core"), - ] + JUNIT5_DEPS, -) diff --git a/java/test/org/openqa/selenium/grid/router/httpd/BlockedRouteTest.java b/java/test/org/openqa/selenium/grid/router/httpd/BlockedRouteTest.java deleted file mode 100644 index d0fc077c7171f..0000000000000 --- a/java/test/org/openqa/selenium/grid/router/httpd/BlockedRouteTest.java +++ /dev/null @@ -1,166 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.router.httpd; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Test; - -class BlockedRouteTest { - - @Test - void matchesExactPathAndMethod() { - BlockedRoute route = new BlockedRoute("GET", "/status"); - assertTrue(route.matches("GET", "/status")); - assertFalse(route.matches("POST", "/status")); - assertFalse(route.matches("GET", "/not-status")); - } - - @Test - void matchesPathWithParameter() { - BlockedRoute route = new BlockedRoute("DELETE", "/session/{session-id}"); - assertTrue(route.matches("DELETE", "/session/123")); - assertTrue(route.matches("DELETE", "/session/abc")); - assertFalse(route.matches("DELETE", "/session/")); - assertFalse(route.matches("DELETE", "/session")); - assertFalse(route.matches("DELETE", "/session/123/extra")); - } - - @Test - void methodMatchingIsCaseInsensitive() { - BlockedRoute route = new BlockedRoute("delete", "/session/{session-id}"); - assertTrue(route.matches("DELETE", "/session/123")); - assertTrue(route.matches("delete", "/session/123")); - } - - @Test - void doesNotMatchIfSegmentCountDiffers() { - BlockedRoute route = new BlockedRoute("GET", "/foo/{bar}"); - assertFalse(route.matches("GET", "/foo")); - assertFalse(route.matches("GET", "/foo/bar/baz")); - } - - @Test - void handlesLeadingAndTrailingSlashes() { - BlockedRoute route = new BlockedRoute("GET", "/foo/{bar}/"); - assertTrue(route.matches("GET", "/foo/123/")); - assertTrue(route.matches("GET", "/foo/123")); - assertFalse(route.matches("GET", "/foo//")); - } - - @Test - void doesNotReDoSOnLongInput() { - BlockedRoute route = new BlockedRoute("GET", "/foo/{bar}"); - String longPath = "/foo/" + "a".repeat(10000); - assertTrue(route.matches("GET", longPath)); - } - - @Test - void preventsPathTraversalWithDoubleSlashes() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // These should all be normalized to /admin/users and match - assertTrue(route.matches("GET", "/admin//users")); - assertTrue(route.matches("GET", "//admin//users")); - assertTrue(route.matches("GET", "/admin/users//")); - assertTrue(route.matches("GET", "///admin///users///")); - } - - @Test - void preventsPathTraversalWithEncodedCharacters() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // %2F is URL-encoded forward slash - assertTrue(route.matches("GET", "/admin%2Fusers")); - assertTrue(route.matches("GET", "%2Fadmin%2Fusers")); - assertTrue(route.matches("GET", "/admin%2F%2Fusers")); - } - - @Test - void preventsPathTraversalWithDotDotSequences() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // These should be normalized and match - assertTrue(route.matches("GET", "/admin/../admin/users")); - assertTrue(route.matches("GET", "/admin/./users")); - assertTrue(route.matches("GET", "/admin/../admin/./users")); - } - - @Test - void preventsPathTraversalAboveRoot() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // These should throw IllegalArgumentException due to path traversal above root - assertThrows( - IllegalArgumentException.class, () -> route.matches("GET", "/admin/../../etc/passwd")); - assertThrows(IllegalArgumentException.class, () -> route.matches("GET", "../../../etc/passwd")); - assertThrows(IllegalArgumentException.class, () -> route.matches("GET", "/admin/../../")); - } - - @Test - void handlesComplexPathTraversalAttempts() { - BlockedRoute route = new BlockedRoute("GET", "/api/data"); - // Complex combinations of traversal techniques - assertTrue(route.matches("GET", "/api//data")); - assertTrue(route.matches("GET", "/api/./data")); - assertTrue(route.matches("GET", "/api/../api/data")); - assertTrue(route.matches("GET", "/api%2Fdata")); - assertTrue(route.matches("GET", "/api%2F%2Fdata")); - } - - @Test - void normalizesMultipleTraversalSequences() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // Multiple ../ sequences should be resolved correctly - assertTrue(route.matches("GET", "/admin/../admin/../admin/users")); - assertTrue(route.matches("GET", "/admin/././users")); - assertTrue(route.matches("GET", "/admin/../admin/./users")); - } - - @Test - void handlesEmptyAndNullPaths() { - BlockedRoute route = new BlockedRoute("GET", "/"); - assertTrue(route.matches("GET", "")); - assertTrue(route.matches("GET", null)); - } - - @Test - void preventsPathTraversalInParameterizedRoutes() { - BlockedRoute route = new BlockedRoute("DELETE", "/session/{session-id}"); - // Path traversal attempts in parameterized routes should be normalized - assertTrue(route.matches("DELETE", "/session//123")); - assertTrue(route.matches("DELETE", "/session/./123")); - assertTrue(route.matches("DELETE", "/session/../session/123")); - assertTrue(route.matches("DELETE", "/session%2F123")); - } - - @Test - void handlesMixedTraversalTechniques() { - BlockedRoute route = new BlockedRoute("GET", "/admin/users"); - // Mixed techniques should all be normalized correctly - assertTrue(route.matches("GET", "/admin//./../admin//users")); - assertTrue(route.matches("GET", "/admin%2F%2F./../admin/users")); - assertTrue(route.matches("GET", "/admin//%2F./../admin/users")); - } - - @Test - void fromStringHandlesPathTraversalInInput() { - // Test that fromString method can handle path traversal in the input string - BlockedRoute route = BlockedRoute.fromString("GET:/admin//users"); - assertTrue(route.matches("GET", "/admin/users")); - - route = BlockedRoute.fromString("GET:/admin%2Fusers"); - assertTrue(route.matches("GET", "/admin/users")); - } -} diff --git a/java/test/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilterTest.java b/java/test/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilterTest.java deleted file mode 100644 index 091c9b40c86aa..0000000000000 --- a/java/test/org/openqa/selenium/grid/router/httpd/BlockedRoutesFilterTest.java +++ /dev/null @@ -1,213 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.router.httpd; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.openqa.selenium.remote.http.HttpMethod.DELETE; -import static org.openqa.selenium.remote.http.HttpMethod.GET; -import static org.openqa.selenium.remote.http.HttpMethod.POST; - -import java.util.List; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.remote.http.HttpHandler; -import org.openqa.selenium.remote.http.HttpRequest; -import org.openqa.selenium.remote.http.HttpResponse; -import org.openqa.selenium.remote.http.Routable; - -class BlockedRoutesFilterTest { - - @Test - void shouldBlockMatchingRequest() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(DELETE, "/session/123"); - - HttpResponse response = filter.execute(request); - - assertEquals(403, response.getStatus()); - assertNotNull(response.getContent()); - verify(mockHandler, never()).execute(request); - } - - @Test - void shouldAllowNonMatchingRequest() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - HttpResponse expectedResponse = new HttpResponse().setStatus(200); - when(mockHandler.execute(any(HttpRequest.class))).thenReturn(expectedResponse); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(GET, "/status"); - - HttpResponse response = filter.execute(request); - - assertEquals(200, response.getStatus()); - } - - @Test - void shouldAllowRequestWithDifferentMethod() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - HttpResponse expectedResponse = new HttpResponse().setStatus(200); - when(mockHandler.execute(any(HttpRequest.class))).thenReturn(expectedResponse); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(POST, "/session/123"); - - HttpResponse response = filter.execute(request); - - assertEquals(200, response.getStatus()); - } - - @Test - void shouldAllowRequestWithDifferentPath() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - HttpResponse expectedResponse = new HttpResponse().setStatus(200); - when(mockHandler.execute(any(HttpRequest.class))).thenReturn(expectedResponse); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(DELETE, "/session"); - - HttpResponse response = filter.execute(request); - - assertEquals(200, response.getStatus()); - } - - @Test - void shouldBlockMultipleMatchingRoutes() { - List blockedRoutes = - List.of( - BlockedRoute.fromString("DELETE:/session/{session-id}"), - BlockedRoute.fromString("POST:/session")); - - HttpHandler mockHandler = mock(HttpHandler.class); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - // Test first blocked route - HttpRequest deleteRequest = new HttpRequest(DELETE, "/session/123"); - - HttpResponse deleteResponse = filter.execute(deleteRequest); - assertEquals(403, deleteResponse.getStatus()); - - // Test second blocked route - HttpRequest postRequest = new HttpRequest(POST, "/session"); - - HttpResponse postResponse = filter.execute(postRequest); - assertEquals(403, postResponse.getStatus()); - } - - @Test - void shouldReturnOriginalRoutableWhenNoBlockedRoutes() { - List blockedRoutes = List.of(); - - // Create a real Routable implementation - Routable originalRoutable = - new Routable() { - @Override - public HttpResponse execute(HttpRequest req) { - return new HttpResponse().setStatus(200); - } - - @Override - public boolean matches(HttpRequest req) { - return true; - } - }; - - Routable result = BlockedRoutesFilter.with(originalRoutable, blockedRoutes); - - assertEquals(originalRoutable, result); - } - - @Test - void shouldReturnOriginalRoutableWhenBlockedRoutesIsNull() { - // Create a real Routable implementation - Routable originalRoutable = - new Routable() { - @Override - public HttpResponse execute(HttpRequest req) { - return new HttpResponse().setStatus(200); - } - - @Override - public boolean matches(HttpRequest req) { - return true; - } - }; - - Routable result = BlockedRoutesFilter.with(originalRoutable, null); - - assertEquals(originalRoutable, result); - } - - @Test - void shouldHandleCaseInsensitiveMethodMatching() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(DELETE, "/session/123"); - - HttpResponse response = filter.execute(request); - - assertEquals(403, response.getStatus()); - } - - @Test - void shouldNotBlockRequestWithFollowPath() { - List blockedRoutes = - List.of(BlockedRoute.fromString("DELETE:/session/{session-id}")); - - HttpHandler mockHandler = mock(HttpHandler.class); - HttpResponse expectedResponse = new HttpResponse().setStatus(200); - when(mockHandler.execute(any(HttpRequest.class))).thenReturn(expectedResponse); - - BlockedRoutesFilter filter = new BlockedRoutesFilter(blockedRoutes, mockHandler); - - HttpRequest request = new HttpRequest(DELETE, "/session/123/se/bidi"); - - HttpResponse response = filter.execute(request); - - assertEquals(200, response.getStatus()); - } -} diff --git a/java/test/org/openqa/selenium/grid/router/httpd/RouterOptionsTest.java b/java/test/org/openqa/selenium/grid/router/httpd/RouterOptionsTest.java deleted file mode 100644 index 83f5899a20a63..0000000000000 --- a/java/test/org/openqa/selenium/grid/router/httpd/RouterOptionsTest.java +++ /dev/null @@ -1,417 +0,0 @@ -// Licensed to the Software Freedom Conservancy (SFC) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The SFC licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.openqa.selenium.grid.router.httpd; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import java.io.StringReader; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.openqa.selenium.grid.config.Config; -import org.openqa.selenium.grid.config.MapConfig; -import org.openqa.selenium.grid.config.TomlConfig; - -class RouterOptionsTest { - - @Test - void shouldReturnEmptyListWhenNoBlockedRoutesConfigured() { - Config config = new MapConfig(ImmutableMap.of()); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void shouldParseSingleBlockedRoute() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", ImmutableList.of("DELETE:/session/{session-id}")))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(1); - - BlockedRoute route = blockedRoutes.get(0); - assertEquals("DELETE", route.getMethod()); - assertEquals("/session/{session-id}", route.getPath()); - } - - @Test - void shouldParseMultipleBlockedRoutes() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", - ImmutableList.of( - "DELETE:/session/{session-id}", "POST:/session", "GET:/status")))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(3); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "GET".equals(route.getMethod()) && "/status".equals(route.getPath())); - } - - @Test - void shouldHandleWhitespaceInBlockedRoutes() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", - ImmutableList.of(" DELETE : /session/{session-id} ", " POST : /session ")))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(2); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - } - - @Test - void shouldIgnoreEmptyEntriesInBlockedRoutes() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", - ImmutableList.of("DELETE:/session/{session-id}", "", "POST:/session")))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(2); - } - - @Test - void shouldHandleNullBlockedRoutes() { - // Test that when blocked-routes is not present in config, it returns empty list - Config config = new MapConfig(ImmutableMap.of("router", ImmutableMap.of())); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void shouldHandleEmptyBlockedRoutes() { - Config config = - new MapConfig( - ImmutableMap.of("router", ImmutableMap.of("blocked-routes", ImmutableList.of()))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfig() { - String[] rawConfig = { - "[router]", - "blocked-routes = [", - " \"DELETE:/session/{session-id}\",", - " \"POST:/session\",", - " \"GET:/status\"", - "]" - }; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(3); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "GET".equals(route.getMethod()) && "/status".equals(route.getPath())); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfigWithWhitespace() { - String[] rawConfig = { - "[router]", - "blocked-routes = [", - " \" DELETE : /session/{session-id} \",", - " \" POST : /session \"", - "]" - }; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(2); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfigWithEmptyEntries() { - String[] rawConfig = { - "[router]", - "blocked-routes = [", - " \"DELETE:/session/{session-id}\",", - " \"\",", - " \"POST:/session\"", - "]" - }; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(2); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfigWithDeleteSessionFlag() { - String[] rawConfig = { - "[router]", - "blocked-routes = [", - " \"POST:/session\",", - " \"GET:/status\"", - "]", - "blocked-delete-session = true" - }; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(3); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "GET".equals(route.getMethod()) && "/status".equals(route.getPath())); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfigWithSingleRoute() { - String[] rawConfig = {"[router]", "blocked-routes = [\"DELETE:/session/{session-id}\"]"}; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(1); - - BlockedRoute route = blockedRoutes.get(0); - assertEquals("DELETE", route.getMethod()); - assertEquals("/session/{session-id}", route.getPath()); - } - - @Test - void shouldParseEmptyBlockedRoutesFromTomlConfig() { - String[] rawConfig = {"[router]", "blocked-routes = []"}; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void shouldParseBlockedRoutesFromTomlConfigWithoutRouterSection() { - String[] rawConfig = {"[other]", "some-option = \"value\""}; - Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void blockedRouteShouldMatchExactPath() { - BlockedRoute route = BlockedRoute.fromString("DELETE:/session/{session-id}"); - - assertTrue(route.matches("DELETE", "/session/123")); - assertTrue(route.matches("DELETE", "/session/abc-def")); - assertFalse(route.matches("DELETE", "/session")); - assertFalse(route.matches("DELETE", "/session/123/extra")); - assertFalse(route.matches("POST", "/session/123")); - } - - @Test - void blockedRouteShouldMatchExactPathWithoutParameters() { - BlockedRoute route = BlockedRoute.fromString("POST:/session"); - - assertTrue(route.matches("POST", "/session")); - assertFalse(route.matches("POST", "/session/123")); - assertFalse(route.matches("GET", "/session")); - } - - @Test - void blockedRouteShouldBeCaseInsensitiveForMethod() { - BlockedRoute route = BlockedRoute.fromString("DELETE:/session/{session-id}"); - - assertTrue(route.matches("delete", "/session/123")); - assertTrue(route.matches("Delete", "/session/123")); - assertTrue(route.matches("DELETE", "/session/123")); - } - - @Test - void shouldThrowExceptionForInvalidRouteFormat() { - assertThrows(IllegalArgumentException.class, () -> BlockedRoute.fromString("DELETE")); - - assertThrows(IllegalArgumentException.class, () -> BlockedRoute.fromString(":path")); - - assertThrows(IllegalArgumentException.class, () -> BlockedRoute.fromString("method:")); - - assertThrows(IllegalArgumentException.class, () -> BlockedRoute.fromString("")); - - assertThrows(IllegalArgumentException.class, () -> BlockedRoute.fromString(null)); - } - - @Test - void blockedRouteToStringShouldReturnOriginalFormat() { - BlockedRoute route = new BlockedRoute("DELETE", "/session/{session-id}"); - assertEquals("DELETE:/session/{session-id}", route.toString()); - } - - @Test - void blockedRouteShouldHandleMultiplePathParameters() { - BlockedRoute route = BlockedRoute.fromString("PUT:/session/{session-id}/element/{element-id}"); - - assertTrue(route.matches("PUT", "/session/123/element/456")); - assertTrue(route.matches("PUT", "/session/abc/element/def")); - assertFalse(route.matches("PUT", "/session/123/element")); - assertFalse(route.matches("PUT", "/session/element/456")); - } - - @Test - void shouldAddDeleteSessionRouteWhenFlagIsEnabled() { - Config config = - new MapConfig(ImmutableMap.of("router", ImmutableMap.of("blocked-delete-session", "true"))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(1); - - BlockedRoute route = blockedRoutes.get(0); - assertEquals("DELETE", route.getMethod()); - assertEquals("/session/{session-id}", route.getPath()); - } - - @Test - void shouldNotAddDuplicateDeleteSessionRoute() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", - ImmutableList.of("DELETE:/session/{session-id}"), - "blocked-delete-session", - "true"))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(1); - - BlockedRoute route = blockedRoutes.get(0); - assertEquals("DELETE", route.getMethod()); - assertEquals("/session/{session-id}", route.getPath()); - } - - @Test - void shouldCombineBlockedRoutesWithDeleteSessionFlag() { - Config config = - new MapConfig( - ImmutableMap.of( - "router", - ImmutableMap.of( - "blocked-routes", - ImmutableList.of("POST:/session", "GET:/status"), - "blocked-delete-session", - "true"))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).hasSize(3); - - assertThat(blockedRoutes) - .anyMatch( - route -> - "DELETE".equals(route.getMethod()) - && "/session/{session-id}".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "POST".equals(route.getMethod()) && "/session".equals(route.getPath())); - assertThat(blockedRoutes) - .anyMatch(route -> "GET".equals(route.getMethod()) && "/status".equals(route.getPath())); - } - - @Test - void shouldNotAddDeleteSessionRouteWhenFlagIsDisabled() { - Config config = - new MapConfig( - ImmutableMap.of("router", ImmutableMap.of("blocked-delete-session", "false"))); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } - - @Test - void shouldNotAddDeleteSessionRouteWhenFlagIsNotSet() { - Config config = new MapConfig(ImmutableMap.of()); - RouterOptions options = new RouterOptions(config); - - List blockedRoutes = options.getBlockedRoutes(); - assertThat(blockedRoutes).isEmpty(); - } -}