From 0a9d4dc8fc9d303dad7fc1ee25cc7381a9888899 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Apr 2026 17:54:21 +0000 Subject: [PATCH 1/3] Release 6.5.10 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e1ef113813..6dbfaff78c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ # limitations under the License. # springBootVersion=3.3.3 -version=6.5.10-SNAPSHOT +version=6.5.10 samplesBranch=6.5.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From 6343002b326cea8806d93ebc449340cc54617901 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 20 Apr 2026 18:55:26 +0000 Subject: [PATCH 2/3] Next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6dbfaff78c..3858affa6e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ # limitations under the License. # springBootVersion=3.3.3 -version=6.5.10 +version=6.5.11-SNAPSHOT samplesBranch=6.5.x org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true From b075f0df022c9ffe2d85e4bf7afbc35db0aae84d Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Wed, 29 Apr 2026 08:57:16 -0600 Subject: [PATCH 3/3] Decode percent-encoded values Closes gh-19136 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- .../security/web/FormPostRedirectStrategy.java | 10 ++++++++-- .../security/web/FormPostRedirectStrategyTests.java | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java b/web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java index e6a2aff459..af308e72c8 100644 --- a/web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java +++ b/web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java @@ -17,6 +17,7 @@ package org.springframework.security.web; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; import java.util.Map.Entry; @@ -30,6 +31,7 @@ import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; import org.springframework.security.crypto.keygen.StringKeyGenerator; import org.springframework.web.util.HtmlUtils; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; /** * Redirect using an auto-submitting HTML form using the POST method. All query params @@ -83,8 +85,12 @@ public final class FormPostRedirectStrategy implements RedirectStrategy { final StringBuilder hiddenInputsHtmlBuilder = new StringBuilder(); for (final Entry> entry : uriComponentsBuilder.build().getQueryParams().entrySet()) { - final String name = entry.getKey(); - for (final String value : entry.getValue()) { + final String name = UriUtils.decode(entry.getKey(), StandardCharsets.UTF_8); + for (final String raw : entry.getValue()) { + if (raw == null) { + continue; + } + final String value = UriUtils.decode(raw, StandardCharsets.UTF_8); // @formatter:off final String hiddenInput = HIDDEN_INPUT_TEMPLATE .replace("{{name}}", HtmlUtils.htmlEscape(name)) diff --git a/web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java b/web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java index f54a1e51d8..0c23d98a49 100644 --- a/web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java @@ -101,6 +101,19 @@ public class FormPostRedirectStrategyTests { assertThat(this.response).satisfies(hasScriptSrcNonce()); } + // gh-19136 + + @Test + public void absoluteUrlWithPercentEncodedQueryParamsRedirect() throws IOException { + this.redirectStrategy.sendRedirect(this.request, this.response, "https://example.com/cb?payload=a%2Bb%2Fc%3D"); + assertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE); + assertThat(this.response.getContentAsString()).contains("action=\"https://example.com/cb\""); + assertThat(this.response.getContentAsString()) + .contains(""); + assertThat(this.response).satisfies(hasScriptSrcNonce()); + } + private ThrowingConsumer hasScriptSrcNonce() { return (response) -> { final String policyDirective = response.getHeader("Content-Security-Policy");