Loading ga.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,8 @@ services: arguments: ['@config.factory', '@ga.command_registry', '@current_user', '@entity_type.manager'] tags: - { name: event_subscriber } ga.csp_subscriber: class: Drupal\ga\EventSubscriber\CspSubscriber tags: - { name: event_subscriber } src/EventSubscriber/CspSubscriber.php 0 → 100644 +50 −0 Original line number Diff line number Diff line <?php namespace Drupal\ga\EventSubscriber; use Drupal\csp\CspEvents; use Drupal\csp\Event\PolicyAlterEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Alter CSP policy for Google Analytics. */ class CspSubscriber implements EventSubscriberInterface { const TRACKING_DOMAIN = 'https://www.google-analytics.com'; /** * {@inheritdoc} */ public static function getSubscribedEvents() { $events[CspEvents::POLICY_ALTER] = ['onCspPolicyAlter']; return $events; } /** * Alter CSP policy for tracking requests. * * @param \Drupal\csp\Event\PolicyAlterEvent $alterEvent * The Policy Alter event. */ public function onCspPolicyAlter(PolicyAlterEvent $alterEvent) { $policy = $alterEvent->getPolicy(); if ($policy->hasDirective('img-src')) { $policy->appendDirective('img-src', [self::TRACKING_DOMAIN]); } elseif ($policy->hasDirective('default-src')) { $imgDirective = array_merge($policy->getDirective('default-src'), [self::TRACKING_DOMAIN]); $policy->setDirective('img-src', $imgDirective); } if ($policy->hasDirective('connect-src')) { $policy->appendDirective('connect-src', [self::TRACKING_DOMAIN]); } elseif ($policy->hasDirective('default-src')) { $connectDirective = array_merge($policy->getDirective('default-src'), [self::TRACKING_DOMAIN]); $policy->setDirective('connect-src', $connectDirective); } } } tests/src/Unit/EventSubscriber/CspSubscriberTest.php 0 → 100644 +142 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\ga\Unit\EventSubscriber; use Drupal\Core\Render\HtmlResponse; use Drupal\csp\Csp; use Drupal\csp\CspEvents; use Drupal\csp\Event\PolicyAlterEvent; use Drupal\csp\EventSubscriber\CoreCspSubscriber; use Drupal\ga\EventSubscriber\CspSubscriber; use Drupal\Tests\UnitTestCase; /** * @coversDefaultClass \Drupal\ga\EventSubscriber\CspSubscriber */ class CspSubscriberTest extends UnitTestCase { /** * The response object. * * @var \Drupal\Core\Render\HtmlResponse|\PHPUnit\Framework\MockObject\MockObject */ private $response; /** * {@inheritdoc} */ public function setUp() { parent::setUp(); $this->response = $this->getMockBuilder(HtmlResponse::class) ->disableOriginalConstructor() ->getMock(); } /** * Check that the subscriber listens to the Policy Alter event. * * @covers ::getSubscribedEvents */ public function testSubscribedEvents() { $this->assertArrayHasKey(CspEvents::POLICY_ALTER, CoreCspSubscriber::getSubscribedEvents()); } /** * Shouldn't alter the policy if no directives are enabled. * * @covers ::onCspPolicyAlter */ public function testNoDirectives() { $policy = new Csp(); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertFalse($alterEvent->getPolicy()->hasDirective('default-src')); $this->assertFalse($alterEvent->getPolicy()->hasDirective('img-src')); $this->assertFalse($alterEvent->getPolicy()->hasDirective('connect-src')); } /** * Test that enabled required directives are modified. * * @covers ::onCspPolicyAlter */ public function testDirectives() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_ANY]); $policy->setDirective('img-src', [Csp::POLICY_SELF]); $policy->setDirective('connect-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_ANY], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('img-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('connect-src') ); } /** * Test img-src fallback if default-src enabled. * * @covers ::onCspPolicyAlter */ public function testImgFallback() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_SELF], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('img-src') ); } /** * Test connect-src fallback if default-src enabled. * * @covers ::onCspPolicyAlter */ public function testConnectFallback() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_SELF], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('connect-src') ); } } Loading
ga.services.yml +5 −0 Original line number Diff line number Diff line Loading @@ -6,3 +6,8 @@ services: arguments: ['@config.factory', '@ga.command_registry', '@current_user', '@entity_type.manager'] tags: - { name: event_subscriber } ga.csp_subscriber: class: Drupal\ga\EventSubscriber\CspSubscriber tags: - { name: event_subscriber }
src/EventSubscriber/CspSubscriber.php 0 → 100644 +50 −0 Original line number Diff line number Diff line <?php namespace Drupal\ga\EventSubscriber; use Drupal\csp\CspEvents; use Drupal\csp\Event\PolicyAlterEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Alter CSP policy for Google Analytics. */ class CspSubscriber implements EventSubscriberInterface { const TRACKING_DOMAIN = 'https://www.google-analytics.com'; /** * {@inheritdoc} */ public static function getSubscribedEvents() { $events[CspEvents::POLICY_ALTER] = ['onCspPolicyAlter']; return $events; } /** * Alter CSP policy for tracking requests. * * @param \Drupal\csp\Event\PolicyAlterEvent $alterEvent * The Policy Alter event. */ public function onCspPolicyAlter(PolicyAlterEvent $alterEvent) { $policy = $alterEvent->getPolicy(); if ($policy->hasDirective('img-src')) { $policy->appendDirective('img-src', [self::TRACKING_DOMAIN]); } elseif ($policy->hasDirective('default-src')) { $imgDirective = array_merge($policy->getDirective('default-src'), [self::TRACKING_DOMAIN]); $policy->setDirective('img-src', $imgDirective); } if ($policy->hasDirective('connect-src')) { $policy->appendDirective('connect-src', [self::TRACKING_DOMAIN]); } elseif ($policy->hasDirective('default-src')) { $connectDirective = array_merge($policy->getDirective('default-src'), [self::TRACKING_DOMAIN]); $policy->setDirective('connect-src', $connectDirective); } } }
tests/src/Unit/EventSubscriber/CspSubscriberTest.php 0 → 100644 +142 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\ga\Unit\EventSubscriber; use Drupal\Core\Render\HtmlResponse; use Drupal\csp\Csp; use Drupal\csp\CspEvents; use Drupal\csp\Event\PolicyAlterEvent; use Drupal\csp\EventSubscriber\CoreCspSubscriber; use Drupal\ga\EventSubscriber\CspSubscriber; use Drupal\Tests\UnitTestCase; /** * @coversDefaultClass \Drupal\ga\EventSubscriber\CspSubscriber */ class CspSubscriberTest extends UnitTestCase { /** * The response object. * * @var \Drupal\Core\Render\HtmlResponse|\PHPUnit\Framework\MockObject\MockObject */ private $response; /** * {@inheritdoc} */ public function setUp() { parent::setUp(); $this->response = $this->getMockBuilder(HtmlResponse::class) ->disableOriginalConstructor() ->getMock(); } /** * Check that the subscriber listens to the Policy Alter event. * * @covers ::getSubscribedEvents */ public function testSubscribedEvents() { $this->assertArrayHasKey(CspEvents::POLICY_ALTER, CoreCspSubscriber::getSubscribedEvents()); } /** * Shouldn't alter the policy if no directives are enabled. * * @covers ::onCspPolicyAlter */ public function testNoDirectives() { $policy = new Csp(); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertFalse($alterEvent->getPolicy()->hasDirective('default-src')); $this->assertFalse($alterEvent->getPolicy()->hasDirective('img-src')); $this->assertFalse($alterEvent->getPolicy()->hasDirective('connect-src')); } /** * Test that enabled required directives are modified. * * @covers ::onCspPolicyAlter */ public function testDirectives() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_ANY]); $policy->setDirective('img-src', [Csp::POLICY_SELF]); $policy->setDirective('connect-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_ANY], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('img-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('connect-src') ); } /** * Test img-src fallback if default-src enabled. * * @covers ::onCspPolicyAlter */ public function testImgFallback() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_SELF], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('img-src') ); } /** * Test connect-src fallback if default-src enabled. * * @covers ::onCspPolicyAlter */ public function testConnectFallback() { $policy = new Csp(); $policy->setDirective('default-src', [Csp::POLICY_SELF]); $alterEvent = new PolicyAlterEvent($policy, $this->response); $subscriber = new CspSubscriber(); $subscriber->onCspPolicyAlter($alterEvent); $this->assertArrayEquals( [Csp::POLICY_SELF], $alterEvent->getPolicy()->getDirective('default-src') ); $this->assertArrayEquals( [Csp::POLICY_SELF, CspSubscriber::TRACKING_DOMAIN], $alterEvent->getPolicy()->getDirective('connect-src') ); } }