You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pa...@apache.org on 2020/01/17 20:53:15 UTC

[wicket] branch csp updated: WICKET-6727: enable CSP in WebApplication and provide default profiles

This is an automated email from the ASF dual-hosted git repository.

papegaaij pushed a commit to branch csp
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/csp by this push:
     new 3bfce9f  WICKET-6727: enable CSP in WebApplication and provide default profiles
3bfce9f is described below

commit 3bfce9fc5431bf98f203a1abff80859eab823503
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Fri Jan 17 21:52:50 2020 +0100

    WICKET-6727: enable CSP in WebApplication and provide default profiles
---
 .../wicket/csp/CSPDirectiveSandboxValue.java       |  2 +-
 .../apache/wicket/csp/CSPDirectiveSrcValue.java    |  4 +-
 .../apache/wicket/csp/CSPHeaderConfiguration.java  | 72 +++++++++++++++++-
 .../java/org/apache/wicket/csp/CSPRenderable.java  |  4 +-
 ...ner.java => ContentSecurityPolicyEnforcer.java} | 46 ++++++++++--
 .../csp/CspNonceHeaderResponseDecorator.java       | 29 ++++----
 .../org/apache/wicket/csp/FixedCSPDirective.java   |  2 +-
 .../wicket/protocol/http/WebApplication.java       | 36 +++++++++
 .../csp/CSPSettingRequestCycleListenerTest.java    | 86 +++++++++++-----------
 9 files changed, 208 insertions(+), 73 deletions(-)

diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSandboxValue.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSandboxValue.java
index 8164b31..8236ee6 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSandboxValue.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSandboxValue.java
@@ -42,7 +42,7 @@ public enum CSPDirectiveSandboxValue implements CSPRenderable
 	}
 
 	@Override
-	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	public String render(ContentSecurityPolicyEnforcer listener, RequestCycle cycle)
 	{
 		return value;
 	}
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSrcValue.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSrcValue.java
index b4a06db..0bb4a4e 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSrcValue.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSrcValue.java
@@ -32,7 +32,7 @@ public enum CSPDirectiveSrcValue implements CSPRenderable
 	NONCE("'nonce-%1$s'")
 	{
 		@Override
-		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+		public String render(ContentSecurityPolicyEnforcer listener, RequestCycle cycle)
 		{
 			return String.format(getValue(), listener.getNonce(cycle));
 		}
@@ -46,7 +46,7 @@ public enum CSPDirectiveSrcValue implements CSPRenderable
 	}
 
 	@Override
-	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	public String render(ContentSecurityPolicyEnforcer listener, RequestCycle cycle)
 	{
 		return value;
 	}
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
index 1abd1a5..91521ba 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
@@ -16,6 +16,21 @@
  */
 package org.apache.wicket.csp;
 
+import static org.apache.wicket.csp.CSPDirective.CHILD_SRC;
+import static org.apache.wicket.csp.CSPDirective.CONNECT_SRC;
+import static org.apache.wicket.csp.CSPDirective.DEFAULT_SRC;
+import static org.apache.wicket.csp.CSPDirective.FONT_SRC;
+import static org.apache.wicket.csp.CSPDirective.IMG_SRC;
+import static org.apache.wicket.csp.CSPDirective.MANIFEST_SRC;
+import static org.apache.wicket.csp.CSPDirective.SCRIPT_SRC;
+import static org.apache.wicket.csp.CSPDirective.STYLE_SRC;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.NONCE;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.NONE;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.STRICT_DYNAMIC;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.UNSAFE_EVAL;
+import static org.apache.wicket.csp.CSPDirectiveSrcValue.UNSAFE_INLINE;
+
 import java.util.ArrayList;
 import java.util.EnumMap;
 import java.util.List;
@@ -33,10 +48,50 @@ public class CSPHeaderConfiguration
 	private Map<CSPDirective, List<CSPRenderable>> directives = new EnumMap<>(CSPDirective.class);
 
 	private boolean addLegacyHeaders = false;
+	
+	private boolean nonceEnabled = false;
 
 	public CSPHeaderConfiguration()
 	{
 	}
+	
+	public CSPHeaderConfiguration disabled() {
+		return clear();
+	}
+
+	public CSPHeaderConfiguration unsafeInline()
+	{
+		return clear().addDirective(DEFAULT_SRC, NONE)
+			.addDirective(STYLE_SRC, SELF, UNSAFE_INLINE)
+			.addDirective(SCRIPT_SRC, SELF, UNSAFE_INLINE, UNSAFE_EVAL)
+			.addDirective(IMG_SRC, SELF)
+			.addDirective(FONT_SRC, SELF)
+			.addDirective(CHILD_SRC, SELF)
+			.addDirective(MANIFEST_SRC, SELF)
+			.addDirective(CONNECT_SRC, SELF);
+	}
+	
+	public CSPHeaderConfiguration strict()
+	{
+		return clear().addDirective(DEFAULT_SRC, NONE)
+			.addDirective(STYLE_SRC, NONCE)
+			.addDirective(SCRIPT_SRC, STRICT_DYNAMIC, NONCE)
+			.addDirective(IMG_SRC, SELF)
+			.addDirective(FONT_SRC, SELF)
+			.addDirective(CHILD_SRC, SELF)
+			.addDirective(MANIFEST_SRC, SELF)
+			.addDirective(CONNECT_SRC, SELF);
+	}
+
+	/**
+	 * True when the {@link CSPDirectiveSrcValue#NONCE} is used in one of the directives.
+	 * 
+	 * @return When any of the directives contains a nonce.
+	 */
+	public boolean isNonceEnabled()
+	{
+		return nonceEnabled;
+	}
 
 	/**
 	 * True when legacy headers should be added.
@@ -104,6 +159,18 @@ public class CSPHeaderConfiguration
 		return !directives.isEmpty();
 	}
 
+	/**
+	 * Removes all CSP directives from the configuration.
+	 * 
+	 * @return {@code this} for chaining.
+	 */
+	public CSPHeaderConfiguration clear()
+	{
+		directives.clear();
+		nonceEnabled = false;
+		return this;
+	}
+
 	@SuppressWarnings("deprecation")
 	private CSPHeaderConfiguration doAddDirective(CSPDirective directive, CSPRenderable value)
 	{
@@ -116,6 +183,7 @@ public class CSPHeaderConfiguration
 		List<CSPRenderable> values = directives.computeIfAbsent(directive, x -> new ArrayList<>());
 		directive.checkValueForDirective(value, values);
 		values.add(value);
+		nonceEnabled |= CSPDirectiveSrcValue.NONCE == value;
 		return this;
 	}
 
@@ -124,12 +192,12 @@ public class CSPHeaderConfiguration
 	 * in the form {@code "key1 value1a value1b; key2 value2a; key3 value3a value3b value3c"}.
 	 * 
 	 * @param listener
-	 *            The {@link CSPSettingRequestCycleListener} that renders the header.
+	 *            The {@link ContentSecurityPolicyEnforcer} that renders the header.
 	 * @param cycle
 	 *            The current {@link RequestCycle}.
 	 * @return the rendered header.
 	 */
-	public String renderHeaderValue(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	public String renderHeaderValue(ContentSecurityPolicyEnforcer listener, RequestCycle cycle)
 	{
 		return directives.entrySet()
 			.stream()
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPRenderable.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPRenderable.java
index 05badc9..0b57567 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPRenderable.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPRenderable.java
@@ -33,10 +33,10 @@ public interface CSPRenderable
 	 * Renders the value that should be put in the CSP header.
 	 * 
 	 * @param listener
-	 *            The {@link CSPSettingRequestCycleListener} that renders this value.
+	 *            The {@link ContentSecurityPolicyEnforcer} that renders this value.
 	 * @param cycle
 	 *            The current {@link RequestCycle}.
 	 * @return The rendered value.
 	 */
-	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle);
+	public String render(ContentSecurityPolicyEnforcer listener, RequestCycle cycle);
 }
\ No newline at end of file
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPSettingRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
similarity index 77%
rename from wicket-core/src/main/java/org/apache/wicket/csp/CSPSettingRequestCycleListener.java
rename to wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
index 85cff77..4d2677c 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPSettingRequestCycleListener.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
@@ -18,6 +18,7 @@ package org.apache.wicket.csp;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Predicate;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataKey;
@@ -28,6 +29,7 @@ import org.apache.wicket.request.IRequestHandlerDelegate;
 import org.apache.wicket.request.cycle.IRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.settings.SecuritySettings;
 import org.apache.wicket.util.lang.Args;
 
 /**
@@ -67,7 +69,7 @@ import org.apache.wicket.util.lang.Args;
  * @author Sven Haster
  * @author Emond Papegaaij
  */
-public class CSPSettingRequestCycleListener implements IRequestCycleListener
+public class ContentSecurityPolicyEnforcer implements IRequestCycleListener
 {
 	public static MetaDataKey<String> NONCE_KEY = new MetaDataKey<>()
 	{
@@ -77,8 +79,10 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 	private final Application application;
 	
 	private Map<CSPHeaderMode, CSPHeaderConfiguration> configs = new HashMap<>();
+	
+	private Predicate<IPageClassRequestHandler> protectedPageFilter = handler -> true;
 
-	public CSPSettingRequestCycleListener(Application application)
+	public ContentSecurityPolicyEnforcer(Application application)
 	{
 		this.application = Args.notNull(application, "application");
 	}
@@ -92,6 +96,20 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 	{
 		return configs.computeIfAbsent(CSPHeaderMode.REPORT_ONLY, x -> new CSPHeaderConfiguration());
 	}
+	
+	/**
+	 * Sets the predicate that determines which requests must be protected by the CSP. When the
+	 * predicate evaluates to false, the request for the page will not be protected.
+	 * 
+	 * @param protectedPageFilter
+	 * @return {@code this} for chaining.
+	 */
+	public ContentSecurityPolicyEnforcer
+			setProtectedPageFilter(Predicate<IPageClassRequestHandler> protectedPageFilter)
+	{
+		this.protectedPageFilter = protectedPageFilter;
+		return this;
+	}
 
 	protected boolean mustProtect(IRequestHandler handler)
 	{
@@ -103,9 +121,10 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 		}
 		return !(handler instanceof BufferedResponseRequestHandler);
 	}
-	
-	protected boolean mustProtectPageRequest(IPageClassRequestHandler handler) {
-		return true;
+
+	protected boolean mustProtectPageRequest(IPageClassRequestHandler handler)
+	{
+		return protectedPageFilter.test(handler);
 	}
 
 	@Override
@@ -124,15 +143,30 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 				webResponse.setHeader(mode.getLegacyHeader(), headerValue);
 		});
 	}
+	
+	/**
+	 * Returns true if any of the headers includes a directive with a nonce.
+	 * 
+	 * @return If a nonce is used in the CSP.
+	 */
+	public boolean isNonceEnabled()
+	{
+		return configs.values().stream().anyMatch(CSPHeaderConfiguration::isNonceEnabled);
+	}
 
 	public String getNonce(RequestCycle cycle)
 	{
 		String nonce = cycle.getMetaData(NONCE_KEY);
 		if (nonce == null)
 		{
-			nonce = application.getSecuritySettings().getRandomSupplier().getRandomBase64(12);
+			nonce = getSecuritySettings().getRandomSupplier().getRandomBase64(12);
 			cycle.setMetaData(NONCE_KEY, nonce);
 		}
 		return nonce;
 	}
+
+	private SecuritySettings getSecuritySettings()
+	{
+		return application.getSecuritySettings();
+	}
 }
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CspNonceHeaderResponseDecorator.java b/wicket-core/src/main/java/org/apache/wicket/csp/CspNonceHeaderResponseDecorator.java
index da87059..425c9db 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CspNonceHeaderResponseDecorator.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CspNonceHeaderResponseDecorator.java
@@ -16,28 +16,22 @@
  */
 package org.apache.wicket.csp;
 
-import org.apache.wicket.Application;
 import org.apache.wicket.markup.head.AbstractCspHeaderItem;
 import org.apache.wicket.markup.head.HeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.IWrappedHeaderItem;
-import org.apache.wicket.markup.head.ResourceAggregator;
 import org.apache.wicket.markup.html.DecoratingHeaderResponse;
 import org.apache.wicket.request.cycle.RequestCycle;
 
 /**
- * Add a <em>Content Security Policy<em> (CSP) nonce to all {@link AbstractCspHeaderItem}s.
- * <p>
- * Note: please don't forget to wrap with {@link ResourceAggregator} when setting it up with
- * {@link Application#setHeaderResponseDecorator}, otherwise dependencies will not be rendered.
- *
- * @see AbstractCspHeaderItem
+ * Add a <em>Content Security Policy<em> (CSP) nonce to all {@link AbstractCspHeaderItem}s when that
+ * is required by the configuration of the CSP.
  */
 public class CspNonceHeaderResponseDecorator extends DecoratingHeaderResponse
 {
-	private CSPSettingRequestCycleListener listener;
+	private ContentSecurityPolicyEnforcer listener;
 
-	public CspNonceHeaderResponseDecorator(IHeaderResponse real, CSPSettingRequestCycleListener listener)
+	public CspNonceHeaderResponseDecorator(IHeaderResponse real, ContentSecurityPolicyEnforcer listener)
 	{
 		super(real);
 
@@ -47,14 +41,17 @@ public class CspNonceHeaderResponseDecorator extends DecoratingHeaderResponse
 	@Override
 	public void render(HeaderItem item)
 	{
-		while (item instanceof IWrappedHeaderItem)
+		if (listener.isNonceEnabled())
 		{
-			item = ((IWrappedHeaderItem) item).getWrapped();
-		}
+			while (item instanceof IWrappedHeaderItem)
+			{
+				item = ((IWrappedHeaderItem) item).getWrapped();
+			}
 
-		if (item instanceof AbstractCspHeaderItem)
-		{
-			((AbstractCspHeaderItem) item).setNonce(listener.getNonce(RequestCycle.get()));
+			if (item instanceof AbstractCspHeaderItem)
+			{
+				((AbstractCspHeaderItem) item).setNonce(listener.getNonce(RequestCycle.get()));
+			}
 		}
 
 		super.render(item);
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java b/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
index 25886d6..a091b6a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
@@ -43,7 +43,7 @@ public class FixedCSPDirective implements CSPRenderable
 	}
 
 	@Override
-	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	public String render(ContentSecurityPolicyEnforcer listener, RequestCycle cycle)
 	{
 		return value;
 	}
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
index ab5beed..8704d01 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
@@ -40,6 +40,9 @@ import org.apache.wicket.core.request.mapper.PackageMapper;
 import org.apache.wicket.core.request.mapper.ResourceMapper;
 import org.apache.wicket.core.util.file.WebApplicationPath;
 import org.apache.wicket.core.util.resource.ClassPathResourceFinder;
+import org.apache.wicket.csp.CSPHeaderConfiguration;
+import org.apache.wicket.csp.ContentSecurityPolicyEnforcer;
+import org.apache.wicket.csp.CspNonceHeaderResponseDecorator;
 import org.apache.wicket.markup.MarkupType;
 import org.apache.wicket.markup.head.CssHeaderItem;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
@@ -144,6 +147,8 @@ public abstract class WebApplication extends Application
 	 * runtime.
 	 */
 	private RuntimeConfigurationType configurationType;
+	
+	private ContentSecurityPolicyEnforcer cspEnforcer;
 
 	/**
 	 * Covariant override for easy getting the current {@link WebApplication} without having to cast
@@ -762,6 +767,12 @@ public abstract class WebApplication extends Application
 		getHeaderContributorListeners().add(head -> head.render(
 			CssHeaderItem.forReference(getResourceSettings().getWicketCoreCSS())));
 
+		cspEnforcer = newCspEnforcer();
+		getRequestCycleListeners().add(getCsp());
+		getHeaderResponseDecorators()
+			.add(response -> new CspNonceHeaderResponseDecorator(response, getCsp()));
+		getCsp().blocking().unsafeInline();
+		
 		// Configure the app.
 		configure();
 	}
@@ -1073,6 +1084,31 @@ public abstract class WebApplication extends Application
 		}
 		return filterFactoryManager;
 	}
+	
+	/**
+	 * Builds the {@link ContentSecurityPolicyEnforcer} to be used for this application. Override
+	 * this method to provider your own implementation.
+	 * 
+	 * @return The newly created CSP enforcer.
+	 */
+	protected ContentSecurityPolicyEnforcer newCspEnforcer()
+	{
+		return new ContentSecurityPolicyEnforcer(this);
+	}
+
+	/**
+	 * Returns the {@link ContentSecurityPolicyEnforcer} for this application. See
+	 * {@link ContentSecurityPolicyEnforcer} and {@link CSPHeaderConfiguration} for instructions on
+	 * configuring the CSP for your specific needs.
+	 * 
+	 * @return The {@link ContentSecurityPolicyEnforcer} for this application.
+	 * @see ContentSecurityPolicyEnforcer
+	 * @see CSPHeaderConfiguration
+	 */
+	public ContentSecurityPolicyEnforcer getCsp()
+	{
+		return cspEnforcer;
+	}
 
 	/**
 	 * If true, auto label css classes such as {@code error} and {@code required} will be updated
diff --git a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
index d4893cd..5a7be31 100644
--- a/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/csp/CSPSettingRequestCycleListenerTest.java
@@ -62,8 +62,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testNullSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, (String) null);
 		});
@@ -72,8 +72,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testEmptySrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, "");
 		});
@@ -86,8 +86,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testInvalidSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, "abc?^()-_\'xyz");
 		});
@@ -100,8 +100,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSrcInputWithNoneIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, SELF, NONE);
 		});
@@ -114,8 +114,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSrcInputWithNoneIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, NONE, SELF);
 		});
@@ -128,8 +128,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSrcInputWithStarIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(DEFAULT_SRC, SELF);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, WILDCARD);
@@ -143,8 +143,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSrcInputWithStarIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(DEFAULT_SRC, WILDCARD);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, SELF);
@@ -154,8 +154,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testWrongSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, ALLOW_FORMS);
 		});
@@ -164,8 +164,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testWrongSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, SELF);
 		});
@@ -174,8 +174,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testNullSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, (String) null);
 		});
@@ -184,16 +184,16 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testEmptySandboxInputIsAccepted()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(SANDBOX, CSPDirectiveSandboxValue.EMPTY);
 	}
 
 	@Test
 	public void testInvalidSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, "abcxyz");
 		});
@@ -202,8 +202,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSandboxInputWithEmptyStringIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(SANDBOX, ALLOW_FORMS);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, EMPTY);
@@ -213,8 +213,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testMultipleSandboxInputWithEmptyStringIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(SANDBOX, EMPTY);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, ALLOW_FORMS);
@@ -224,8 +224,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testNullReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, (String) null);
 		});
@@ -234,8 +234,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testEmptyReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, "");
 		});
@@ -244,8 +244,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testInvalidReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, "abc?^()-_\'xyz");
 		});
@@ -254,8 +254,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testAllCSPSrcDefaultEnumsAreSetCorrectly() throws NoSuchAlgorithmException
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 
 		final int cspDirectiveCount = CSPDirective.values().length;
 		final int cspDirectiveSrcValueCount = CSPDirectiveSrcValue.values().length;
@@ -284,8 +284,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testCSPReportUriDirectiveSetCorrectly()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(REPORT_URI, "http://report.example.com");
 		cspListener.reporting().addDirective(REPORT_URI, "/example-report-uri");
 
@@ -300,8 +300,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testCSPSandboxDirectiveSetCorrectly()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		final int cspSandboxDirectiveValueCount = CSPDirectiveSandboxValue.values().length;
 		for (int i = 0; i < cspSandboxDirectiveValueCount; i++)
 		{
@@ -329,8 +329,8 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 	@Test
 	public void testChildSrcDirectiveAlsoSetsFrameSrcDirective()
 	{
-		CSPSettingRequestCycleListener cspListener =
-			new CSPSettingRequestCycleListener(tester.getApplication());
+		ContentSecurityPolicyEnforcer cspListener =
+			new ContentSecurityPolicyEnforcer(tester.getApplication());
 		cspListener.blocking().addDirective(CHILD_SRC, SELF);
 		cspListener.reporting().addDirective(CHILD_SRC, SELF);
 		StringBuffer headerErrors = checkHeaders(cspListener);
@@ -341,7 +341,7 @@ public class CSPSettingRequestCycleListenerTest extends WicketTestCase
 		}
 	}
 
-	private StringBuffer checkHeaders(CSPSettingRequestCycleListener cspListener)
+	private StringBuffer checkHeaders(ContentSecurityPolicyEnforcer cspListener)
 	{
 		StringBuffer headerErrors = new StringBuffer();
 		wicketTester.getRequestCycle().getListeners().add(cspListener);