From b29940d956e638b14f2012022b0ad91ebac7f8e8 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Thu, 26 Feb 2015 12:54:15 +0100 Subject: [PATCH] Add support for 'child-src' directive This is required when working with stuff such as PDF.js in the files_pdfviewer application. Opt-in only. Master change only because the stable CSP policies has a failback that allows nearly anything :see_no_evil: --- .../http/contentsecuritypolicy.php | 17 +++++++++++++++++ .../http/ContentSecurityPolicyTest.php | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/public/appframework/http/contentsecuritypolicy.php b/lib/public/appframework/http/contentsecuritypolicy.php index cb9a241d8a..6778d1035b 100644 --- a/lib/public/appframework/http/contentsecuritypolicy.php +++ b/lib/public/appframework/http/contentsecuritypolicy.php @@ -65,6 +65,8 @@ class ContentSecurityPolicy { private $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 @@ -180,6 +182,16 @@ class ContentSecurityPolicy { 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 + */ + public function addAllowedChildSrcDomain($domain) { + $this->allowedChildSrcDomains[] = $domain; + return $this; + } + /** * Get the generated Content-Security-Policy as a string * @return string @@ -236,6 +248,11 @@ class ContentSecurityPolicy { $policy .= ';'; } + if(!empty($this->allowedChildSrcDomains)) { + $policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains); + $policy .= ';'; + } + return rtrim($policy, ';'); } } diff --git a/tests/lib/appframework/http/ContentSecurityPolicyTest.php b/tests/lib/appframework/http/ContentSecurityPolicyTest.php index 739028cb3b..f79c23ae64 100644 --- a/tests/lib/appframework/http/ContentSecurityPolicyTest.php +++ b/tests/lib/appframework/http/ContentSecurityPolicyTest.php @@ -181,7 +181,6 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $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"; @@ -197,8 +196,23 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } + public function testGetAllowedChildSrcDomain() { + $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';child-src child.owncloud.com"; + + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyChildSrcValidMultiple() { + $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';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 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"; + $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;child-src child.owncloud.org"; $this->contentSecurityPolicy->allowInlineStyle(false) ->allowEvalScript(false) @@ -209,6 +223,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { ->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()); }