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/16 21:11:47 UTC

[wicket] branch csp-configurable updated: WICKET-6727: more refactoring of the CSP API

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

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


The following commit(s) were added to refs/heads/csp-configurable by this push:
     new a1c9bd7  WICKET-6727: more refactoring of the CSP API
a1c9bd7 is described below

commit a1c9bd7c856379c52a22a4a7b26f33da59e3bbf5
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Thu Jan 16 22:10:44 2020 +0100

    WICKET-6727: more refactoring of the CSP API
---
 .../java/org/apache/wicket/csp/CSPDirective.java   | 191 +++++++++
 .../wicket/csp/CSPDirectiveSandboxValue.java       |  49 +++
 .../apache/wicket/csp/CSPDirectiveSrcValue.java    |  58 +++
 .../apache/wicket/csp/CSPHeaderConfiguration.java  | 143 +++++++
 .../java/org/apache/wicket/csp/CSPHeaderMode.java  |  45 +++
 .../java/org/apache/wicket/csp/CSPRenderable.java  |  42 ++
 .../wicket/csp/CSPSettingRequestCycleListener.java | 441 ++-------------------
 .../org/apache/wicket/csp/FixedCSPDirective.java   |  50 +++
 .../csp/CSPSettingRequestCycleListenerTest.java    |  96 +++--
 9 files changed, 665 insertions(+), 450 deletions(-)

diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java
new file mode 100644
index 0000000..1ea52c1
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.wicket.util.string.Strings;
+
+/**
+ * An enum holding the possible CSP Directives. Via the
+ * {@link #checkValueForDirective(CSPRenderable, List)}-method, new values can be verified before
+ * being added to the list of values for a directive.
+ */
+public enum CSPDirective
+{
+	DEFAULT_SRC("default-src"),
+	SCRIPT_SRC("script-src"),
+	STYLE_SRC("style-src"),
+	IMG_SRC("img-src"),
+	CONNECT_SRC("connect-src"),
+	FONT_SRC("font-src"),
+	OBJECT_SRC("object-src"),
+	MANIFEST_SRC("manifest-src"),
+	MEDIA_SRC("media-src"),
+	CHILD_SRC("child-src"),
+	FRAME_ANCESTORS("frame-ancestors"),
+	@Deprecated
+	/** @deprecated Gebruik CHILD-SRC, deze zet ook automatisch FRAME-SRC. */
+	FRAME_SRC("frame-src"),
+	SANDBOX("sandbox")
+	{
+		@Override
+		public void checkValueForDirective(CSPRenderable value,
+				List<CSPRenderable> existingDirectiveValues)
+		{
+			if (!existingDirectiveValues.isEmpty())
+			{
+				if (CSPDirectiveSandboxValue.EMPTY.equals(value))
+				{
+					throw new IllegalArgumentException(
+						"A sandbox directive can't contain an empty string if it already contains other values ");
+				}
+				if (existingDirectiveValues.contains(CSPDirectiveSandboxValue.EMPTY))
+				{
+					throw new IllegalArgumentException(
+						"A sandbox directive can't contain other values if it already contains an empty string");
+				}
+			}
+
+			if (!(value instanceof CSPDirectiveSandboxValue))
+			{
+				throw new IllegalArgumentException(
+					"A sandbox directive can only contain values from CSPDirectiveSandboxValue or be empty");
+			}
+		}
+	},
+	REPORT_URI("report-uri")
+	{
+		@Override
+		public void checkValueForDirective(CSPRenderable value,
+				List<CSPRenderable> existingDirectiveValues)
+		{
+			if (!existingDirectiveValues.isEmpty())
+			{
+				throw new IllegalArgumentException(
+					"A report-uri directive can only contain one uri");
+			}
+			if (!(value instanceof FixedCSPDirective))
+			{
+				throw new IllegalArgumentException(
+					"A report-uri directive can only contain an URI");
+			}
+			try
+			{
+				new URI(value.render(null, null));
+			}
+			catch (URISyntaxException urise)
+			{
+				throw new IllegalArgumentException("Illegal URI for report-uri directive", urise);
+			}
+		}
+	};
+
+	private String value;
+
+	private CSPDirective(String value)
+	{
+		this.value = value;
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+
+	/**
+	 * Check if {@code value} can be added to the list of other values.
+	 * 
+	 * @param value
+	 *            The value to add.
+	 * @param existingDirectiveValues
+	 *            The other values.
+	 * @throws IllegalArgumentException
+	 *             if the given value is invalid.
+	 */
+	public void checkValueForDirective(CSPRenderable value,
+			List<CSPRenderable> existingDirectiveValues)
+	{
+		if (!existingDirectiveValues.isEmpty())
+		{
+			if (CSPDirectiveSrcValue.WILDCARD.equals(value)
+				|| CSPDirectiveSrcValue.NONE.equals(value))
+			{
+				throw new IllegalArgumentException(
+					"A -src directive can't contain an * or a 'none' if it already contains other values ");
+			}
+			if (existingDirectiveValues.contains(CSPDirectiveSrcValue.WILDCARD)
+				|| existingDirectiveValues.contains(CSPDirectiveSrcValue.NONE))
+			{
+				throw new IllegalArgumentException(
+					"A -src directive can't contain other values if it already contains an * or a 'none'");
+			}
+		}
+
+		if (value instanceof CSPDirectiveSrcValue)
+		{
+			return;
+		}
+
+		if (value instanceof CSPDirectiveSandboxValue)
+		{
+			throw new IllegalArgumentException(
+				"A -src directive can't contain any of the sandbox directive values");
+		}
+
+		String strValue = value.render(null, null);
+		if ("data:".equals(strValue) || "https:".equals(strValue))
+		{
+			return;
+		}
+
+		// strip off "*." so "*.example.com" becomes "example.com" and we can check if
+		// it
+		// is a valid uri
+		if (strValue.startsWith("*."))
+		{
+			strValue = strValue.substring(2);
+		}
+
+		try
+		{
+			new URI(strValue);
+		}
+		catch (URISyntaxException urise)
+		{
+			throw new IllegalArgumentException("Illegal URI for -src directive", urise);
+		}
+	}
+
+	/**
+	 * @return The CSPDirective constant whose value-parameter equals the input-parameter or
+	 *         {@code null} if none can be found.
+	 */
+	public static CSPDirective fromValue(String value)
+	{
+		if (Strings.isEmpty(value))
+			return null;
+		for (int i = 0; i < values().length; i++)
+		{
+			if (value.equals(values()[i].getValue()))
+				return values()[i];
+		}
+		return null;
+	}
+}
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
new file mode 100644
index 0000000..8164b31
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSandboxValue.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * An enum representing the only possible values for the sandbox directive
+ */
+public enum CSPDirectiveSandboxValue implements CSPRenderable
+{
+	ALLOW_FORMS("allow-forms"),
+	ALLOW_SAME_ORIGIN("allow-same-origin"),
+	ALLOW_SCRIPTS("allow-scripts"),
+	ALLOW_TOP_NAVIGATION("allow-top-navigation"),
+	EMPTY("");
+
+	private String value;
+
+	private CSPDirectiveSandboxValue(String value)
+	{
+		this.value = value;
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+
+	@Override
+	public String render(CSPSettingRequestCycleListener 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
new file mode 100644
index 0000000..b4a06db
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirectiveSrcValue.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * An enum holding the default values for -src directives including the mandatory single quotes
+ */
+public enum CSPDirectiveSrcValue implements CSPRenderable
+{
+	NONE("'none'"),
+	WILDCARD("*"),
+	SELF("'self'"),
+	UNSAFE_INLINE("'unsafe-inline'"),
+	UNSAFE_EVAL("'unsafe-eval'"),
+	STRICT_DYNAMIC("'strict-dynamic'"),
+	NONCE("'nonce-%1$s'")
+	{
+		@Override
+		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+		{
+			return String.format(getValue(), listener.getNonce(cycle));
+		}
+	};
+
+	private String value;
+
+	private CSPDirectiveSrcValue(String value)
+	{
+		this.value = value;
+	}
+
+	@Override
+	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	{
+		return value;
+	}
+
+	public String getValue()
+	{
+		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
new file mode 100644
index 0000000..1abd1a5
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * 
+ * @author papegaaij
+ */
+public class CSPHeaderConfiguration
+{
+	private Map<CSPDirective, List<CSPRenderable>> directives = new EnumMap<>(CSPDirective.class);
+
+	private boolean addLegacyHeaders = false;
+
+	public CSPHeaderConfiguration()
+	{
+	}
+
+	/**
+	 * True when legacy headers should be added.
+	 * 
+	 * @return True when legacy headers should be added.
+	 */
+	public boolean isAddLegacyHeaders()
+	{
+		return addLegacyHeaders;
+	}
+
+	/**
+	 * Enable legacy {@code X-Content-Security-Policy} headers for older browsers, such as IE.
+	 * 
+	 * @param addLegacyHeaders
+	 *            True when the legacy headers should be added.
+	 * @return {@code this} for chaining
+	 */
+	public CSPHeaderConfiguration setAddLegacyHeaders(boolean addLegacyHeaders)
+	{
+		this.addLegacyHeaders = addLegacyHeaders;
+		return this;
+	}
+
+	/**
+	 * Adds the given values to the CSP directive on this configuraiton.
+	 * 
+	 * @param directive
+	 *            The directive to add the values to.
+	 * @param values
+	 *            The values to add.
+	 */
+	public CSPHeaderConfiguration addDirective(CSPDirective directive, CSPRenderable... values)
+	{
+		for (CSPRenderable value : values)
+		{
+			doAddDirective(directive, value);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds a free-form value to a directive for the CSP header. This is primarily meant to used for
+	 * URIs.
+	 * 
+	 * @param directive
+	 *            The directive to add the values to.
+	 * @param values
+	 *            The values to add.
+	 */
+	public CSPHeaderConfiguration addDirective(CSPDirective directive, String... values)
+	{
+		for (String value : values)
+		{
+			doAddDirective(directive, new FixedCSPDirective(value));
+		}
+		return this;
+	}
+
+	/**
+	 * @return true if this {@code CSPHeaderConfiguration} has any directives configured.
+	 */
+	public boolean isSet()
+	{
+		return !directives.isEmpty();
+	}
+
+	@SuppressWarnings("deprecation")
+	private CSPHeaderConfiguration doAddDirective(CSPDirective directive, CSPRenderable value)
+	{
+		// Add backwards compatible frame-src
+		// see http://caniuse.com/#feat=contentsecuritypolicy2
+		if (CSPDirective.CHILD_SRC.equals(directive))
+		{
+			doAddDirective(CSPDirective.FRAME_SRC, value);
+		}
+		List<CSPRenderable> values = directives.computeIfAbsent(directive, x -> new ArrayList<>());
+		directive.checkValueForDirective(value, values);
+		values.add(value);
+		return this;
+	}
+
+	/**
+	 * Renders this {@code CSPHeaderConfiguration} into an HTTP header. The returned String will be
+	 * in the form {@code "key1 value1a value1b; key2 value2a; key3 value3a value3b value3c"}.
+	 * 
+	 * @param listener
+	 *            The {@link CSPSettingRequestCycleListener} that renders the header.
+	 * @param cycle
+	 *            The current {@link RequestCycle}.
+	 * @return the rendered header.
+	 */
+	public String renderHeaderValue(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	{
+		return directives.entrySet()
+			.stream()
+			.map(e -> e.getKey().getValue() + " "
+				+ e.getValue()
+					.stream()
+					.map(r -> r.render(listener, cycle))
+					.collect(Collectors.joining(" ")))
+			.collect(Collectors.joining("; "));
+	}
+}
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderMode.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderMode.java
new file mode 100644
index 0000000..48a3a95
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderMode.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+/**
+ * Defines the possible headers for a CSP directive.
+ * 
+ * @author papegaaij
+ */
+public enum CSPHeaderMode
+{
+	BLOCKING("Content-Security-Policy"),
+	REPORT_ONLY("Content-Security-Policy-Report-Only");
+
+	private final String header;
+
+	private CSPHeaderMode(String header)
+	{
+		this.header = header;
+	}
+
+	public String getHeader()
+	{
+		return header;
+	}
+
+	public String getLegacyHeader()
+	{
+		return "X-" + getHeader();
+	}
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..05badc9
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPRenderable.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * {@code CSPRenderable} describes a directive that is part of a Content-Security-Policy (CSP in
+ * short). Most directives are predefined in enums.
+ * 
+ * @author papegaaij
+ * @see CSPDirectiveSrcValue
+ * @see CSPDirectiveSandboxValue
+ * @see FixedCSPDirective
+ */
+public interface CSPRenderable
+{
+	/**
+	 * Renders the value that should be put in the CSP header.
+	 * 
+	 * @param listener
+	 *            The {@link CSPSettingRequestCycleListener} that renders this value.
+	 * @param cycle
+	 *            The current {@link RequestCycle}.
+	 * @return The rendered value.
+	 */
+	public String render(CSPSettingRequestCycleListener 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/CSPSettingRequestCycleListener.java
index d688990..85cff77 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPSettingRequestCycleListener.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPSettingRequestCycleListener.java
@@ -16,23 +16,19 @@
  */
 package org.apache.wicket.csp;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.EnumMap;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
+import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.core.request.handler.BufferedResponseRequestHandler;
+import org.apache.wicket.core.request.handler.IPageClassRequestHandler;
 import org.apache.wicket.request.IRequestHandler;
+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.util.string.Strings;
+import org.apache.wicket.util.lang.Args;
 
 /**
  * An {@link IRequestCycleListener} that adds {@code Content-Security-Policy} and/or
@@ -78,425 +74,54 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 		private static final long serialVersionUID = 1L;
 	};
 
-	public static interface CSPRenderable
-	{
-		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle);
-	}
+	private final Application application;
+	
+	private Map<CSPHeaderMode, CSPHeaderConfiguration> configs = new HashMap<>();
 
-	private static final class FixedCSPDirective implements CSPRenderable
+	public CSPSettingRequestCycleListener(Application application)
 	{
-		private String value;
-
-		public FixedCSPDirective(String value)
-		{
-			if (Strings.isEmpty(value))
-				throw new IllegalArgumentException(
-					"CSP directive cannot have empty or null values");
-			this.value = value;
-		}
-
-		@Override
-		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
-		{
-			return value;
-		}
+		this.application = Args.notNull(application, "application");
 	}
 
-	/**
-	 * An enum holding the default values for -src directives including the mandatory single quotes
-	 */
-	public enum CSPDirectiveSrcValue implements CSPRenderable
+	public CSPHeaderConfiguration blocking()
 	{
-		NONE("'none'"),
-		WILDCARD("*"),
-		SELF("'self'"),
-		UNSAFE_INLINE("'unsafe-inline'"),
-		UNSAFE_EVAL("'unsafe-eval'"),
-		STRICT_DYNAMIC("'strict-dynamic'"),
-		NONCE("'nonce-%1$s'")
-		{
-			@Override
-			public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
-			{
-				return String.format(getValue(), listener.getNonce(cycle));
-			}
-		};
-
-		private String value;
-
-		private CSPDirectiveSrcValue(String value)
-		{
-			this.value = value;
-		}
-
-		@Override
-		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
-		{
-			return value;
-		}
-
-		public String getValue()
-		{
-			return value;
-		}
+		return configs.computeIfAbsent(CSPHeaderMode.BLOCKING, x -> new CSPHeaderConfiguration());
 	}
 
-	/**
-	 * An enum representing the only possible values for the sandbox directive
-	 */
-	public enum CSPDirectiveSandboxValue implements CSPRenderable
+	public CSPHeaderConfiguration reporting()
 	{
-		ALLOW_FORMS("allow-forms"),
-		ALLOW_SAME_ORIGIN("allow-same-origin"),
-		ALLOW_SCRIPTS("allow-scripts"),
-		ALLOW_TOP_NAVIGATION("allow-top-navigation"),
-		EMPTY("");
-
-		private String value;
-
-		private CSPDirectiveSandboxValue(String value)
-		{
-			this.value = value;
-		}
-
-		public String getValue()
-		{
-			return value;
-		}
-
-		@Override
-		public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
-		{
-			return value;
-		}
+		return configs.computeIfAbsent(CSPHeaderMode.REPORT_ONLY, x -> new CSPHeaderConfiguration());
 	}
 
-	/** An enum holding the possible CSP Directives */
-	public enum CSPDirective
+	protected boolean mustProtect(IRequestHandler handler)
 	{
-		DEFAULT_SRC("default-src"),
-		SCRIPT_SRC("script-src"),
-		STYLE_SRC("style-src"),
-		IMG_SRC("img-src"),
-		CONNECT_SRC("connect-src"),
-		FONT_SRC("font-src"),
-		OBJECT_SRC("object-src"),
-		MANIFEST_SRC("manifest-src"),
-		MEDIA_SRC("media-src"),
-		CHILD_SRC("child-src"),
-		FRAME_ANCESTORS("frame-ancestors"),
-		@Deprecated
-		/** @deprecated Gebruik CHILD-SRC, deze zet ook automatisch FRAME-SRC. */
-		FRAME_SRC("frame-src"),
-		SANDBOX("sandbox")
-		{
-			@Override
-			protected void checkValueForDirective(CSPRenderable value,
-					List<CSPRenderable> existingDirectiveValues)
-			{
-				if (!existingDirectiveValues.isEmpty())
-				{
-					if (CSPDirectiveSandboxValue.EMPTY.equals(value))
-					{
-						throw new IllegalArgumentException(
-							"A sandbox directive can't contain an empty string if it already contains other values ");
-					}
-					if (existingDirectiveValues.contains(CSPDirectiveSandboxValue.EMPTY))
-					{
-						throw new IllegalArgumentException(
-							"A sandbox directive can't contain other values if it already contains an empty string");
-					}
-				}
-
-				if (!(value instanceof CSPDirectiveSandboxValue))
-				{
-					throw new IllegalArgumentException(
-						"A sandbox directive can only contain values from CSPDirectiveSandboxValue or be empty");
-				}
-			}
-		},
-		REPORT_URI("report-uri")
-		{
-			@Override
-			protected void checkValueForDirective(CSPRenderable value,
-					List<CSPRenderable> existingDirectiveValues)
-			{
-				if (!existingDirectiveValues.isEmpty())
-				{
-					throw new IllegalArgumentException(
-						"A report-uri directive can only contain one uri");
-				}
-				if (!(value instanceof FixedCSPDirective))
-				{
-					throw new IllegalArgumentException(
-						"A report-uri directive can only contain an URI");
-				}
-				try
-				{
-					new URI(value.render(null, null));
-				}
-				catch (URISyntaxException urise)
-				{
-					throw new IllegalArgumentException("Illegal URI for report-uri directive",
-						urise);
-				}
-			}
-		};
-
-		private String value;
-
-		private CSPDirective(String value)
-		{
-			this.value = value;
-		}
-
-		public String getValue()
+		if (handler instanceof IRequestHandlerDelegate)
+			return mustProtect(((IRequestHandlerDelegate) handler).getDelegateHandler());
+		if (handler instanceof IPageClassRequestHandler)
 		{
-			return value;
-		}
-
-		protected void checkValueForDirective(CSPRenderable value,
-				List<CSPRenderable> existingDirectiveValues)
-		{
-			if (!existingDirectiveValues.isEmpty())
-			{
-				if (CSPDirectiveSrcValue.WILDCARD.equals(value)
-					|| CSPDirectiveSrcValue.NONE.equals(value))
-				{
-					throw new IllegalArgumentException(
-						"A -src directive can't contain an * or a 'none' if it already contains other values ");
-				}
-				if (existingDirectiveValues.contains(CSPDirectiveSrcValue.WILDCARD)
-					|| existingDirectiveValues.contains(CSPDirectiveSrcValue.NONE))
-				{
-					throw new IllegalArgumentException(
-						"A -src directive can't contain other values if it already contains an * or a 'none'");
-				}
-			}
-
-			if (value instanceof CSPDirectiveSrcValue)
-			{
-				return;
-			}
-
-			if (value instanceof CSPDirectiveSandboxValue)
-			{
-				throw new IllegalArgumentException(
-					"A -src directive can't contain any of the sandbox directive values");
-			}
-
-			String strValue = value.render(null, null);
-			if ("data:".equals(strValue) || "https:".equals(strValue))
-			{
-				return;
-			}
-
-			// strip off "*." so "*.example.com" becomes "example.com" and we can check if
-			// it
-			// is a valid uri
-			if (strValue.startsWith("*."))
-			{
-				strValue = strValue.substring(2);
-			}
-
-			try
-			{
-				new URI(strValue);
-			}
-			catch (URISyntaxException urise)
-			{
-				throw new IllegalArgumentException("Illegal URI for -src directive", urise);
-			}
-		}
-
-		/**
-		 * @return The CSPDirective constant whose value-parameter equals the input-parameter or
-		 *         {@code null} if none can be found.
-		 */
-		public static CSPDirective fromValue(String value)
-		{
-			if (Strings.isEmpty(value))
-				return null;
-			for (int i = 0; i < values().length; i++)
-			{
-				if (value.equals(values()[i].getValue()))
-					return values()[i];
-			}
-			return null;
+			return mustProtectPageRequest((IPageClassRequestHandler) handler);
 		}
+		return !(handler instanceof BufferedResponseRequestHandler);
 	}
-
-	private enum CSPHeaderMode
-	{
-		BLOCKING("Content-Security-Policy"),
-		REPORT_ONLY("Content-Security-Policy-Report-Only");
-
-		private final String header;
-
-		private CSPHeaderMode(String header)
-		{
-			this.header = header;
-		}
-
-		public String getHeader()
-		{
-			return header;
-		}
-
-		public String getLegacyHeader()
-		{
-			return "X-" + getHeader();
-		}
-	}
-
-	public class CSPConfiguration
-	{
-		private CSPHeaderMode mode;
-
-		private Map<CSPDirective, List<CSPRenderable>> directives =
-			new EnumMap<>(CSPDirective.class);
-
-		private boolean addLegacyHeaders = false;
-
-		private CSPConfiguration(CSPHeaderMode mode)
-		{
-			this.mode = mode;
-		}
-
-		public CSPHeaderMode getMode()
-		{
-			return mode;
-		}
-
-		/**
-		 * True when legacy headers should be added.
-		 * 
-		 * @return True when legacy headers should be added.
-		 */
-		public boolean isAddLegacyHeaders()
-		{
-			return addLegacyHeaders;
-		}
-
-		/**
-		 * Enable legacy {@code X-Content-Security-Policy} headers for older browsers, such as IE.
-		 * 
-		 * @param addLegacyHeaders
-		 *            True when the legacy headers should be added.
-		 * @return {@code this} for chaining
-		 */
-		public CSPConfiguration setAddLegacyHeaders(boolean addLegacyHeaders)
-		{
-			this.addLegacyHeaders = addLegacyHeaders;
-			return this;
-		}
-
-		public CSPConfiguration addDirective(CSPDirective directive, CSPDirectiveSrcValue... values)
-		{
-			for (CSPDirectiveSrcValue value : values)
-			{
-				doAddDirective(directive, value);
-			}
-			return this;
-		}
-
-		/**
-		 * Adds any of the default values to the sandbox directive for the 'blocking' CSP header.
-		 * Use {@link #addBlockingDirective(CSPDirective, String...)} with the sandbox
-		 * {@link CSPDirective} and a single empty string (<em>not</em> {@code null}) to add the
-		 * empty sandbox directive.
-		 */
-		public CSPConfiguration addDirective(CSPDirective sandboxDirective,
-				CSPDirectiveSandboxValue... values)
-		{
-			for (CSPDirectiveSandboxValue value : values)
-			{
-				doAddDirective(sandboxDirective, value);
-			}
-			return this;
-		}
-
-		/**
-		 * Adds any value to a directive for the 'blocking' CSP header. Use
-		 * {@link #addBlockingDirective(CSPDirective, CSPDirectiveSandboxValue...)} and
-		 * {@link #addBlockingDirective(CSPDirective, CSPDirectiveSrcValue...)} for the default
-		 * values for the sandbox and -src directives.
-		 */
-		public CSPConfiguration addDirective(CSPDirective directive, String... values)
-		{
-			for (String value : values)
-			{
-				doAddDirective(directive, new FixedCSPDirective(value));
-			}
-			return this;
-		}
-
-		public boolean isSet()
-		{
-			return !directives.isEmpty();
-		}
-
-		private CSPConfiguration doAddDirective(CSPDirective directive, CSPRenderable value)
-		{
-			// Add backwards compatible frame-src
-			// see http://caniuse.com/#feat=contentsecuritypolicy2
-			if (CSPDirective.CHILD_SRC.equals(directive))
-			{
-				doAddDirective(CSPDirective.FRAME_SRC, value);
-			}
-			List<CSPRenderable> values =
-				directives.computeIfAbsent(directive, x -> new ArrayList<>());
-			directive.checkValueForDirective(value, values);
-			values.add(value);
-			return this;
-		}
-
-		// @returns "key1 value1a value1b; key2 value2a; key3 value3a value3b value3c"
-		public String renderHeaderValue(RequestCycle cycle)
-		{
-			return directives.entrySet()
-				.stream()
-				.map(e -> e.getKey().getValue() + " "
-					+ e.getValue()
-						.stream()
-						.map(r -> r.render(CSPSettingRequestCycleListener.this, cycle))
-						.collect(Collectors.joining(" ")))
-				.collect(Collectors.joining("; "));
-		}
-	}
-
-	private Function<Integer, byte[]> randomSupplier;
-
-	private Map<CSPHeaderMode, CSPConfiguration> configs = new HashMap<>();
-
-	public CSPSettingRequestCycleListener()
-	{
-	}
-
-	public CSPSettingRequestCycleListener(Function<Integer, byte[]> randomSupplier)
-	{
-		this.randomSupplier = randomSupplier;
-	}
-
-	public CSPConfiguration blocking()
-	{
-		return configs.computeIfAbsent(CSPHeaderMode.BLOCKING, CSPConfiguration::new);
-	}
-
-	public CSPConfiguration reporting()
-	{
-		return configs.computeIfAbsent(CSPHeaderMode.REPORT_ONLY, CSPConfiguration::new);
+	
+	protected boolean mustProtectPageRequest(IPageClassRequestHandler handler) {
+		return true;
 	}
 
 	@Override
 	public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
 	{
+		if (!mustProtect(handler))
+			return;
+		
 		WebResponse webResponse = (WebResponse) cycle.getResponse();
-		configs.values().stream().filter(CSPConfiguration::isSet).forEach(config -> {
-			String headerValue = config.renderHeaderValue(cycle);
-			webResponse.setHeader(config.getMode().getHeader(), headerValue);
+		configs.entrySet().stream().filter(entry -> entry.getValue().isSet()).forEach(entry -> {
+			CSPHeaderMode mode = entry.getKey();
+			CSPHeaderConfiguration config = entry.getValue();
+			String headerValue = config.renderHeaderValue(this, cycle);
+			webResponse.setHeader(mode.getHeader(), headerValue);
 			if (config.isAddLegacyHeaders())
-				webResponse.setHeader(config.getMode().getLegacyHeader(), headerValue);
+				webResponse.setHeader(mode.getLegacyHeader(), headerValue);
 		});
 	}
 
@@ -505,7 +130,7 @@ public class CSPSettingRequestCycleListener implements IRequestCycleListener
 		String nonce = cycle.getMetaData(NONCE_KEY);
 		if (nonce == null)
 		{
-			nonce = Base64.getEncoder().encodeToString(randomSupplier.apply(12));
+			nonce = application.getSecuritySettings().getRandomSupplier().getRandomBase64(12);
 			cycle.setMetaData(NONCE_KEY, nonce);
 		}
 		return nonce;
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
new file mode 100644
index 0000000..25886d6
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.csp;
+
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.util.string.Strings;
+
+/**
+ * A simpel CSP directive that renders the string specified.
+ * 
+ * @author papegaaij
+ */
+public class FixedCSPDirective implements CSPRenderable
+{
+	private String value;
+
+	/**
+	 * Creates a new {@code FixedCSPDirective} for the given value.
+	 * 
+	 * @param value
+	 *            the value to render;
+	 */
+	public FixedCSPDirective(String value)
+	{
+		if (Strings.isEmpty(value))
+			throw new IllegalArgumentException(
+				"CSP directive cannot have empty or null values");
+		this.value = value;
+	}
+
+	@Override
+	public String render(CSPSettingRequestCycleListener listener, RequestCycle cycle)
+	{
+		return value;
+	}
+}
\ No newline at end of file
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 14ba223..28b292c 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
@@ -16,16 +16,16 @@
  */
 package org.apache.wicket.csp;
 
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.CHILD_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.DEFAULT_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.FRAME_SRC;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.REPORT_URI;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective.SANDBOX;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.ALLOW_FORMS;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue.EMPTY;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.NONE;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.SELF;
-import static org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue.WILDCARD;
+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.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 java.net.URI;
 import java.net.URISyntaxException;
@@ -37,9 +37,6 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirective;
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSandboxValue;
-import org.apache.wicket.csp.CSPSettingRequestCycleListener.CSPDirectiveSrcValue;
 import org.apache.wicket.mock.MockHomePage;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.Assertions;
@@ -47,7 +44,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 @SuppressWarnings("deprecation")
-public class CSPSettingRequestCycleListenerTest
+public class CSPSettingRequestCycleListenerTest extends WicketTester
 {
 	private static String HEADER_CSP = "Content-Security-Policy";
 
@@ -64,7 +61,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testNullSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, (String) null);
 		});
@@ -73,7 +71,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testEmptySrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, "");
 		});
@@ -86,7 +85,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testInvalidSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, "abc?^()-_\'xyz");
 		});
@@ -99,7 +99,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSrcInputWithNoneIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, SELF, NONE);
 		});
@@ -112,7 +113,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSrcInputWithNoneIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, NONE, SELF);
 		});
@@ -125,7 +127,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSrcInputWithStarIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(DEFAULT_SRC, SELF);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, WILDCARD);
@@ -139,7 +142,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSrcInputWithStarIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(DEFAULT_SRC, WILDCARD);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, SELF);
@@ -149,7 +153,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testWrongSrcInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(DEFAULT_SRC, ALLOW_FORMS);
 		});
@@ -158,7 +163,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testWrongSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, SELF);
 		});
@@ -167,7 +173,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testNullSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, (String) null);
 		});
@@ -176,14 +183,16 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testEmptySandboxInputIsAccepted()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(SANDBOX, CSPDirectiveSandboxValue.EMPTY);
 	}
 
 	@Test
 	public void testInvalidSandboxInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, "abcxyz");
 		});
@@ -192,7 +201,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSandboxInputWithEmptyStringIsRejected1()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(SANDBOX, ALLOW_FORMS);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, EMPTY);
@@ -202,7 +212,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testMultipleSandboxInputWithEmptyStringIsRejected2()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(SANDBOX, EMPTY);
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(SANDBOX, ALLOW_FORMS);
@@ -212,7 +223,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testNullReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, (String) null);
 		});
@@ -221,7 +233,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testEmptyReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, "");
 		});
@@ -230,7 +243,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testInvalidReportUriInputIsRejected()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		Assertions.assertThrows(IllegalArgumentException.class, () -> {
 			cspListener.blocking().addDirective(REPORT_URI, "abc?^()-_\'xyz");
 		});
@@ -239,12 +253,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testAllCSPSrcDefaultEnumsAreSetCorrectly() throws NoSuchAlgorithmException
 	{
-		SecureRandom random = SecureRandom.getInstanceStrong();
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener(length -> {
-			byte[] ret = new byte[length];
-			random.nextBytes(ret);
-			return ret;
-		});
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 
 		final int cspDirectiveCount = CSPDirective.values().length;
 		final int cspDirectiveSrcValueCount = CSPDirectiveSrcValue.values().length;
@@ -273,7 +283,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testCSPReportUriDirectiveSetCorrectly()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(REPORT_URI, "http://report.example.com");
 		cspListener.reporting().addDirective(REPORT_URI, "/example-report-uri");
 
@@ -288,14 +299,15 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testCSPSandboxDirectiveSetCorrectly()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		final int cspSandboxDirectiveValueCount = CSPDirectiveSandboxValue.values().length;
 		for (int i = 0; i < cspSandboxDirectiveValueCount; i++)
 		{
 			final CSPDirectiveSandboxValue cspDirectiveValue = CSPDirectiveSandboxValue.values()[i];
 			if (cspDirectiveValue.equals(CSPDirectiveSandboxValue.EMPTY))
 				continue;
-			
+
 			cspListener.blocking().addDirective(SANDBOX, cspDirectiveValue);
 			cspListener.reporting().addDirective(SANDBOX, cspDirectiveValue);
 		}
@@ -316,7 +328,8 @@ public class CSPSettingRequestCycleListenerTest
 	@Test
 	public void testChildSrcDirectiveAlsoSetsFrameSrcDirective()
 	{
-		CSPSettingRequestCycleListener cspListener = new CSPSettingRequestCycleListener();
+		CSPSettingRequestCycleListener cspListener =
+			new CSPSettingRequestCycleListener(getApplication());
 		cspListener.blocking().addDirective(CHILD_SRC, SELF);
 		cspListener.reporting().addDirective(CHILD_SRC, SELF);
 		StringBuffer headerErrors = checkHeaders(cspListener);
@@ -460,5 +473,4 @@ public class CSPSettingRequestCycleListenerTest
 
 		return false;
 	}
-
 }