Merge pull request #13989 from owncloud/enhancment/security/11857

Allow AppFramework applications to specify a custom CSP header
This commit is contained in:
Clark Tomlinson 2015-02-18 10:27:29 -05:00
commit 8d09cc3b91
8 changed files with 529 additions and 22 deletions

View File

@ -929,15 +929,6 @@ $CONFIG = array(
'mssql' 'mssql'
), ),
/**
* Custom CSP policy, changing this will overwrite the standard policy
*/
'custom_csp_policy' =>
"default-src 'self'; script-src 'self' 'unsafe-eval'; ".
"style-src 'self' 'unsafe-inline'; frame-src *; img-src *; ".
"font-src 'self' data:; media-src *; connect-src *",
/** /**
* All other config options * All other config options
*/ */

View File

@ -189,7 +189,7 @@ class OC_Response {
} }
} }
/* /**
* This function adds some security related headers to all requests served via base.php * This function adds some security related headers to all requests served via base.php
* The implementation of this function has to happen here to ensure that all third-party * The implementation of this function has to happen here to ensure that all third-party
* components (e.g. SabreDAV) also benefit from this headers. * components (e.g. SabreDAV) also benefit from this headers.
@ -204,17 +204,20 @@ class OC_Response {
header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains
} }
// Content Security Policy /**
// If you change the standard policy, please also change it in config.sample.php * FIXME: Content Security Policy for legacy ownCloud components. This
$policy = OC_Config::getValue('custom_csp_policy', * can be removed once \OCP\AppFramework\Http\Response from the AppFramework
'default-src \'self\'; ' * is used everywhere.
* @see \OCP\AppFramework\Http\Response::getHeaders
*/
$policy = 'default-src \'self\'; '
. 'script-src \'self\' \'unsafe-eval\'; ' . 'script-src \'self\' \'unsafe-eval\'; '
. 'style-src \'self\' \'unsafe-inline\'; ' . 'style-src \'self\' \'unsafe-inline\'; '
. 'frame-src *; ' . 'frame-src *; '
. 'img-src *; ' . 'img-src *; '
. 'font-src \'self\' data:; ' . 'font-src \'self\' data:; '
. 'media-src *; ' . 'media-src *; '
. 'connect-src *'); . 'connect-src *';
header('Content-Security-Policy:' . $policy); header('Content-Security-Policy:' . $policy);
// https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag

View File

@ -0,0 +1,241 @@
<?php
/**
* Copyright (c) 2015 Lukas Reschke lukas@owncloud.com
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OCP\AppFramework\Http;
use OCP\AppFramework\Http;
/**
* Class ContentSecurityPolicy is a simple helper which allows applications to
* modify the Content-Security-Policy sent by ownCloud. Per default only JavaScript,
* stylesheets, images, fonts, media and connections from the same domain
* ('self') are allowed.
*
* Even if a value gets modified above defaults will still get appended. Please
* notice that ownCloud ships already with sensible defaults and those policies
* should require no modification at all for most use-cases.
*
* @package OCP\AppFramework\Http
*/
class ContentSecurityPolicy {
/** @var bool Whether inline JS snippets are allowed */
private $inlineScriptAllowed = false;
/**
* @var bool Whether eval in JS scripts is allowed
* TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/11925
*/
private $evalScriptAllowed = true;
/** @var array Domains from which scripts can get loaded */
private $allowedScriptDomains = [
'\'self\'',
];
/**
* @var bool Whether inline CSS is allowed
* TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/13458
*/
private $inlineStyleAllowed = true;
/** @var array Domains from which CSS can get loaded */
private $allowedStyleDomains = [
'\'self\'',
];
/** @var array Domains from which images can get loaded */
private $allowedImageDomains = [
'\'self\'',
];
/** @var array Domains to which connections can be done */
private $allowedConnectDomains = [
'\'self\'',
];
/** @var array Domains from which media elements can be loaded */
private $allowedMediaDomains = [
'\'self\'',
];
/** @var array Domains from which object elements can be loaded */
private $allowedObjectDomains = [];
/** @var array Domains from which iframes can be loaded */
private $allowedFrameDomains = [];
/** @var array Domains from which fonts can be loaded */
private $allowedFontDomains = [
'\'self\'',
];
/**
* Whether inline JavaScript snippets are allowed or forbidden
* @param bool $state
* @return $this
*/
public function allowInlineScript($state = false) {
$this->inlineScriptAllowed = $state;
return $this;
}
/**
* Whether eval in JavaScript is allowed or forbidden
* @param bool $state
* @return $this
*/
public function allowEvalScript($state = true) {
$this->evalScriptAllowed= $state;
return $this;
}
/**
* Allows to execute JavaScript files from a specific domain. Use * to
* allow JavaScript from all domains.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedScriptDomain($domain) {
$this->allowedScriptDomains[] = $domain;
return $this;
}
/**
* Whether inline CSS snippets are allowed or forbidden
* @param bool $state
* @return $this
*/
public function allowInlineStyle($state = true) {
$this->inlineStyleAllowed = $state;
return $this;
}
/**
* Allows to execute CSS files from a specific domain. Use * to allow
* CSS from all domains.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedStyleDomain($domain) {
$this->allowedStyleDomains[] = $domain;
return $this;
}
/**
* Allows using fonts from a specific domain. Use * to allow
* fonts from all domains.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedFontDomain($domain) {
$this->allowedFontDomains[] = $domain;
return $this;
}
/**
* Allows embedding images from a specific domain. Use * to allow
* images from all domains.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedImageDomain($domain) {
$this->allowedImageDomains[] = $domain;
return $this;
}
/**
* To which remote domains the JS connect to.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedConnectDomain($domain) {
$this->allowedConnectDomains[] = $domain;
return $this;
}
/**
* From whoch domains media elements can be embedded.
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedMediaDomain($domain) {
$this->allowedMediaDomains[] = $domain;
return $this;
}
/**
* From which domains objects such as <object>, <embed> or <applet> are executed
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedObjectDomain($domain) {
$this->allowedObjectDomains[] = $domain;
return $this;
}
/**
* Which domains can be embedded in an iframe
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
* @return $this
*/
public function addAllowedFrameDomain($domain) {
$this->allowedFrameDomains[] = $domain;
return $this;
}
/**
* Get the generated Content-Security-Policy as a string
* @return string
*/
public function buildPolicy() {
$policy = "default-src 'none';";
if(!empty($this->allowedScriptDomains)) {
$policy .= 'script-src ' . implode(' ', $this->allowedScriptDomains);
if($this->inlineScriptAllowed) {
$policy .= ' \'unsafe-inline\'';
}
if($this->evalScriptAllowed) {
$policy .= ' \'unsafe-eval\'';
}
$policy .= ';';
}
if(!empty($this->allowedStyleDomains)) {
$policy .= 'style-src ' . implode(' ', $this->allowedStyleDomains);
if($this->inlineStyleAllowed) {
$policy .= ' \'unsafe-inline\'';
}
$policy .= ';';
}
if(!empty($this->allowedImageDomains)) {
$policy .= 'img-src ' . implode(' ', $this->allowedImageDomains);
$policy .= ';';
}
if(!empty($this->allowedFontDomains)) {
$policy .= 'font-src ' . implode(' ', $this->allowedFontDomains);
$policy .= ';';
}
if(!empty($this->allowedConnectDomains)) {
$policy .= 'connect-src ' . implode(' ', $this->allowedConnectDomains);
$policy .= ';';
}
if(!empty($this->allowedMediaDomains)) {
$policy .= 'media-src ' . implode(' ', $this->allowedMediaDomains);
$policy .= ';';
}
if(!empty($this->allowedObjectDomains)) {
$policy .= 'object-src ' . implode(' ', $this->allowedObjectDomains);
$policy .= ';';
}
if(!empty($this->allowedFrameDomains)) {
$policy .= 'frame-src ' . implode(' ', $this->allowedFrameDomains);
$policy .= ';';
}
return rtrim($policy, ';');
}
}

View File

@ -72,6 +72,9 @@ class Response {
*/ */
private $ETag; private $ETag;
/** @var ContentSecurityPolicy|null Used Content-Security-Policy */
private $contentSecurityPolicy = null;
/** /**
* Caches the response * Caches the response
@ -186,13 +189,19 @@ class Response {
* @return array the headers * @return array the headers
*/ */
public function getHeaders() { public function getHeaders() {
$mergeWith = array(); $mergeWith = [];
if($this->lastModified) { if($this->lastModified) {
$mergeWith['Last-Modified'] = $mergeWith['Last-Modified'] =
$this->lastModified->format(\DateTime::RFC2822); $this->lastModified->format(\DateTime::RFC2822);
} }
// Build Content-Security-Policy and use default if none has been specified
if(is_null($this->contentSecurityPolicy)) {
$this->setContentSecurityPolicy(new ContentSecurityPolicy());
}
$this->headers['Content-Security-Policy'] = $this->contentSecurityPolicy->buildPolicy();
if($this->ETag) { if($this->ETag) {
$mergeWith['ETag'] = '"' . $this->ETag . '"'; $mergeWith['ETag'] = '"' . $this->ETag . '"';
} }
@ -221,6 +230,25 @@ class Response {
return $this; return $this;
} }
/**
* Set a Content-Security-Policy
* @param ContentSecurityPolicy $csp Policy to set for the response object
* @return $this
*/
public function setContentSecurityPolicy(ContentSecurityPolicy $csp) {
$this->contentSecurityPolicy = $csp;
return $this;
}
/**
* Get the currently used Content-Security-Policy
* @return ContentSecurityPolicy|null Used Content-Security-Policy or null if
* none specified.
*/
public function getContentSecurityPolicy() {
return $this->contentSecurityPolicy;
}
/** /**
* Get response status * Get response status

View File

@ -173,11 +173,12 @@ class ControllerTest extends \Test\TestCase {
public function testFormatDataResponseJSON() { public function testFormatDataResponseJSON() {
$expectedHeaders = array( $expectedHeaders = [
'test' => 'something', 'test' => 'something',
'Cache-Control' => 'no-cache, must-revalidate', 'Cache-Control' => 'no-cache, must-revalidate',
'Content-Type' => 'application/json; charset=utf-8' 'Content-Type' => 'application/json; charset=utf-8',
); 'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'",
];
$response = $this->controller->customDataResponse(array('hi')); $response = $this->controller->customDataResponse(array('hi'));
$response = $this->controller->buildResponse($response, 'json'); $response = $this->controller->buildResponse($response, 'json');

View File

@ -0,0 +1,215 @@
<?php
/**
* Copyright (c) 2015 Lukas Reschke lukas@owncloud.com
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OC\AppFramework\Http;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\ContentSecurityPolicy;
/**
* Class ContentSecurityPolicyTest
*
* @package OC\AppFramework\Http
*/
class ContentSecurityPolicyTest extends \Test\TestCase {
/** @var ContentSecurityPolicy */
private $contentSecurityPolicy;
public function setUp() {
parent::setUp();
$this->contentSecurityPolicy = new ContentSecurityPolicy();
}
public function testGetPolicyDefault() {
$defaultPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyScriptDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyScriptDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com www.owncloud.org 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyScriptAllowInline() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->allowInlineScript(true);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyScriptAllowInlineWithDomain() {
$expectedPolicy = "default-src 'none';script-src 'self' www.owncloud.com 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
$this->contentSecurityPolicy->allowInlineScript(true);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyScriptDisallowInlineAndEval() {
$expectedPolicy = "default-src 'none';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->allowInlineScript(false);
$this->contentSecurityPolicy->allowEvalScript(false);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyStyleDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyStyleDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyStyleAllowInline() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->allowInlineStyle(true);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyStyleAllowInlineWithDomain() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyStyleDisallowInline() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->allowInlineStyle(false);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyImageDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' www.owncloud.com;font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyImageDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' www.owncloud.com www.owncloud.org;font-src 'self';connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyFontDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' www.owncloud.com;connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyFontDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self'";
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyConnectDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self' www.owncloud.com;media-src 'self'";
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyConnectDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self'";
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyMediaDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com";
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyMediaDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org";
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyObjectDomainValid() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com";
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyObjectDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org";
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetAllowedFrameDomain() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com";
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyFrameDomainValidMultiple() {
$expectedPolicy = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org";
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testConfigureStacked() {
$expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org";
$this->contentSecurityPolicy->allowInlineStyle(false)
->allowEvalScript(false)
->addAllowedScriptDomain('script.owncloud.org')
->addAllowedStyleDomain('style.owncloud.org')
->addAllowedFontDomain('font.owncloud.org')
->addAllowedImageDomain('img.owncloud.org')
->addAllowedConnectDomain('connect.owncloud.org')
->addAllowedMediaDomain('media.owncloud.org')
->addAllowedObjectDomain('objects.owncloud.org')
->addAllowedFrameDomain('frame.owncloud.org');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
}

View File

@ -66,7 +66,10 @@ class DataResponseTest extends \Test\TestCase {
$headers = array('test' => 'something'); $headers = array('test' => 'something');
$response = new DataResponse($data, $code, $headers); $response = new DataResponse($data, $code, $headers);
$expectedHeaders = array('Cache-Control' => 'no-cache, must-revalidate'); $expectedHeaders = [
'Cache-Control' => 'no-cache, must-revalidate',
'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'",
];
$expectedHeaders = array_merge($expectedHeaders, $headers); $expectedHeaders = array_merge($expectedHeaders, $headers);
$this->assertEquals($data, $response->getData()); $this->assertEquals($data, $response->getData());

View File

@ -49,7 +49,7 @@ class ResponseTest extends \Test\TestCase {
} }
function testSetHeaders(){ public function testSetHeaders() {
$expected = array( $expected = array(
'Last-Modified' => 1, 'Last-Modified' => 1,
'ETag' => 3, 'ETag' => 3,
@ -58,15 +58,40 @@ class ResponseTest extends \Test\TestCase {
$this->childResponse->setHeaders($expected); $this->childResponse->setHeaders($expected);
$headers = $this->childResponse->getHeaders(); $headers = $this->childResponse->getHeaders();
$expected['Content-Security-Policy'] = "default-src 'none';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'";
$this->assertEquals($expected, $headers); $this->assertEquals($expected, $headers);
} }
public function testOverwriteCsp() {
$expected = [
'Content-Security-Policy' => "default-src 'none';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self';connect-src 'self';media-src 'self'",
];
$policy = new Http\ContentSecurityPolicy();
$policy->allowInlineScript(true);
$this->childResponse->setContentSecurityPolicy($policy);
$headers = $this->childResponse->getHeaders();
$this->assertEquals(array_merge($expected, $headers), $headers);
}
public function testGetCsp() {
$policy = new Http\ContentSecurityPolicy();
$policy->allowInlineScript(true);
$this->childResponse->setContentSecurityPolicy($policy);
$this->assertEquals($policy, $this->childResponse->getContentSecurityPolicy());
}
public function testGetCspEmpty() {
$this->assertNull($this->childResponse->getContentSecurityPolicy());
}
public function testAddHeaderValueNullDeletesIt(){ public function testAddHeaderValueNullDeletesIt(){
$this->childResponse->addHeader('hello', 'world'); $this->childResponse->addHeader('hello', 'world');
$this->childResponse->addHeader('hello', null); $this->childResponse->addHeader('hello', null);
$this->assertEquals(1, count($this->childResponse->getHeaders())); $this->assertEquals(2, count($this->childResponse->getHeaders()));
} }