Merge pull request #21989 from owncloud/make-csp-modifiable
Add public API to give developers the possibility to adjust the global CSP defaults
This commit is contained in:
commit
32067ac49b
15 changed files with 1315 additions and 347 deletions
|
@ -32,7 +32,6 @@ namespace OC\AppFramework\DependencyInjection;
|
|||
|
||||
use OC;
|
||||
use OC\AppFramework\Http;
|
||||
use OC\AppFramework\Http\Request;
|
||||
use OC\AppFramework\Http\Dispatcher;
|
||||
use OC\AppFramework\Http\Output;
|
||||
use OC\AppFramework\Core\API;
|
||||
|
@ -43,8 +42,6 @@ use OC\AppFramework\Middleware\SessionMiddleware;
|
|||
use OC\AppFramework\Utility\SimpleContainer;
|
||||
use OCP\AppFramework\IApi;
|
||||
use OCP\AppFramework\IAppContainer;
|
||||
use OCP\AppFramework\Middleware;
|
||||
use OCP\IServerContainer;
|
||||
|
||||
|
||||
class DIContainer extends SimpleContainer implements IAppContainer {
|
||||
|
@ -255,6 +252,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
|
|||
return $this->getServer()->getSession();
|
||||
});
|
||||
|
||||
$this->registerService('OCP\\Security\\IContentSecurityPolicyManager', function($c) {
|
||||
return $this->getServer()->getContentSecurityPolicyManager();
|
||||
});
|
||||
|
||||
$this->registerService('ServerContainer', function ($c) {
|
||||
return $this->getServer();
|
||||
});
|
||||
|
@ -319,7 +320,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
|
|||
$app->getServer()->getLogger(),
|
||||
$c['AppName'],
|
||||
$app->isLoggedIn(),
|
||||
$app->isAdminUser()
|
||||
$app->isAdminUser(),
|
||||
$app->getServer()->getContentSecurityPolicyManager()
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ use OC\Appframework\Middleware\Security\Exceptions\CrossSiteRequestForgeryExcept
|
|||
use OC\Appframework\Middleware\Security\Exceptions\NotAdminException;
|
||||
use OC\Appframework\Middleware\Security\Exceptions\NotLoggedInException;
|
||||
use OC\AppFramework\Utility\ControllerMethodReflector;
|
||||
use OC\Security\CSP\ContentSecurityPolicyManager;
|
||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Middleware;
|
||||
|
@ -52,15 +54,24 @@ use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
|
|||
* check fails
|
||||
*/
|
||||
class SecurityMiddleware extends Middleware {
|
||||
|
||||
/** @var INavigationManager */
|
||||
private $navigationManager;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var ControllerMethodReflector */
|
||||
private $reflector;
|
||||
/** @var string */
|
||||
private $appName;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
/** @var bool */
|
||||
private $isLoggedIn;
|
||||
/** @var bool */
|
||||
private $isAdminUser;
|
||||
/** @var ContentSecurityPolicyManager */
|
||||
private $contentSecurityPolicyManager;
|
||||
|
||||
/**
|
||||
* @param IRequest $request
|
||||
|
@ -71,6 +82,7 @@ class SecurityMiddleware extends Middleware {
|
|||
* @param string $appName
|
||||
* @param bool $isLoggedIn
|
||||
* @param bool $isAdminUser
|
||||
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager
|
||||
*/
|
||||
public function __construct(IRequest $request,
|
||||
ControllerMethodReflector $reflector,
|
||||
|
@ -79,7 +91,8 @@ class SecurityMiddleware extends Middleware {
|
|||
ILogger $logger,
|
||||
$appName,
|
||||
$isLoggedIn,
|
||||
$isAdminUser) {
|
||||
$isAdminUser,
|
||||
ContentSecurityPolicyManager $contentSecurityPolicyManager) {
|
||||
$this->navigationManager = $navigationManager;
|
||||
$this->request = $request;
|
||||
$this->reflector = $reflector;
|
||||
|
@ -88,6 +101,7 @@ class SecurityMiddleware extends Middleware {
|
|||
$this->logger = $logger;
|
||||
$this->isLoggedIn = $isLoggedIn;
|
||||
$this->isAdminUser = $isAdminUser;
|
||||
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
|
||||
}
|
||||
|
||||
|
||||
|
@ -139,6 +153,25 @@ class SecurityMiddleware extends Middleware {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the default CSP modifications that may be injected by other
|
||||
* applications
|
||||
*
|
||||
* @param Controller $controller
|
||||
* @param string $methodName
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
*/
|
||||
public function afterController($controller, $methodName, Response $response) {
|
||||
$policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy();
|
||||
|
||||
$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
|
||||
$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
|
||||
|
||||
$response->setContentSecurityPolicy($defaultPolicy);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* If an SecurityException is being caught, ajax requests return a JSON error
|
||||
|
|
199
lib/private/security/csp/contentsecuritypolicy.php
Normal file
199
lib/private/security/csp/contentsecuritypolicy.php
Normal file
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
namespace OC\Security\CSP;
|
||||
|
||||
/**
|
||||
* Class ContentSecurityPolicy extends the public class and adds getter and setters.
|
||||
* This is necessary since we don't want to expose the setters and getters to the
|
||||
* public API.
|
||||
*
|
||||
* @package OC\Security\CSP
|
||||
*/
|
||||
class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy {
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInlineScriptAllowed() {
|
||||
return $this->inlineScriptAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $inlineScriptAllowed
|
||||
*/
|
||||
public function setInlineScriptAllowed($inlineScriptAllowed) {
|
||||
$this->inlineScriptAllowed = $inlineScriptAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEvalScriptAllowed() {
|
||||
return $this->evalScriptAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $evalScriptAllowed
|
||||
*/
|
||||
public function setEvalScriptAllowed($evalScriptAllowed) {
|
||||
$this->evalScriptAllowed = $evalScriptAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedScriptDomains() {
|
||||
return $this->allowedScriptDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedScriptDomains
|
||||
*/
|
||||
public function setAllowedScriptDomains($allowedScriptDomains) {
|
||||
$this->allowedScriptDomains = $allowedScriptDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInlineStyleAllowed() {
|
||||
return $this->inlineStyleAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boolean $inlineStyleAllowed
|
||||
*/
|
||||
public function setInlineStyleAllowed($inlineStyleAllowed) {
|
||||
$this->inlineStyleAllowed = $inlineStyleAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedStyleDomains() {
|
||||
return $this->allowedStyleDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedStyleDomains
|
||||
*/
|
||||
public function setAllowedStyleDomains($allowedStyleDomains) {
|
||||
$this->allowedStyleDomains = $allowedStyleDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedImageDomains() {
|
||||
return $this->allowedImageDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedImageDomains
|
||||
*/
|
||||
public function setAllowedImageDomains($allowedImageDomains) {
|
||||
$this->allowedImageDomains = $allowedImageDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedConnectDomains() {
|
||||
return $this->allowedConnectDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedConnectDomains
|
||||
*/
|
||||
public function setAllowedConnectDomains($allowedConnectDomains) {
|
||||
$this->allowedConnectDomains = $allowedConnectDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedMediaDomains() {
|
||||
return $this->allowedMediaDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedMediaDomains
|
||||
*/
|
||||
public function setAllowedMediaDomains($allowedMediaDomains) {
|
||||
$this->allowedMediaDomains = $allowedMediaDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedObjectDomains() {
|
||||
return $this->allowedObjectDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedObjectDomains
|
||||
*/
|
||||
public function setAllowedObjectDomains($allowedObjectDomains) {
|
||||
$this->allowedObjectDomains = $allowedObjectDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedFrameDomains() {
|
||||
return $this->allowedFrameDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedFrameDomains
|
||||
*/
|
||||
public function setAllowedFrameDomains($allowedFrameDomains) {
|
||||
$this->allowedFrameDomains = $allowedFrameDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedFontDomains() {
|
||||
return $this->allowedFontDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedFontDomains
|
||||
*/
|
||||
public function setAllowedFontDomains($allowedFontDomains) {
|
||||
$this->allowedFontDomains = $allowedFontDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedChildSrcDomains() {
|
||||
return $this->allowedChildSrcDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $allowedChildSrcDomains
|
||||
*/
|
||||
public function setAllowedChildSrcDomains($allowedChildSrcDomains) {
|
||||
$this->allowedChildSrcDomains = $allowedChildSrcDomains;
|
||||
}
|
||||
|
||||
}
|
73
lib/private/security/csp/contentsecuritypolicymanager.php
Normal file
73
lib/private/security/csp/contentsecuritypolicymanager.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Security\CSP;
|
||||
|
||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||
use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
|
||||
use OCP\Security\IContentSecurityPolicyManager;
|
||||
|
||||
class ContentSecurityPolicyManager implements IContentSecurityPolicyManager {
|
||||
/** @var ContentSecurityPolicy[] */
|
||||
private $policies = [];
|
||||
|
||||
/** {@inheritdoc} */
|
||||
public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) {
|
||||
$this->policies[] = $policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured default policy. This is not in the public namespace
|
||||
* as it is only supposed to be used by core itself.
|
||||
*
|
||||
* @return ContentSecurityPolicy
|
||||
*/
|
||||
public function getDefaultPolicy() {
|
||||
$defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy();
|
||||
foreach($this->policies as $policy) {
|
||||
$defaultPolicy = $this->mergePolicies($defaultPolicy, $policy);
|
||||
}
|
||||
return $defaultPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the first given policy with the second one
|
||||
*
|
||||
* @param ContentSecurityPolicy $defaultPolicy
|
||||
* @param EmptyContentSecurityPolicy $originalPolicy
|
||||
* @return ContentSecurityPolicy
|
||||
*/
|
||||
public function mergePolicies(ContentSecurityPolicy $defaultPolicy,
|
||||
EmptyContentSecurityPolicy $originalPolicy) {
|
||||
foreach((object)(array)$originalPolicy as $name => $value) {
|
||||
$setter = 'set'.ucfirst($name);
|
||||
if(is_array($value)) {
|
||||
$getter = 'get'.ucfirst($name);
|
||||
$currentValues = is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : [];
|
||||
$defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value))));
|
||||
} elseif (is_bool($value)) {
|
||||
$defaultPolicy->$setter($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultPolicy;
|
||||
}
|
||||
}
|
|
@ -63,6 +63,7 @@ use OC\Lock\NoopLockingProvider;
|
|||
use OC\Mail\Mailer;
|
||||
use OC\Notification\Manager;
|
||||
use OC\Security\CertificateManager;
|
||||
use OC\Security\CSP\ContentSecurityPolicyManager;
|
||||
use OC\Security\Crypto;
|
||||
use OC\Security\CSRF\CsrfTokenGenerator;
|
||||
use OC\Security\CSRF\CsrfTokenManager;
|
||||
|
@ -74,6 +75,7 @@ use OC\Security\TrustedDomainHelper;
|
|||
use OC\Session\CryptoWrapper;
|
||||
use OC\Tagging\TagMapper;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\Security\IContentSecurityPolicyManager;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
|
@ -598,6 +600,9 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
$sessionStorage
|
||||
);
|
||||
});
|
||||
$this->registerService('ContentSecurityPolicyManager', function (Server $c) {
|
||||
return new ContentSecurityPolicyManager();
|
||||
});
|
||||
$this->registerService('ShareManager', function(Server $c) {
|
||||
$config = $c->getConfig();
|
||||
$factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory');
|
||||
|
@ -1220,6 +1225,13 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
return $this->query('CsrfTokenManager');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IContentSecurityPolicyManager
|
||||
*/
|
||||
public function getContentSecurityPolicyManager() {
|
||||
return $this->query('ContentSecurityPolicyManager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Not a public API as of 8.2, wait for 9.0
|
||||
*
|
||||
|
|
|
@ -72,7 +72,7 @@ abstract class Controller {
|
|||
* @since 6.0.0 - parameter $appName was added in 7.0.0 - parameter $app was removed in 7.0.0
|
||||
*/
|
||||
public function __construct($appName,
|
||||
IRequest $request){
|
||||
IRequest $request) {
|
||||
$this->appName = $appName;
|
||||
$this->request = $request;
|
||||
|
||||
|
|
|
@ -38,17 +38,17 @@ use OCP\AppFramework\Http;
|
|||
* @package OCP\AppFramework\Http
|
||||
* @since 8.1.0
|
||||
*/
|
||||
class ContentSecurityPolicy {
|
||||
class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
|
||||
/** @var bool Whether inline JS snippets are allowed */
|
||||
private $inlineScriptAllowed = false;
|
||||
protected $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;
|
||||
protected $evalScriptAllowed = true;
|
||||
/** @var array Domains from which scripts can get loaded */
|
||||
private $allowedScriptDomains = [
|
||||
protected $allowedScriptDomains = [
|
||||
'\'self\'',
|
||||
];
|
||||
/**
|
||||
|
@ -56,342 +56,33 @@ class ContentSecurityPolicy {
|
|||
* TODO: Disallow per default
|
||||
* @link https://github.com/owncloud/core/issues/13458
|
||||
*/
|
||||
private $inlineStyleAllowed = true;
|
||||
protected $inlineStyleAllowed = true;
|
||||
/** @var array Domains from which CSS can get loaded */
|
||||
private $allowedStyleDomains = [
|
||||
protected $allowedStyleDomains = [
|
||||
'\'self\'',
|
||||
];
|
||||
/** @var array Domains from which images can get loaded */
|
||||
private $allowedImageDomains = [
|
||||
protected $allowedImageDomains = [
|
||||
'\'self\'',
|
||||
'data:',
|
||||
'blob:',
|
||||
];
|
||||
/** @var array Domains to which connections can be done */
|
||||
private $allowedConnectDomains = [
|
||||
protected $allowedConnectDomains = [
|
||||
'\'self\'',
|
||||
];
|
||||
/** @var array Domains from which media elements can be loaded */
|
||||
private $allowedMediaDomains = [
|
||||
protected $allowedMediaDomains = [
|
||||
'\'self\'',
|
||||
];
|
||||
/** @var array Domains from which object elements can be loaded */
|
||||
private $allowedObjectDomains = [];
|
||||
protected $allowedObjectDomains = [];
|
||||
/** @var array Domains from which iframes can be loaded */
|
||||
private $allowedFrameDomains = [];
|
||||
protected $allowedFrameDomains = [];
|
||||
/** @var array Domains from which fonts can be loaded */
|
||||
private $allowedFontDomains = [
|
||||
protected $allowedFontDomains = [
|
||||
'\'self\'',
|
||||
];
|
||||
/** @var array Domains from which web-workers and nested browsing content can load elements */
|
||||
private $allowedChildSrcDomains = [];
|
||||
|
||||
/**
|
||||
* Whether inline JavaScript snippets are allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function allowInlineScript($state = false) {
|
||||
$this->inlineScriptAllowed = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether eval in JavaScript is allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedScriptDomain($domain) {
|
||||
$this->allowedScriptDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed script domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowScriptDomain($domain) {
|
||||
$this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether inline CSS snippets are allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedStyleDomain($domain) {
|
||||
$this->allowedStyleDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed style domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowStyleDomain($domain) {
|
||||
$this->allowedStyleDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedFontDomain($domain) {
|
||||
$this->allowedFontDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed font domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowFontDomain($domain) {
|
||||
$this->allowedFontDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedImageDomain($domain) {
|
||||
$this->allowedImageDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed image domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowImageDomain($domain) {
|
||||
$this->allowedImageDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedConnectDomain($domain) {
|
||||
$this->allowedConnectDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed connect domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowConnectDomain($domain) {
|
||||
$this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* From which domains media elements can be embedded.
|
||||
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedMediaDomain($domain) {
|
||||
$this->allowedMediaDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed media domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowMediaDomain($domain) {
|
||||
$this->allowedMediaDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedObjectDomain($domain) {
|
||||
$this->allowedObjectDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed object domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowObjectDomain($domain) {
|
||||
$this->allowedObjectDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedFrameDomain($domain) {
|
||||
$this->allowedFrameDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed frame domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowFrameDomain($domain) {
|
||||
$this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domains from which web-workers and nested browsing content can load elements
|
||||
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedChildSrcDomain($domain) {
|
||||
$this->allowedChildSrcDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed child src domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowChildSrcDomain($domain) {
|
||||
$this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generated Content-Security-Policy as a string
|
||||
* @return string
|
||||
* @since 8.1.0
|
||||
*/
|
||||
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 .= ';';
|
||||
}
|
||||
|
||||
if(!empty($this->allowedChildSrcDomains)) {
|
||||
$policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains);
|
||||
$policy .= ';';
|
||||
}
|
||||
|
||||
return rtrim($policy, ';');
|
||||
}
|
||||
protected $allowedChildSrcDomains = [];
|
||||
}
|
||||
|
|
387
lib/public/appframework/http/emptycontentsecuritypolicy.php
Normal file
387
lib/public/appframework/http/emptycontentsecuritypolicy.php
Normal file
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author sualko <klaus@jsxc.org>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCP\AppFramework\Http;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
|
||||
/**
|
||||
* Class EmptyContentSecurityPolicy is a simple helper which allows applications
|
||||
* to modify the Content-Security-Policy sent by ownCloud. Per default the policy
|
||||
* is forbidding everything.
|
||||
*
|
||||
* As alternative with sane exemptions look at ContentSecurityPolicy
|
||||
*
|
||||
* @see \OCP\AppFramework\Http\ContentSecurityPolicy
|
||||
* @package OCP\AppFramework\Http
|
||||
* @since 9.0.0
|
||||
*/
|
||||
class EmptyContentSecurityPolicy {
|
||||
/** @var bool Whether inline JS snippets are allowed */
|
||||
protected $inlineScriptAllowed = null;
|
||||
/**
|
||||
* @var bool Whether eval in JS scripts is allowed
|
||||
* TODO: Disallow per default
|
||||
* @link https://github.com/owncloud/core/issues/11925
|
||||
*/
|
||||
protected $evalScriptAllowed = null;
|
||||
/** @var array Domains from which scripts can get loaded */
|
||||
protected $allowedScriptDomains = null;
|
||||
/**
|
||||
* @var bool Whether inline CSS is allowed
|
||||
* TODO: Disallow per default
|
||||
* @link https://github.com/owncloud/core/issues/13458
|
||||
*/
|
||||
protected $inlineStyleAllowed = null;
|
||||
/** @var array Domains from which CSS can get loaded */
|
||||
protected $allowedStyleDomains = null;
|
||||
/** @var array Domains from which images can get loaded */
|
||||
protected $allowedImageDomains = null;
|
||||
/** @var array Domains to which connections can be done */
|
||||
protected $allowedConnectDomains = null;
|
||||
/** @var array Domains from which media elements can be loaded */
|
||||
protected $allowedMediaDomains = null;
|
||||
/** @var array Domains from which object elements can be loaded */
|
||||
protected $allowedObjectDomains = null;
|
||||
/** @var array Domains from which iframes can be loaded */
|
||||
protected $allowedFrameDomains = null;
|
||||
/** @var array Domains from which fonts can be loaded */
|
||||
protected $allowedFontDomains = null;
|
||||
/** @var array Domains from which web-workers and nested browsing content can load elements */
|
||||
protected $allowedChildSrcDomains = null;
|
||||
|
||||
/**
|
||||
* Whether inline JavaScript snippets are allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function allowInlineScript($state = false) {
|
||||
$this->inlineScriptAllowed = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether eval in JavaScript is allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedScriptDomain($domain) {
|
||||
$this->allowedScriptDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed script domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowScriptDomain($domain) {
|
||||
$this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether inline CSS snippets are allowed or forbidden
|
||||
* @param bool $state
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedStyleDomain($domain) {
|
||||
$this->allowedStyleDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed style domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowStyleDomain($domain) {
|
||||
$this->allowedStyleDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedFontDomain($domain) {
|
||||
$this->allowedFontDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed font domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowFontDomain($domain) {
|
||||
$this->allowedFontDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedImageDomain($domain) {
|
||||
$this->allowedImageDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed image domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowImageDomain($domain) {
|
||||
$this->allowedImageDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedConnectDomain($domain) {
|
||||
$this->allowedConnectDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed connect domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowConnectDomain($domain) {
|
||||
$this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* From which domains media elements can be embedded.
|
||||
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedMediaDomain($domain) {
|
||||
$this->allowedMediaDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed media domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowMediaDomain($domain) {
|
||||
$this->allowedMediaDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedObjectDomain($domain) {
|
||||
$this->allowedObjectDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed object domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowObjectDomain($domain) {
|
||||
$this->allowedObjectDomains = array_diff($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
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedFrameDomain($domain) {
|
||||
$this->allowedFrameDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed frame domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowFrameDomain($domain) {
|
||||
$this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domains from which web-workers and nested browsing content can load elements
|
||||
* @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function addAllowedChildSrcDomain($domain) {
|
||||
$this->allowedChildSrcDomains[] = $domain;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified allowed child src domain from the allowed domains.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return $this
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function disallowChildSrcDomain($domain) {
|
||||
$this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generated Content-Security-Policy as a string
|
||||
* @return string
|
||||
* @since 8.1.0
|
||||
*/
|
||||
public function buildPolicy() {
|
||||
$policy = "default-src 'none';";
|
||||
|
||||
if(!empty($this->allowedScriptDomains) || $this->inlineScriptAllowed || $this->evalScriptAllowed) {
|
||||
$policy .= 'script-src ';
|
||||
if(is_array($this->allowedScriptDomains)) {
|
||||
$policy .= implode(' ', $this->allowedScriptDomains);
|
||||
}
|
||||
if($this->inlineScriptAllowed) {
|
||||
$policy .= ' \'unsafe-inline\'';
|
||||
}
|
||||
if($this->evalScriptAllowed) {
|
||||
$policy .= ' \'unsafe-eval\'';
|
||||
}
|
||||
$policy .= ';';
|
||||
}
|
||||
|
||||
if(!empty($this->allowedStyleDomains) || $this->inlineStyleAllowed) {
|
||||
$policy .= 'style-src ';
|
||||
if(is_array($this->allowedStyleDomains)) {
|
||||
$policy .= 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 .= ';';
|
||||
}
|
||||
|
||||
if(!empty($this->allowedChildSrcDomains)) {
|
||||
$policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains);
|
||||
$policy .= ';';
|
||||
}
|
||||
|
||||
return rtrim($policy, ';');
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@
|
|||
// use OCP namespace for all classes that are considered public.
|
||||
// This means that they should be used by apps instead of the internal ownCloud classes
|
||||
namespace OCP;
|
||||
use OCP\Security\IContentSecurityPolicyManager;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
|
||||
|
@ -512,4 +513,10 @@ interface IServerContainer {
|
|||
* @since 9.0.0
|
||||
*/
|
||||
public function getShareManager();
|
||||
|
||||
/**
|
||||
* @return IContentSecurityPolicyManager
|
||||
* @since 9.0.0
|
||||
*/
|
||||
public function getContentSecurityPolicyManager();
|
||||
}
|
||||
|
|
50
lib/public/security/icontentsecuritypolicymanager.php
Normal file
50
lib/public/security/icontentsecuritypolicymanager.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCP\Security;
|
||||
use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
|
||||
|
||||
/**
|
||||
* Used for Content Security Policy manipulations
|
||||
*
|
||||
* @package OCP\Security
|
||||
* @since 9.0.0
|
||||
*/
|
||||
interface IContentSecurityPolicyManager {
|
||||
/**
|
||||
* Allows to inject something into the default content policy. This is for
|
||||
* example useful when you're injecting Javascript code into a view belonging
|
||||
* to another controller and cannot modify its Content-Security-Policy itself.
|
||||
* Note that the adjustment is only applied to applications that use AppFramework
|
||||
* controllers.
|
||||
*
|
||||
* To use this from your `app.php` use `\OC::$server->getContentSecurityPolicyManager()->addDefaultPolicy($policy)`,
|
||||
* $policy has to be of type `\OCP\AppFramework\Http\ContentSecurityPolicy`.
|
||||
*
|
||||
* WARNING: Using this API incorrectly may make the instance more insecure.
|
||||
* Do think twice before adding whitelisting resources. Please do also note
|
||||
* that it is not possible to use the `disallowXYZ` functions.
|
||||
*
|
||||
* @param EmptyContentSecurityPolicy $policy
|
||||
* @since 9.0.0
|
||||
*/
|
||||
public function addDefaultPolicy(EmptyContentSecurityPolicy $policy);
|
||||
}
|
|
@ -426,21 +426,4 @@ class ContentSecurityPolicyTest extends \Test\TestCase {
|
|||
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com');
|
||||
$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' data: blob: 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;child-src child.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')
|
||||
->addAllowedChildSrcDomain('child.owncloud.org')
|
||||
->addAllowedFrameDomain('frame.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
}
|
||||
|
|
430
tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php
Normal file
430
tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?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\EmptyContentSecurityPolicy;
|
||||
|
||||
/**
|
||||
* Class ContentSecurityPolicyTest
|
||||
*
|
||||
* @package OC\AppFramework\Http
|
||||
*/
|
||||
class EmptyContentSecurityPolicyTest extends \Test\TestCase {
|
||||
|
||||
/** @var EmptyContentSecurityPolicy */
|
||||
private $contentSecurityPolicy;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->contentSecurityPolicy = new EmptyContentSecurityPolicy();
|
||||
}
|
||||
|
||||
public function testGetPolicyDefault() {
|
||||
$defaultPolicy = "default-src 'none'";
|
||||
$this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyScriptDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';script-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyScriptDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';script-src www.owncloud.com www.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowScriptDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowScriptDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';script-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowScriptDomainMultipleStacked() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyScriptAllowInline() {
|
||||
$expectedPolicy = "default-src 'none';script-src 'unsafe-inline'";
|
||||
|
||||
$this->contentSecurityPolicy->allowInlineScript(true);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyScriptAllowInlineWithDomain() {
|
||||
$expectedPolicy = "default-src 'none';script-src www.owncloud.com 'unsafe-inline'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->allowInlineScript(true);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyScriptAllowInlineAndEval() {
|
||||
$expectedPolicy = "default-src 'none';script-src 'unsafe-inline' 'unsafe-eval'";
|
||||
|
||||
$this->contentSecurityPolicy->allowInlineScript(true);
|
||||
$this->contentSecurityPolicy->allowEvalScript(true);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyStyleDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';style-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyStyleDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';style-src www.owncloud.com www.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowStyleDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowStyleDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';style-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowStyleDomainMultipleStacked() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyStyleAllowInline() {
|
||||
$expectedPolicy = "default-src 'none';style-src 'unsafe-inline'";
|
||||
|
||||
$this->contentSecurityPolicy->allowInlineStyle(true);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyStyleAllowInlineWithDomain() {
|
||||
$expectedPolicy = "default-src 'none';style-src www.owncloud.com 'unsafe-inline'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->allowInlineStyle(true);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyStyleDisallowInline() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->allowInlineStyle(false);
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyImageDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';img-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyImageDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';img-src www.owncloud.com www.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowImageDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowImageDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';img-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowImageDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyFontDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';font-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyFontDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';font-src www.owncloud.com www.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowFontDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowFontDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';font-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowFontDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyConnectDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';connect-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyConnectDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';connect-src www.owncloud.com www.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowConnectDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowConnectDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';connect-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowConnectDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyMediaDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';media-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyMediaDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';media-src 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 testGetPolicyDisallowMediaDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowMediaDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';media-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowMediaDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyObjectDomainValid() {
|
||||
$expectedPolicy = "default-src 'none';object-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyObjectDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';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 testGetPolicyDisallowObjectDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowObjectDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';object-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowObjectDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetAllowedFrameDomain() {
|
||||
$expectedPolicy = "default-src 'none';frame-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyFrameDomainValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';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 testGetPolicyDisallowFrameDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowFrameDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';frame-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowFrameDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetAllowedChildSrcDomain() {
|
||||
$expectedPolicy = "default-src 'none';child-src child.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyChildSrcValidMultiple() {
|
||||
$expectedPolicy = "default-src 'none';child-src child.owncloud.com child.owncloud.org";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com');
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowChildSrcDomain() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowChildSrcDomainMultiple() {
|
||||
$expectedPolicy = "default-src 'none';child-src www.owncloud.com";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
|
||||
public function testGetPolicyDisallowChildSrcDomainMultipleStakes() {
|
||||
$expectedPolicy = "default-src 'none'";
|
||||
|
||||
$this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
|
||||
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com');
|
||||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ use OC\Appframework\Middleware\Security\Exceptions\NotAdminException;
|
|||
use OC\Appframework\Middleware\Security\Exceptions\NotLoggedInException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
|
||||
use OC\AppFramework\Utility\ControllerMethodReflector;
|
||||
use OC\Security\CSP\ContentSecurityPolicy;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
@ -48,6 +49,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
private $logger;
|
||||
private $navigationManager;
|
||||
private $urlGenerator;
|
||||
private $contentSecurityPolicyManager;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
@ -72,6 +74,10 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
'OCP\IRequest')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->contentSecurityPolicyManager = $this->getMockBuilder(
|
||||
'OC\Security\CSP\ContentSecurityPolicyManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->middleware = $this->getMiddleware(true, true);
|
||||
$this->secException = new SecurityException('hey', false);
|
||||
$this->secAjaxException = new SecurityException('hey', true);
|
||||
|
@ -91,7 +97,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
$this->logger,
|
||||
'files',
|
||||
$isLoggedIn,
|
||||
$isAdminUser
|
||||
$isAdminUser,
|
||||
$this->contentSecurityPolicyManager
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -410,5 +417,31 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
$this->assertTrue($response instanceof JSONResponse);
|
||||
}
|
||||
|
||||
public function testAfterController() {
|
||||
$response = $this->getMockBuilder('\OCP\AppFramework\Http\Response')->disableOriginalConstructor()->getMock();
|
||||
$defaultPolicy = new ContentSecurityPolicy();
|
||||
$defaultPolicy->addAllowedImageDomain('defaultpolicy');
|
||||
$currentPolicy = new ContentSecurityPolicy();
|
||||
$currentPolicy->addAllowedConnectDomain('currentPolicy');
|
||||
$mergedPolicy = new ContentSecurityPolicy();
|
||||
$mergedPolicy->addAllowedMediaDomain('mergedPolicy');
|
||||
$response
|
||||
->expects($this->exactly(2))
|
||||
->method('getContentSecurityPolicy')
|
||||
->willReturn($currentPolicy);
|
||||
$this->contentSecurityPolicyManager
|
||||
->expects($this->once())
|
||||
->method('getDefaultPolicy')
|
||||
->willReturn($defaultPolicy);
|
||||
$this->contentSecurityPolicyManager
|
||||
->expects($this->once())
|
||||
->method('mergePolicies')
|
||||
->with($defaultPolicy, $currentPolicy)
|
||||
->willReturn($mergedPolicy);
|
||||
$response->expects($this->once())
|
||||
->method('setContentSecurityPolicy')
|
||||
->with($mergedPolicy);
|
||||
|
||||
$this->middleware->afterController($this->controller, 'test', $response);
|
||||
}
|
||||
}
|
||||
|
|
66
tests/lib/security/csp/ContentSecurityPolicyManagerTest.php
Normal file
66
tests/lib/security/csp/ContentSecurityPolicyManagerTest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
use OC\Security\CSP\ContentSecurityPolicyManager;
|
||||
|
||||
class ContentSecurityPolicyManagerTest extends \Test\TestCase {
|
||||
/** @var ContentSecurityPolicyManager */
|
||||
private $contentSecurityPolicyManager;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->contentSecurityPolicyManager = new ContentSecurityPolicyManager();
|
||||
}
|
||||
|
||||
public function testAddDefaultPolicy() {
|
||||
$this->contentSecurityPolicyManager->addDefaultPolicy(new \OCP\AppFramework\Http\ContentSecurityPolicy());
|
||||
}
|
||||
|
||||
public function testGetDefaultPolicyWithPolicies() {
|
||||
$policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$policy->addAllowedFontDomain('mydomain.com');
|
||||
$policy->addAllowedImageDomain('anotherdomain.de');
|
||||
$this->contentSecurityPolicyManager->addDefaultPolicy($policy);
|
||||
$policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
|
||||
$policy->addAllowedFontDomain('example.com');
|
||||
$policy->addAllowedImageDomain('example.org');
|
||||
$policy->allowInlineScript(true);
|
||||
$this->contentSecurityPolicyManager->addDefaultPolicy($policy);
|
||||
$policy = new \OCP\AppFramework\Http\EmptyContentSecurityPolicy();
|
||||
$policy->addAllowedChildSrcDomain('childdomain');
|
||||
$policy->addAllowedFontDomain('anotherFontDomain');
|
||||
$this->contentSecurityPolicyManager->addDefaultPolicy($policy);
|
||||
|
||||
$expected = new \OC\Security\CSP\ContentSecurityPolicy();
|
||||
$expected->allowInlineScript(true);
|
||||
$expected->addAllowedFontDomain('mydomain.com');
|
||||
$expected->addAllowedFontDomain('example.com');
|
||||
$expected->addAllowedFontDomain('anotherFontDomain');
|
||||
$expected->addAllowedImageDomain('anotherdomain.de');
|
||||
$expected->addAllowedImageDomain('example.org');
|
||||
$expected->addAllowedChildSrcDomain('childdomain');
|
||||
$expectedStringPolicy = 'default-src \'none\';script-src \'self\' \'unsafe-inline\' \'unsafe-eval\';style-src \'self\' \'unsafe-inline\';img-src \'self\' data: blob: anotherdomain.de example.org;font-src \'self\' mydomain.com example.com anotherFontDomain;connect-src \'self\';media-src \'self\';child-src childdomain';
|
||||
|
||||
$this->assertEquals($expected, $this->contentSecurityPolicyManager->getDefaultPolicy());
|
||||
$this->assertSame($expectedStringPolicy, $this->contentSecurityPolicyManager->getDefaultPolicy()->buildPolicy());
|
||||
}
|
||||
|
||||
}
|
|
@ -62,10 +62,12 @@ class Server extends \Test\TestCase {
|
|||
['CapabilitiesManager', '\OC\CapabilitiesManager'],
|
||||
['ContactsManager', '\OC\ContactsManager'],
|
||||
['ContactsManager', '\OCP\Contacts\IManager'],
|
||||
['ContentSecurityPolicyManager', '\OC\Security\CSP\ContentSecurityPolicyManager'],
|
||||
['CommentsManager', '\OCP\Comments\ICommentsManager'],
|
||||
['Crypto', '\OC\Security\Crypto'],
|
||||
['Crypto', '\OCP\Security\ICrypto'],
|
||||
['CryptoWrapper', '\OC\Session\CryptoWrapper'],
|
||||
['CsrfTokenManager', '\OC\Security\CSRF\CsrfTokenManager'],
|
||||
|
||||
['DatabaseConnection', '\OC\DB\Connection'],
|
||||
['DatabaseConnection', '\OCP\IDBConnection'],
|
||||
|
|
Loading…
Reference in a new issue