Skip to content

Commit b075f0d

Browse files
committed
Decode percent-encoded values
Closes gh-19136 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
1 parent 6343002 commit b075f0d

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

web/src/main/java/org/springframework/security/web/FormPostRedirectStrategy.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.security.web;
1818

1919
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
2021
import java.util.Base64;
2122
import java.util.List;
2223
import java.util.Map.Entry;
@@ -30,6 +31,7 @@
3031
import org.springframework.security.crypto.keygen.StringKeyGenerator;
3132
import org.springframework.web.util.HtmlUtils;
3233
import org.springframework.web.util.UriComponentsBuilder;
34+
import org.springframework.web.util.UriUtils;
3335

3436
/**
3537
* Redirect using an auto-submitting HTML form using the POST method. All query params
@@ -83,8 +85,12 @@ public void sendRedirect(final HttpServletRequest request, final HttpServletResp
8385

8486
final StringBuilder hiddenInputsHtmlBuilder = new StringBuilder();
8587
for (final Entry<String, List<String>> entry : uriComponentsBuilder.build().getQueryParams().entrySet()) {
86-
final String name = entry.getKey();
87-
for (final String value : entry.getValue()) {
88+
final String name = UriUtils.decode(entry.getKey(), StandardCharsets.UTF_8);
89+
for (final String raw : entry.getValue()) {
90+
if (raw == null) {
91+
continue;
92+
}
93+
final String value = UriUtils.decode(raw, StandardCharsets.UTF_8);
8894
// @formatter:off
8995
final String hiddenInput = HIDDEN_INPUT_TEMPLATE
9096
.replace("{{name}}", HtmlUtils.htmlEscape(name))

web/src/test/java/org/springframework/security/web/FormPostRedirectStrategyTests.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ public void absoluteUrlWithQueryParamsRedirect() throws IOException {
101101
assertThat(this.response).satisfies(hasScriptSrcNonce());
102102
}
103103

104+
// gh-19136
105+
106+
@Test
107+
public void absoluteUrlWithPercentEncodedQueryParamsRedirect() throws IOException {
108+
this.redirectStrategy.sendRedirect(this.request, this.response, "https://example.com/cb?payload=a%2Bb%2Fc%3D");
109+
assertThat(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());
110+
assertThat(this.response.getContentType()).isEqualTo(MediaType.TEXT_HTML_VALUE);
111+
assertThat(this.response.getContentAsString()).contains("action=\"https://example.com/cb\"");
112+
assertThat(this.response.getContentAsString())
113+
.contains("<input name=\"payload\" type=\"hidden\" value=\"a+b/c=\" />");
114+
assertThat(this.response).satisfies(hasScriptSrcNonce());
115+
}
116+
104117
private ThrowingConsumer<MockHttpServletResponse> hasScriptSrcNonce() {
105118
return (response) -> {
106119
final String policyDirective = response.getHeader("Content-Security-Policy");

0 commit comments

Comments
 (0)