Merge pull request #13989 from owncloud/enhancment/security/11857
Allow AppFramework applications to specify a custom CSP header
This commit is contained in:
commit
8d09cc3b91
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, ';');
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue