From dddcc5e9adaad0a85ae40664317e501c0cdf46aa Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 7 May 2021 14:56:56 +0100 Subject: [PATCH] Ignore trailing slash in Origin header See gh-26892 --- .../web/cors/CorsConfiguration.java | 3 +++ .../web/cors/CorsConfigurationTests.java | 3 ++- .../web/cors/DefaultCorsProcessorTests.java | 15 ++++++++++++--- .../reactive/DefaultCorsProcessorTests.java | 18 +++++++++++++++--- 4 files changed, 32 insertions(+), 7 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 a8cbd3b2062..1e2e6f5d2a7 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 @@ -551,6 +551,9 @@ public class CorsConfiguration { if (!StringUtils.hasText(requestOrigin)) { return null; } + if (requestOrigin.endsWith("/")) { + requestOrigin = requestOrigin.substring(0, requestOrigin.length() - 1); + } 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 82c5286dce7..a7fc54b19a4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -291,6 +291,7 @@ public class CorsConfigurationTests { 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"); diff --git a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java index 5c163779723..c57aeffeada 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -170,10 +170,19 @@ public class DefaultCorsProcessorTests { this.conf.addAllowedOrigin("https://DOMAIN2.com"); this.processor.processRequest(this.conf, this.request, this.response); + assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue(); - assertThat(this.response.getHeaders(HttpHeaders.VARY)).contains(HttpHeaders.ORIGIN, - HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS); + } + + @Test // gh-26892 + public void actualRequestTrailingSlashOriginMatch() throws Exception { + this.request.setMethod(HttpMethod.GET.name()); + this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com/"); + this.conf.addAllowedOrigin("https://domain2.com"); + + this.processor.processRequest(this.conf, this.request, this.response); assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue(); } @Test diff --git a/spring-web/src/test/java/org/springframework/web/cors/reactive/DefaultCorsProcessorTests.java b/spring-web/src/test/java/org/springframework/web/cors/reactive/DefaultCorsProcessorTests.java index 4549d1409a7..36b5a4787e9 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/reactive/DefaultCorsProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/reactive/DefaultCorsProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -172,10 +172,22 @@ public class DefaultCorsProcessorTests { this.processor.process(this.conf, exchange); ServerHttpResponse response = exchange.getResponse(); + assertThat((Object) response.getStatusCode()).isNull(); assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue(); - assertThat(response.getHeaders().get(VARY)).contains(ORIGIN, - ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS); + } + + @Test // gh-26892 + public void actualRequestTrailingSlashOriginMatch() { + ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest + .method(HttpMethod.GET, "http://localhost/test.html") + .header(HttpHeaders.ORIGIN, "https://domain2.com/")); + + this.conf.addAllowedOrigin("https://domain2.com"); + this.processor.process(this.conf, exchange); + + ServerHttpResponse response = exchange.getResponse(); assertThat((Object) response.getStatusCode()).isNull(); + assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue(); } @Test