From ecf347bd1aaaeb2cd11b8ffbc60da099c68f1d83 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 19 Jun 2017 13:55:46 +0200 Subject: [PATCH 1/2] Add CSP frame-ancestors support Didn't set the @since annotation yet. Signed-off-by: Thomas Citharel --- .../Security/CSP/ContentSecurityPolicy.php | 14 +++++++ .../Http/ContentSecurityPolicy.php | 3 ++ .../Http/EmptyContentSecurityPolicy.php | 31 ++++++++++++++ .../Http/ContentSecurityPolicyTest.php | 41 +++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/lib/private/Security/CSP/ContentSecurityPolicy.php b/lib/private/Security/CSP/ContentSecurityPolicy.php index 4731460949..a689250552 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicy.php +++ b/lib/private/Security/CSP/ContentSecurityPolicy.php @@ -197,4 +197,18 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy $this->allowedChildSrcDomains = $allowedChildSrcDomains; } + /** + * @return array + */ + public function getAllowedFrameAncestors() { + return $this->allowedFrameAncestors; + } + + /** + * @param array $allowedFrameAncestors + */ + public function setAllowedFrameAncestors($allowedFrameAncestors) { + $this->allowedFrameAncestors = $allowedFrameAncestors; + } + } diff --git a/lib/public/AppFramework/Http/ContentSecurityPolicy.php b/lib/public/AppFramework/Http/ContentSecurityPolicy.php index 17844497f9..0a792fa263 100644 --- a/lib/public/AppFramework/Http/ContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/ContentSecurityPolicy.php @@ -84,4 +84,7 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy { ]; /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = []; + + /** @var array Domains which can embeed this Nextcloud instance */ + protected $allowedFrameAncestors = []; } diff --git a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php index 64d4eb6e5d..d0536259f7 100644 --- a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php @@ -68,6 +68,8 @@ class EmptyContentSecurityPolicy { protected $allowedFontDomains = null; /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = null; + /** @var array Domains which can embeed this Nextcloud instance */ + protected $allowedFrameAncestors = null; /** * Whether inline JavaScript snippets are allowed or forbidden @@ -326,6 +328,30 @@ class EmptyContentSecurityPolicy { return $this; } + /** + * Domains which can embeed an iFrame of the Nextcloud instance + * + * @param string $domain + * @return $this + * @since 12.x + */ + public function addAllowedFrameAncestorDomain($domain) { + $this->allowedFrameAncestors[] = $domain; + return $this; + } + + /** + * Domains which can embeed an iFrame of the Nextcloud instance + * + * @param string $domain + * @return $this + * @since 12.x + */ + public function disallowFrameAncestorDomain($domain) { + $this->allowedFrameAncestors = array_diff($this->allowedFrameAncestors, [$domain]); + return $this; + } + /** * Get the generated Content-Security-Policy as a string * @return string @@ -405,6 +431,11 @@ class EmptyContentSecurityPolicy { $policy .= ';'; } + if(!empty($this->allowedFrameAncestors)) { + $policy .= 'frame-ancestors ' . implode(' ', $this->allowedFrameAncestors); + $policy .= ';'; + } + return rtrim($policy, ';'); } } diff --git a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php index 503148d633..90dcf99d00 100644 --- a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php +++ b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php @@ -426,4 +426,45 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } + + + + public function testGetAllowedFrameAncestorDomain() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-ancestors sub.nextcloud.com"; + + $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyFrameAncestorValidMultiple() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-ancestors sub.nextcloud.com foo.nextcloud.com"; + + $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('foo.nextcloud.com'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyDisallowFrameAncestorDomain() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; + + $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameAncestorDomain('www.nextcloud.com'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyDisallowFrameAncestorDomainMultiple() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self';frame-ancestors www.nextcloud.com"; + + $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameAncestorDomain('www.nextcloud.org'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyDisallowFrameAncestorDomainMultipleStakes() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self';connect-src 'self';media-src 'self'"; + + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } } From eb51c46549d1fe01fe8fdbefd2303bf597c88b54 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 27 Jul 2017 17:25:09 +0200 Subject: [PATCH 2/2] fix typo and set @since properly Signed-off-by: Thomas Citharel --- lib/public/AppFramework/Http/ContentSecurityPolicy.php | 2 +- .../AppFramework/Http/EmptyContentSecurityPolicy.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/public/AppFramework/Http/ContentSecurityPolicy.php b/lib/public/AppFramework/Http/ContentSecurityPolicy.php index 0a792fa263..b17dc070fe 100644 --- a/lib/public/AppFramework/Http/ContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/ContentSecurityPolicy.php @@ -85,6 +85,6 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy { /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = []; - /** @var array Domains which can embeed this Nextcloud instance */ + /** @var array Domains which can embed this Nextcloud instance */ protected $allowedFrameAncestors = []; } diff --git a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php index d0536259f7..5902d4ddbe 100644 --- a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php +++ b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php @@ -68,7 +68,7 @@ class EmptyContentSecurityPolicy { protected $allowedFontDomains = null; /** @var array Domains from which web-workers and nested browsing content can load elements */ protected $allowedChildSrcDomains = null; - /** @var array Domains which can embeed this Nextcloud instance */ + /** @var array Domains which can embed this Nextcloud instance */ protected $allowedFrameAncestors = null; /** @@ -329,11 +329,11 @@ class EmptyContentSecurityPolicy { } /** - * Domains which can embeed an iFrame of the Nextcloud instance + * Domains which can embed an iFrame of the Nextcloud instance * * @param string $domain * @return $this - * @since 12.x + * @since 13.0.0 */ public function addAllowedFrameAncestorDomain($domain) { $this->allowedFrameAncestors[] = $domain; @@ -341,11 +341,11 @@ class EmptyContentSecurityPolicy { } /** - * Domains which can embeed an iFrame of the Nextcloud instance + * Domains which can embed an iFrame of the Nextcloud instance * * @param string $domain * @return $this - * @since 12.x + * @since 13.0.0 */ public function disallowFrameAncestorDomain($domain) { $this->allowedFrameAncestors = array_diff($this->allowedFrameAncestors, [$domain]);