From dc4e053d591eb819269b083fb8e435eb89ef7f96 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 10 May 2021 12:02:08 +0100 Subject: [PATCH] CorsConfiguration ignores trailing "/" in pattern Recent commit dddcc5e9adaad0a85ae40664317e501c0cdf46aa ensured a trailing "/" in the Origin header has no effect. This commit does the same for a trailing "/" in configured patterns. See gh-26892 --- .../springframework/web/cors/CorsConfiguration.java | 13 +++++++++---- .../web/cors/CorsConfigurationTests.java | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index 1e2e6f5d2a7..ddbe9d5ba66 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -138,7 +138,12 @@ public class CorsConfiguration { * {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}. */ public void setAllowedOrigins(@Nullable List allowedOrigins) { - this.allowedOrigins = (allowedOrigins != null ? new ArrayList<>(allowedOrigins) : null); + this.allowedOrigins = (allowedOrigins != null ? + allowedOrigins.stream().map(this::trimTrailingSlash).collect(Collectors.toList()) : null); + } + + private String trimTrailingSlash(String origin) { + return origin.endsWith("/") ? origin.substring(0, origin.length() - 1) : origin; } /** @@ -159,6 +164,7 @@ public class CorsConfiguration { else if (this.allowedOrigins == DEFAULT_PERMIT_ALL && CollectionUtils.isEmpty(this.allowedOriginPatterns)) { setAllowedOrigins(DEFAULT_PERMIT_ALL); } + origin = trimTrailingSlash(origin); this.allowedOrigins.add(origin); } @@ -209,6 +215,7 @@ public class CorsConfiguration { if (this.allowedOriginPatterns == null) { this.allowedOriginPatterns = new ArrayList<>(4); } + originPattern = trimTrailingSlash(originPattern); this.allowedOriginPatterns.add(new OriginPattern(originPattern)); if (this.allowedOrigins == DEFAULT_PERMIT_ALL) { this.allowedOrigins = null; @@ -551,9 +558,7 @@ public class CorsConfiguration { if (!StringUtils.hasText(requestOrigin)) { return null; } - if (requestOrigin.endsWith("/")) { - requestOrigin = requestOrigin.substring(0, requestOrigin.length() - 1); - } + requestOrigin = trimTrailingSlash(requestOrigin); if (!ObjectUtils.isEmpty(this.allowedOrigins)) { if (this.allowedOrigins.contains(ALL)) { validateAllowCredentials(); diff --git a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java index a7fc54b19a4..4cfdf1cc353 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java @@ -282,17 +282,25 @@ public class CorsConfigurationTests { @Test public void checkOriginAllowed() { + // "*" matches CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); assertThat(config.checkOrigin("https://domain.com")).isEqualTo("*"); + // "*" does not match together with allowCredentials config.setAllowCredentials(true); assertThatIllegalArgumentException().isThrownBy(() -> config.checkOrigin("https://domain.com")); + // specific origin matches Origin header with or without trailing "/" config.setAllowedOrigins(Collections.singletonList("https://domain.com")); assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com"); assertThat(config.checkOrigin("https://domain.com/")).isEqualTo("https://domain.com"); + // specific origin with trailing "/" matches Origin header with or without trailing "/" + config.setAllowedOrigins(Collections.singletonList("https://domain.com/")); + assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com"); + assertThat(config.checkOrigin("https://domain.com/")).isEqualTo("https://domain.com"); + config.setAllowCredentials(false); assertThat(config.checkOrigin("https://domain.com")).isEqualTo("https://domain.com"); }