diff --git a/apps/qubit/config/filters.yml b/apps/qubit/config/filters.yml
index f32765bafc..a4a9bed81a 100644
--- a/apps/qubit/config/filters.yml
+++ b/apps/qubit/config/filters.yml
@@ -25,5 +25,8 @@ QubitLimitResults:
QubitTransaction:
class: QubitTransactionFilter
+QubitCSP:
+ class: QubitCSP
+
cache: ~
execution: ~
diff --git a/config/app.yml b/config/app.yml
index c2a8ae4c9f..c8a378b313 100644
--- a/config/app.yml
+++ b/config/app.yml
@@ -59,3 +59,12 @@ all:
# Maximum allowed value for full-width treeview "items per page" setting
treeview_items_per_page_max: 10000
+
+ # Content Security Policy (CSP) header configuration. CSP settings are used
+ # only when a B5 theme is in use, otherwise these settings will be ignored.
+ csp:
+ # Configure CSP response header to be either
+ # 'Content-Security-Policy-Report-Only' or 'Content-Security-Policy'
+ response_header: Content-Security-Policy-Report-Only
+ # Configure CSP response directives.
+ directives: default-src 'self'; font-src 'self'; img-src 'self' https://www.gravatar.com/avatar/; script-src 'self'; style-src 'self' 'nonce'; frame-ancestors 'self';
diff --git a/docker/bootstrap.php b/docker/bootstrap.php
index be93ae442c..bd4cb8f58b 100644
--- a/docker/bootstrap.php
+++ b/docker/bootstrap.php
@@ -104,6 +104,9 @@ function get_host_and_port($value, $default_port)
persistent: true
read_only: false
htmlpurifier_enabled: false
+ csp:
+ response_header: Content-Security-Policy-Report-Only
+ directives: default-src 'self'; font-src 'self'; img-src 'self' https://www.gravatar.com/avatar/; script-src 'self'; style-src 'self' 'nonce'; frame-ancestors 'self';
EOT;
file_put_contents(_ATOM_DIR.'/apps/qubit/config/app.yml', $app_yml);
diff --git a/lib/filter/QubitCSPFilter.php b/lib/filter/QubitCSPFilter.php
new file mode 100644
index 0000000000..367f41c3fc
--- /dev/null
+++ b/lib/filter/QubitCSPFilter.php
@@ -0,0 +1,80 @@
+.
+ */
+
+class QubitCSP extends sfFilter
+{
+ public function execute($filterChain)
+ {
+ // Only use CSP if theme is b5.
+ if (sfConfig::get('app_b5_theme', false)) {
+ $cspResponseHeader = sfConfig::get('app_csp_response_header', '');
+
+ if (empty($cspResponseHeader)) {
+ // CSP is deactivated.
+ $filterChain->execute();
+
+ return;
+ }
+
+ $context = $this->getContext();
+ if (false === array_search($cspResponseHeader, ['Content-Security-Policy-Report-Only', 'Content-Security-Policy'])) {
+ $context->getLogger()->err(
+ sprintf(
+ 'Setting \'app_csp_response_header\' is not set properly. CSP is not being used.'
+ )
+ );
+
+ $filterChain->execute();
+
+ return;
+ }
+
+ $cspDirectives = sfConfig::get('app_csp_directives', '');
+ if (empty($cspDirectives)) {
+ $context->getLogger()->err(
+ sprintf(
+ 'Setting \'app_csp_directives\' is not set properly. CSP is not being used.'
+ )
+ );
+
+ $filterChain->execute();
+
+ return;
+ }
+
+ $nonce = $this->getRandomNonce();
+ // Set response header.
+ $context->response->setHttpHeader(
+ $cspResponseHeader,
+ $cspDirectives = str_replace('nonce', 'nonce-'.$nonce, $cspDirectives)
+ );
+ // Save for use in templates.
+ sfConfig::set('csp_nonce', 'nonce='.$nonce);
+ }
+
+ $filterChain->execute();
+ }
+
+ protected function getRandomNonce($length = 32)
+ {
+ $string = md5(rand());
+
+ return substr($string, 0, $length);
+ }
+}
diff --git a/plugins/arDominionB5Plugin/modules/accession/templates/browseSuccess.php b/plugins/arDominionB5Plugin/modules/accession/templates/browseSuccess.php
index dc9020a631..fbed2acc6d 100644
--- a/plugins/arDominionB5Plugin/modules/accession/templates/browseSuccess.php
+++ b/plugins/arDominionB5Plugin/modules/accession/templates/browseSuccess.php
@@ -43,17 +43,20 @@
getResults() as $hit) { ?>
getData(); ?>
-
+
+ |
'accession', 'slug' => $doc['slug']]); ?>
|
'accession', 'slug' => $doc['slug']]); ?>
|
-
+ |
|
sort) { ?>
-
+ |
-
+
+ |
|
-
+ |
|
-
+ |
|
-
+ |
|
diff --git a/plugins/arDominionB5Plugin/modules/informationobject/templates/_identifierOptions.php b/plugins/arDominionB5Plugin/modules/informationobject/templates/_identifierOptions.php
index ac77d98392..93b988538a 100644
--- a/plugins/arDominionB5Plugin/modules/informationobject/templates/_identifierOptions.php
+++ b/plugins/arDominionB5Plugin/modules/informationobject/templates/_identifierOptions.php
@@ -47,10 +47,13 @@ class="collapse"
-
+
+ |
|
-
+ |
|
diff --git a/plugins/arDominionB5Plugin/modules/sfIsadPlugin/templates/_event.php b/plugins/arDominionB5Plugin/modules/sfIsadPlugin/templates/_event.php
index 35eb13ca19..2361c3af76 100644
--- a/plugins/arDominionB5Plugin/modules/sfIsadPlugin/templates/_event.php
+++ b/plugins/arDominionB5Plugin/modules/sfIsadPlugin/templates/_event.php
@@ -7,10 +7,14 @@
-
+
+ |
|
-
+ |
|
diff --git a/webpack.config.js b/webpack.config.js
index 60d196a5b1..0f7bd799b8 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -79,7 +79,7 @@ module.exports = {
filename: "js/[name].bundle.[contenthash].js",
clean: true,
},
- devtool: devMode ? "eval-source-map" : "source-map",
+ devtool: "source-map",
module: {
rules: [
{
| | | |