In putting together the PAYPAL integration for the CART plugin, I discovered more about the depth of the security consciousness of the devs (OK, let's be honest, probably Mike!). - They (he?) thought of everything, it seems. Including "Content-Security-Policy" headers.
In the case of PAYPAL (and other payment processors), there's a need to modify the CSP to allow the checkout.js script from PAYPAL to run (optimally [according to them] it should be loaded from THEIR server). This is something that numerous others do as well (eg. STRIPE - a creditcard processor)]. But as is, the Content-Security-Policy breaks loading of off-site scripts.
One solution is to weaken security and turn off Content-Security-Policy headers in .htconfig.php, as the comment indicates:
// These lines set additional security headers to be sent with all responses
// You may wish to set transport_security_header to 0 if your server already sends
// this header. content_security_policy may need to be disabled if you wish to
// run the piwik analytics plugin or include other offsite resources on a page
It seems, thought, that the list of things that may require disabling is only likely to grow, and I really like the CSP restrictions.
There are a several ways to make it so the CSP can be manipulated - but it seems to me that the best solution is one that allows addon authors to change the string in a context dependent manner rather than needing to directly manipulate the string (and possibly remove or corrupt something important).
As a proposal, I suggest a hook based solution - which would allow the inclusion through a registered hook or by using the Hook::insert() function for inclusion in narrowly contexts (eg., only scripts from a certain offsite host are only permitted in pages originating with a specific plugin).
Anyway, here's the proposed code for initial review/consideration. If there are better implementations - that'd be great - this is just a first stab. If it looks OK as is, I'll submit a Merge Request.
FILE: boot.php
if(App::$config['system']['content_security_policy']) {
$cspsettings = Array (
'script-src' => Array ("'self'","'unsafe-inline'","'unsafe-eval'"),
'style-src' => Array ("'self'","'unsafe-inline'")
);
call_hooks('content_security_policy',$cspsettings);
// Legitimate CSP directives (cxref: https://content-security-policy.com/)
$validcspdirectives=Array(
"default-src", "script-src", "style-src",
"img-src", "connect-src", "font-src",
"object-src", "media-src", 'frame-src',
'sandbox', 'report-uri', 'child-src',
'form-action', 'frame-ancestors', 'plugin-types'
);
$cspheader = "Content-Security-Policy:";
foreach ($cspsettings as $cspdirective => $csp) {
if (!in_array($cspdirective,$validcspdirectives)) {
logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_NORMAL);
continue;
}
$cspsettingsarray=array_unique($cspsettings[$cspdirective]);
$cspsetpolicy = implode(' ',$cspsettingsarray);
if ($cspsetpolicy) {
$cspheader .= " ".$cspdirective." ".$cspsetpolicy.";";
}
}
header($cspheader);
//Original CSP Header
//header("Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'");
}