Skip to content
Merged
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 @@ -28,7 +28,9 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -297,6 +299,62 @@ public void sendRedirect(final String location) throws IOException {
}
}

/**
* Overridden to apply the same include protection as
* {@link #sendRedirect(String)}. Since Servlet API 6.1
* {@code HttpServletResponseWrapper} overrides every {@code sendRedirect}
* variant with a direct delegation to the wrapped response, so each new
* overload must be gated here explicitly - none of them dispatches
* through another override on this wrapper.
*/
@Override
public void sendRedirect(final String location, final int sc) throws IOException {
if (!this.isProtectHeadersOnInclude()) {
logHeaderModificationCallOnIncludeForMethod("sendRedirect()");
this.committedReason = CommitReason.SEND_REDIRECT;
super.sendRedirect(location, sc);
}
}

/**
* Overridden to apply the same include protection as
* {@link #sendRedirect(String)}, see {@link #sendRedirect(String, int)}.
*/
@Override
public void sendRedirect(final String location, final boolean clearBuffer) throws IOException {
if (!this.isProtectHeadersOnInclude()) {
logHeaderModificationCallOnIncludeForMethod("sendRedirect()");
this.committedReason = CommitReason.SEND_REDIRECT;
super.sendRedirect(location, clearBuffer);
}
}

/**
* Overridden to apply the same include protection as
* {@link #sendRedirect(String)}, see {@link #sendRedirect(String, int)}.
*/
@Override
public void sendRedirect(final String location, final int sc, final boolean clearBuffer) throws IOException {
if (!this.isProtectHeadersOnInclude()) {
logHeaderModificationCallOnIncludeForMethod("sendRedirect()");
this.committedReason = CommitReason.SEND_REDIRECT;
super.sendRedirect(location, sc, clearBuffer);
}
}

/**
* Overridden to apply the include header protection: response trailer
* fields are headers as well and must not be settable by included
* servlets when header protection is enabled.
*/
@Override
public void setTrailerFields(final Supplier<Map<String, String>> supplier) {
if (!this.isProtectHeadersOnInclude()) {
logHeaderModificationCallOnIncludeForMethod("setTrailerFields()");
super.setTrailerFields(supplier);
}
}

@Override
public void setDateHeader(final String name, final long value) {
if (!this.isProtectHeadersOnInclude()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,44 @@ public void testNoViolationChecksOnCommittedResponseWhenSendRedirect() throws IO
Mockito.verify(spyInclude, never()).checkContentTypeOverride(Mockito.any());
}

@Test
public void testSendRedirectOverloadsProtectedOnInclude() throws IOException {
final SlingJakartaHttpServletResponse orig = Mockito.mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
info.setProtectHeadersOnInclude(true);

final HttpServletResponse include = new SlingJakartaHttpServletResponseImpl(requestData, orig);

include.sendRedirect("/target");
include.sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY);
include.sendRedirect("/target", false);
include.sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY, false);
include.setTrailerFields(java.util.Collections::emptyMap);

Mockito.verifyNoInteractions(orig);
}

@Test
public void testSendRedirectOverloadsDelegateWhenNotProtected() throws IOException {
final SlingJakartaHttpServletResponse orig = Mockito.mock(SlingJakartaHttpServletResponse.class);
final RequestData requestData = mock(RequestData.class);
final DispatchingInfo info = new DispatchingInfo(DispatcherType.INCLUDE);
when(requestData.getDispatchingInfo()).thenReturn(info);
when(requestData.getRequestProgressTracker()).thenReturn(mock(RequestProgressTracker.class));

final HttpServletResponse include = new SlingJakartaHttpServletResponseImpl(requestData, orig);

include.sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY);
include.sendRedirect("/target", true);
include.sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY, false);

Mockito.verify(orig, times(1)).sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY);
Mockito.verify(orig, times(1)).sendRedirect("/target", true);
Mockito.verify(orig, times(1)).sendRedirect("/target", HttpServletResponse.SC_MOVED_PERMANENTLY, false);
}

@Test
public void testNoViolationChecksOnCommittedResponseWhenSendError() throws IOException {
final SlingJakartaHttpServletResponse orig = Mockito.mock(SlingJakartaHttpServletResponse.class);
Expand Down