diff --git a/java/src/org/openqa/selenium/remote/http/UrlTemplate.java b/java/src/org/openqa/selenium/remote/http/UrlTemplate.java index 81f9b45efdb42..6358947867666 100644 --- a/java/src/org/openqa/selenium/remote/http/UrlTemplate.java +++ b/java/src/org/openqa/selenium/remote/http/UrlTemplate.java @@ -141,15 +141,21 @@ public UrlTemplate(String template) { } /** - * @return A {@link Match} with all parameters filled if successful, null otherwise. Remove - * subPath from matchAgainst before matching. + * Matches a URL against this template after stripping a leading prefix. + * + *

The {@code prefix} is treated literally (not as a regex) and stripped from the start of + * {@code matchAgainst} before matching, unless it is empty or {@code "/"}. + * + * @param matchAgainst the URL (or path) to match against this template + * @param prefix a literal prefix to strip from {@code matchAgainst} before matching + * @return a {@link Match} with all parameters filled if successful, {@code null} otherwise */ public UrlTemplate.@Nullable Match match(@Nullable String matchAgainst, @Nullable String prefix) { if (matchAgainst == null || prefix == null) { return null; } if (!prefix.isEmpty() && !prefix.equals("/")) { - matchAgainst = matchAgainst.replaceFirst(prefix, ""); + matchAgainst = matchAgainst.replaceFirst(Pattern.quote(prefix), ""); } return match(matchAgainst); } diff --git a/java/test/org/openqa/selenium/remote/http/UrlTemplateTest.java b/java/test/org/openqa/selenium/remote/http/UrlTemplateTest.java index cfc91bbf06898..55310eb820bd6 100644 --- a/java/test/org/openqa/selenium/remote/http/UrlTemplateTest.java +++ b/java/test/org/openqa/selenium/remote/http/UrlTemplateTest.java @@ -96,4 +96,15 @@ void noPartialMatches() { assertThat(new UrlTemplate("/session").match("/session-no")).isNull(); assertThat(new UrlTemplate("/session").match("/no-session-no")).isNull(); } + + @Test + void shouldNotThrowWhenPrefixContainsRegexMetacharacters() { + // The prefix contains '(', a regex metacharacter; stripping it must treat it literally + // rather than as a pattern (which would throw PatternSyntaxException). + UrlTemplate.Match match = + new UrlTemplate("/session/{id}").match("/wd(hub/session/1234", "/wd(hub"); + + assertThat(match).isNotNull(); + assertThat(match.getParameters()).containsExactlyInAnyOrderEntriesOf(Map.of("id", "1234")); + } }