Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.io.IOException;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -70,6 +72,11 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd

private static final String SERVER_RELATIVE_LOCATION_PREFIX = "serverRelative:";

/**
* Matches strings that start with an RFC 3986 URL scheme followed by a colon.
*/
private static final Predicate<String> URL_MATCHER = Pattern.compile("^[a-zA-Z][a-zA-Z0-9+.-]*:.*").asMatchPredicate();

/**
* The entry point into Spring Web Flow.
*/
Expand Down Expand Up @@ -507,7 +514,7 @@ private void sendExternalRedirect(String location, HttpServletRequest request, H
url = "/" + url;
}
sendRedirect(url, request, response);
} else if (location.startsWith("http://") || location.startsWith("https://")) {
} else if (URL_MATCHER.test(location)) {
sendRedirect(location, request, response);
} else {
if (isRedirectServletRelative(request)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,14 @@ public void testLaunchFlowWithDefinitionRedirect() throws Exception {
@Test
public void testLaunchFlowWithExternalHttpRedirect() throws Exception {
setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("https://www.paypal.com");
context.requestExternalRedirect("http://www.paypal.com");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
EasyMock.expectLastCall().andReturn(result);
EasyMock.replay(flowExecutor);
flowHandlerAdapter.handle(request, response, flowHandler);
EasyMock.verify(flowExecutor);
assertEquals("https://www.paypal.com", response.getRedirectedUrl());
assertEquals("http://www.paypal.com", response.getRedirectedUrl());
EasyMock.verify(flowExecutor);
}

Expand All @@ -256,6 +256,48 @@ public void testLaunchFlowWithExternalHttpsRedirect() throws Exception {
EasyMock.verify(flowExecutor);
}

@Test
public void testLaunchFlowWithExternalMailtoRedirect() throws Exception {
setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("mailto:help@mail.test");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
EasyMock.expectLastCall().andReturn(result);
EasyMock.replay(flowExecutor);
flowHandlerAdapter.handle(request, response, flowHandler);
EasyMock.verify(flowExecutor);
assertEquals("mailto:help@mail.test", response.getRedirectedUrl());
EasyMock.verify(flowExecutor);
}

@Test
public void testLaunchFlowWithExternalTelRedirect() throws Exception {
setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("tel:+1.800.555.1212");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
EasyMock.expectLastCall().andReturn(result);
EasyMock.replay(flowExecutor);
flowHandlerAdapter.handle(request, response, flowHandler);
EasyMock.verify(flowExecutor);
assertEquals("tel:+1.800.555.1212", response.getRedirectedUrl());
EasyMock.verify(flowExecutor);
}

@Test
public void testLaunchFlowWithExternalCustomSchemeRedirect() throws Exception {
setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("my-mobile-app://redirect/target/path");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
EasyMock.expectLastCall().andReturn(result);
EasyMock.replay(flowExecutor);
flowHandlerAdapter.handle(request, response, flowHandler);
EasyMock.verify(flowExecutor);
assertEquals("my-mobile-app://redirect/target/path", response.getRedirectedUrl());
EasyMock.verify(flowExecutor);
}

@Test
public void testLaunchFlowWithExternalRedirectServletRelative() throws Exception {
setupRequest("/springtravel", "/app", "/foo", "GET");
Expand Down