From 6dde1f3817ba3b45225fce3890f705e83fe561d3 Mon Sep 17 00:00:00 2001 From: Pedro Santos Date: Wed, 27 Nov 2024 16:06:34 -0300 Subject: [PATCH] WICKET-7107 configure ContentSecurityPolicySettings to protect buffered pages --- .../CSPSettingRequestCycleListenerTest.java | 75 +++++++++++++------ .../csp/ContentSecurityPolicySettings.java | 9 ++- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/wicket-core-tests/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java b/wicket-core-tests/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java index 1fdd8cb92c4..28c5efd7386 100644 --- a/wicket-core-tests/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java +++ b/wicket-core-tests/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java @@ -16,19 +16,21 @@ */ package org.apache.wicket.csp; -import static org.apache.wicket.csp.CSPDirective.CHILD_SRC; -import static org.apache.wicket.csp.CSPDirective.DEFAULT_SRC; -import static org.apache.wicket.csp.CSPDirective.FRAME_SRC; -import static org.apache.wicket.csp.CSPDirective.IMG_SRC; -import static org.apache.wicket.csp.CSPDirective.REPORT_URI; -import static org.apache.wicket.csp.CSPDirective.SANDBOX; -import static org.apache.wicket.csp.CSPDirectiveSandboxValue.ALLOW_FORMS; -import static org.apache.wicket.csp.CSPDirectiveSandboxValue.EMPTY; -import static org.apache.wicket.csp.CSPDirectiveSrcValue.NONE; -import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF; -import static org.apache.wicket.csp.CSPDirectiveSrcValue.WILDCARD; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.apache.wicket.MarkupContainer; +import org.apache.wicket.RestartResponseException; +import org.apache.wicket.markup.IMarkupResourceStreamProvider; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.mock.MockApplication; +import org.apache.wicket.protocol.http.WebApplication; +import org.apache.wicket.request.cycle.RequestCycle; +import org.apache.wicket.util.resource.IResourceStream; +import org.apache.wicket.util.resource.StringResourceStream; +import org.apache.wicket.util.tester.DummyHomePage; +import org.apache.wicket.util.tester.WicketTestCase; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.net.URI; import java.net.URISyntaxException; @@ -39,15 +41,14 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.wicket.mock.MockApplication; -import org.apache.wicket.protocol.http.WebApplication; -import org.apache.wicket.request.cycle.RequestCycle; -import org.apache.wicket.util.tester.DummyHomePage; -import org.apache.wicket.util.tester.WicketTestCase; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import static org.apache.wicket.csp.CSPDirective.*; +import static org.apache.wicket.csp.CSPDirectiveSandboxValue.ALLOW_FORMS; +import static org.apache.wicket.csp.CSPDirectiveSandboxValue.EMPTY; +import static org.apache.wicket.csp.CSPDirectiveSrcValue.*; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; class CSPSettingRequestCycleListenerTest extends WicketTestCase { @@ -381,6 +382,36 @@ void testConstantValues_whenInvalidConstant_acceptedByConfigurer(String source) assertThrows(IllegalArgumentException.class, () -> settings.add(IMG_SRC, source + ":")); } + @Test + void addCspDirectiveInBufferedPageResponses() + { + tester.setFollowRedirects(true); + tester.getApplication().getCspSettings().blocking().add(STYLE_SRC, SELF); + + tester.startPage(RedirectPage.class); + + assertThat(tester.getLastResponse().getHeader("Content-Security-Policy"), + containsString(STYLE_SRC.getValue())); + } + + public static class Page extends WebPage implements IMarkupResourceStreamProvider + { + @Override + public IResourceStream getMarkupResourceStream(MarkupContainer container, + Class containerClass) + { + return new StringResourceStream(""); + } + } + + public static class RedirectPage extends Page + { + public RedirectPage() + { + throw new RestartResponseException(new Page()); + } + } + private String renderDirective(List values, ContentSecurityPolicySettings settings, RequestCycle cycle) diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicySettings.java b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicySettings.java index 65b510b7f4f..4c3ba85b45a 100644 --- a/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicySettings.java +++ b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicySettings.java @@ -25,6 +25,7 @@ import org.apache.wicket.Application; import org.apache.wicket.MetaDataKey; import org.apache.wicket.Page; +import org.apache.wicket.core.request.handler.BufferedResponseRequestHandler; import org.apache.wicket.core.request.handler.IPageRequestHandler; import org.apache.wicket.core.request.handler.RenderPageRequestHandler; import org.apache.wicket.protocol.http.WebApplication; @@ -69,14 +70,18 @@ public class ContentSecurityPolicySettings private final Map configs = new EnumMap<>( CSPHeaderMode.class); - private Predicate protectedFilter = RenderPageRequestHandler.class::isInstance; + private Predicate protectedFilter; private Supplier nonceCreator; public ContentSecurityPolicySettings(Application application) { Args.notNull(application, "application"); - + + Predicate isPage = RenderPageRequestHandler.class::isInstance; + Predicate isBufferedPage = BufferedResponseRequestHandler.class::isInstance; + protectedFilter = isPage.or(isBufferedPage); + nonceCreator = () -> application.getSecuritySettings().getRandomSupplier().getRandomBase64(NONCE_LENGTH); }