diff --git a/_config/extensions.yml b/_config/extensions.yml index 3e09117..8e325b4 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -1,3 +1,6 @@ PageController: extensions: - - Firesphere\CSPHeaders\Extensions\ControllerCSPExtension \ No newline at end of file + - Firesphere\CSPHeaders\Extensions\ControllerCSPExtension +SilverStripe\SiteConfig\SiteConfig: + extensions: + - Firesphere\CSPHeaders\Extensions\SiteConfigExtension \ No newline at end of file diff --git a/src/Extensions/ControllerCSPExtension.php b/src/Extensions/ControllerCSPExtension.php index ec3f080..3bd9a0c 100644 --- a/src/Extensions/ControllerCSPExtension.php +++ b/src/Extensions/ControllerCSPExtension.php @@ -3,6 +3,7 @@ namespace Firesphere\CSPHeaders\Extensions; +use Firesphere\CSPHeaders\Models\CSPDomain; use Firesphere\CSPHeaders\View\CSPBackend; use Phpcsp\Security\ContentSecurityPolicyHeaderBuilder; use Phpcsp\Security\InvalidValueException; @@ -11,6 +12,7 @@ use SilverStripe\Control\HTTPRequest; use SilverStripe\Core\Extension; use SilverStripe\Core\Injector\Injector; +use SilverStripe\SiteConfig\SiteConfig; /** * Class \Firesphere\CSPHeaders\Extensions\ControllerCSPExtension @@ -87,6 +89,7 @@ public function onAfterInit() if (Director::isLive() || $this->checkCookie($this->owner->getRequest())) { $policy = $this->setDefaultPolicies(); $this->setConfigPolicies($policy); + $this->setSiteConfigPolicies($policy); $this->setReportPolicy(); $headers = $policy->getHeaders(CSPBackend::config()->get('legacy_headers')); @@ -99,6 +102,7 @@ public function onAfterInit() /** * @param HTTPRequest $request * @return bool + * default-src 'self' http://192.168.33.5/ piwik.casa-laguna.net; font-src 'self' http://192.168.33.5/ netdna.bootstrapcdn.com fonts.gstatic.com; form-action 'self' http://192.168.33.5/; frame-src 'self' http://192.168.33.5/ *.vimeocdn.com player.vimeo.com www.youtube.com www.youtube-nocookie.com; img-src 'self' http://192.168.33.5/ secure.gravatar.com a.slack-edge.com avatars.slack-edge.com emoji.slack-edge.com *.imgur.com imgur.com *.wp.com piwik.casa-laguna.net data: i.ytimg.com packagist.org www.silverstripe.org www.silverstripe.com; media-src 'self' http://192.168.33.5/ *.vimeocdn.com player.vimeo.com www.youtube.com www.youtube-nocookie.com; script-src 'self' http://192.168.33.5/ code.jquery.com piwik.casa-laguna.net 'sha256-eMFqux9gzJQ++3HtmRPlbbsshtbkqI6ieVhYTWbaV1k='; style-src 'self' http://192.168.33.5/ 'unsafe-inline'; base-uri 'self' http://192.168.33.5/ https:; reflected-xss block; report-uri https://casalaguna.report-uri.com/r/d/csp/wizard; */ protected function checkCookie($request) { @@ -188,6 +192,22 @@ protected function setConfigPolicies($policy) } } + /** + * Add SiteConfig set domain policies + * + * @param ContentSecurityPolicyHeaderBuilder $policy + */ + protected function setSiteConfigPolicies($policy) + { + /** @var DataList|CSPDomain[] $domains */ + $domains = SiteConfig::current_site_config()->CSPDomains(); + $map = $this->allowedDirectivesMap; + + foreach ($domains as $domain) { + $policy->addSourceExpression($map[$domain->Source], $domain->Domain); + } + } + protected function setReportPolicy() { $config = CSPBackend::config()->get('report_to'); diff --git a/src/Extensions/SiteConfigExtension.php b/src/Extensions/SiteConfigExtension.php new file mode 100644 index 0000000..0ce9417 --- /dev/null +++ b/src/Extensions/SiteConfigExtension.php @@ -0,0 +1,39 @@ + CSPDomain::class + ]; + + public function updateCMSFields(FieldList $fields) + { + $cspConfig = GridFieldConfig_RecordEditor::create(); + + $fields->addFieldToTab( + 'Root.CSP', + GridField::create( + 'CSPDomains', + _t(self::class . '.CSPDOMAINS', 'CSP Domains'), + CSPDomain::get(), + $cspConfig + ) + ); + } +} diff --git a/src/Models/CSPDomain.php b/src/Models/CSPDomain.php new file mode 100644 index 0000000..a5282ed --- /dev/null +++ b/src/Models/CSPDomain.php @@ -0,0 +1,145 @@ + 'Varchar(255)', + 'Source' => 'Enum("default,script,style,img,media,font,form,Frame")' + ]; + + private static $has_one = [ + 'SiteConfig' => SiteConfig::class + ]; + + private static $summary_fields = [ + 'Domain', + 'Source' + ]; + + public function getTitle() + { + return $this->Domain; + } + + public function getCMSFields() + { + $fields = parent::getCMSFields(); + $fields->removeByName(['SiteConfigID']); + + return $fields; + } + + public function onBeforeWrite() + { + $this->SiteConfigID = SiteConfig::current_site_config()->ID; + parent::onBeforeWrite(); + } + + public function canCreate($member = null, $context = array()) + { + $canCreate = parent::canCreate($member, $context); + + if ($canCreate) { + return Permission::check('CREATE_CSPDomain', $member); + } + + return $canCreate; + } + + public function canEdit($member = null) + { + $canEdit = parent::canEdit($member, $context); + + if ($canEdit) { + return Permission::check('EDIT_CSPDomain', $member); + } + + return $canEdit; + } + + public function canView($member = null) + { + $canView = parent::canView($member, $context); + + if ($canView) { + return Permission::check('VIEW_CSPDomain', $member); + } + + return $canView; + } + + public function canDelete($member = null) + { + $canDelete = parent::canDelete($member, $context); + + if ($canDelete) { + return Permission::check('DELETE_CSPDOMAIN', $member); + } + + return $canDelete; + } + + /** + * Return a map of permission codes to add to the dropdown shown in the Security section of the CMS. + * array( + * 'VIEW_SITE' => 'View the site', + * ); + */ + public function providePermissions() + { + return [ + 'CREATE_CSPDOMAIN' => [ + 'name' => _t(self::class . '.PERMISSION_CREATE_DESCRIPTION', 'Create CSP Domains'), + 'category' => _t('Permissions.CSPDOMAINS_CATEGORY', 'CSP Permissions'), + 'help' => _t( + self::class . '.PERMISSION_CREATE_HELP', + 'Permission required to create new CSP Domains.' + ) + ], + 'EDIT_CSPDOMAIN' => [ + 'name' => _t(self::class . '.PERMISSION_EDIT_DESCRIPTION', 'Edit CSP Domains'), + 'category' => _t('Permissions.CSPDOMAINS_CATEGORY', 'CSP Permissions'), + 'help' => _t( + self::class . '.PERMISSION_EDIT_HELP', + 'Permission required to edit existing CSP Domains.' + ) + ], + 'DELETE_CSPDOMAIN' => [ + 'name' => _t(self::class . '.PERMISSION_DELETE_DESCRIPTION', 'Delete CSP Domains'), + 'category' => _t('Permissions.CSPDOMAINS_CATEGORY', 'CSP Permissions'), + 'help' => _t( + self::class . '.PERMISSION_DELETE_HELP', + 'Permission required to delete existing CSP Domains.' + ) + ], + 'VIEW_CSPDOMAIN' => [ + 'name' => _t(self::class . '.PERMISSION_VIEW_DESCRIPTION', 'View CSP Domains'), + 'category' => _t('Permissions.CSPDOMAINS_CATEGORY', 'CSP Permissions'), + 'help' => _t( + self::class . '.PERMISSION_VIEW_HELP', + 'Permission required to view existing CSP Domains.' + ) + ], + ]; + } +} diff --git a/src/View/CSPRequirements.php b/src/View/CSPRequirements.php index ab03c33..7bb10ce 100644 --- a/src/View/CSPRequirements.php +++ b/src/View/CSPRequirements.php @@ -17,10 +17,7 @@ class CSPRequirements extends Requirements implements Flushable public static function backend() { if (!self::$backend) { - self::$backend = parent::backend(); - if (!self::$backend) { - self::$backend = CSPBackend::create(); - } + self::$backend = CSPBackend::create(); } return self::$backend;