Allow to set a strict-dynamic CSP through the API

Signed-off-by: Julius Härtl <jus@bitgrid.net>
pull/31503/head
Julius Härtl 4 years ago
parent 0825c3ea34
commit bd03dd37be
No known key found for this signature in database
GPG Key ID: 4C614C6ED2CDE6DF
  1. 7
      lib/private/Security/CSP/ContentSecurityPolicy.php
  2. 2
      lib/public/AppFramework/Http/ContentSecurityPolicy.php
  3. 15
      lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
  4. 17
      tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php
  5. 2
      tests/lib/Security/CSP/ContentSecurityPolicyManagerTest.php

@ -244,4 +244,11 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy
public function setReportTo(array $reportTo) {
$this->reportTo = $reportTo;
}
/**
* @param boolean $strictDynamicAllowed
*/
public function setStrictDynamicAllowed(bool $strictDynamicAllowed) {
$this->strictDynamicAllowed = $strictDynamicAllowed;
}
}

@ -44,6 +44,8 @@ class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
protected $inlineScriptAllowed = false;
/** @var bool Whether eval in JS scripts is allowed */
protected $evalScriptAllowed = false;
/** @var bool Whether strict-dynamic should be set */
protected $strictDynamicAllowed = null;
/** @var array Domains from which scripts can get loaded */
protected $allowedScriptDomains = [
'\'self\'',

@ -41,6 +41,8 @@ class EmptyContentSecurityPolicy {
protected $inlineScriptAllowed = null;
/** @var string Whether JS nonces should be used */
protected $useJsNonce = null;
/** @var bool Whether strict-dynamic should be used */
protected $strictDynamicAllowed = null;
/**
* @var bool Whether eval in JS scripts is allowed
* TODO: Disallow per default
@ -93,6 +95,16 @@ class EmptyContentSecurityPolicy {
return $this;
}
/**
* @param bool $state
* @return EmptyContentSecurityPolicy
* @since 24.0.0
*/
public function useStrictDynamic(bool $state = false): self {
$this->strictDynamicAllowed = $state;
return $this;
}
/**
* Use the according JS nonce
* This method is only for CSPMiddleware, custom values are ignored in mergePolicies of ContentSecurityPolicyManager
@ -438,6 +450,9 @@ class EmptyContentSecurityPolicy {
if (!empty($this->allowedScriptDomains) || $this->inlineScriptAllowed || $this->evalScriptAllowed) {
$policy .= 'script-src ';
if (is_string($this->useJsNonce)) {
if ($this->strictDynamicAllowed) {
$policy .= '\'strict-dynamic\' ';
}
$policy .= '\'nonce-'.base64_encode($this->useJsNonce).'\'';
$allowedScriptDomains = array_flip($this->allowedScriptDomains);
unset($allowedScriptDomains['\'self\'']);

@ -472,4 +472,21 @@ class ContentSecurityPolicyTest extends \Test\TestCase {
$this->contentSecurityPolicy->allowEvalScript(true);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyNonce() {
$nonce = 'my-nonce';
$expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
$this->contentSecurityPolicy->useJsNonce($nonce);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
public function testGetPolicyNonceStrictDynamic() {
$nonce = 'my-nonce';
$expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
$this->contentSecurityPolicy->useJsNonce($nonce);
$this->contentSecurityPolicy->useStrictDynamic(true);
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
}

@ -86,6 +86,7 @@ class ContentSecurityPolicyManagerTest extends TestCase {
$policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
$policy->addAllowedFontDomain('mydomain.com');
$policy->addAllowedImageDomain('anotherdomain.de');
$policy->useStrictDynamic(true);
$e->addPolicy($policy);
});
@ -117,6 +118,7 @@ class ContentSecurityPolicyManagerTest extends TestCase {
$expected->addAllowedImageDomain('example.org');
$expected->addAllowedChildSrcDomain('childdomain');
$expected->addAllowedFormActionDomain('thirdDomain');
$expected->useStrictDynamic(true);
$expectedStringPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: anotherdomain.de example.org;font-src 'self' data: mydomain.com example.com anotherFontDomain;connect-src 'self';media-src 'self';child-src childdomain;frame-ancestors 'self';form-action 'self' thirdDomain";
$this->assertEquals($expected, $this->contentSecurityPolicyManager->getDefaultPolicy());

Loading…
Cancel
Save