You are here

class ResponseCspSubscriberTest in Content-Security-Policy 8

@coversDefaultClass \Drupal\csp\EventSubscriber\ResponseCspSubscriber @group csp

Hierarchy

Expanded class hierarchy of ResponseCspSubscriberTest

File

tests/src/Unit/EventSubscriber/ResponseCspSubscriberTest.php, line 22

Namespace

Drupal\Tests\csp\Unit\EventSubscriber
View source
class ResponseCspSubscriberTest extends UnitTestCase {

  /**
   * Mock HTTP Response.
   *
   * @var \Drupal\Core\Render\HtmlResponse|\PHPUnit_Framework_MockObject_MockObject
   */
  protected $response;

  /**
   * Mock Response Event.
   *
   * @var \Symfony\Component\HttpKernel\Event\FilterResponseEvent|\PHPUnit_Framework_MockObject_MockObject
   */
  protected $event;

  /**
   * The Library Policy service.
   *
   * @var \Drupal\csp\LibraryPolicyBuilder|\PHPUnit_Framework_MockObject_MockObject
   */
  private $libraryPolicy;

  /**
   * The Reporting Handler Plugin Manager service.
   *
   * @var \Drupal\csp\ReportingHandlerPluginManager|\PHPUnit_Framework_MockObject_MockObject
   */
  private $reportingHandlerPluginManager;

  /**
   * The Event Dispatcher Service.
   *
   * @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  private $eventDispatcher;

  /**
   * {@inheritdoc}
   */
  public function setUp() : void {
    parent::setUp();
    $this->response = $this
      ->getMockBuilder(HtmlResponse::class)
      ->disableOriginalConstructor()
      ->getMock();
    $this->response->headers = $this
      ->getMockBuilder(ResponseHeaderBag::class)
      ->disableOriginalConstructor()
      ->getMock();
    $responseCacheableMetadata = $this
      ->getMockBuilder(CacheableMetadata::class)
      ->getMock();
    $this->response
      ->method('getCacheableMetadata')
      ->willReturn($responseCacheableMetadata);

    /** @var \Symfony\Component\HttpKernel\Event\FilterResponseEvent|\PHPUnit_Framework_MockObject_MockObject $event */
    $this->event = $this
      ->getMockBuilder(FilterResponseEvent::class)
      ->disableOriginalConstructor()
      ->getMock();
    $this->event
      ->expects($this
      ->any())
      ->method('isMasterRequest')
      ->willReturn(TRUE);
    $this->event
      ->expects($this
      ->any())
      ->method('getResponse')
      ->willReturn($this->response);
    $this->libraryPolicy = $this
      ->getMockBuilder(LibraryPolicyBuilder::class)
      ->disableOriginalConstructor()
      ->getMock();
    $this->reportingHandlerPluginManager = $this
      ->getMockBuilder(ReportingHandlerPluginManager::class)
      ->disableOriginalConstructor()
      ->getMock();
    $this->eventDispatcher = $this
      ->getMockBuilder(EventDispatcher::class)
      ->disableOriginalConstructor()
      ->getMock();
  }

  /**
   * Check that the subscriber listens to the Response event.
   *
   * @covers ::getSubscribedEvents
   */
  public function testSubscribedEvents() {
    $this
      ->assertArrayHasKey(KernelEvents::RESPONSE, ResponseCspSubscriber::getSubscribedEvents());
  }

  /**
   * Check that Policy Alter events are dispatched.
   *
   * @covers ::onKernelResponse
   */
  public function testPolicyAlterEvent() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => FALSE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'style-src' => [
              'base' => 'any',
            ],
          ],
        ],
        'enforce' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'self',
            ],
          ],
        ],
      ],
    ]);
    $this->eventDispatcher
      ->expects($this
      ->exactly(2))
      ->method('dispatch')
      ->with($this
      ->equalTo(CspEvents::POLICY_ALTER), $this
      ->callback(function ($event) {
      $policy = $event
        ->getPolicy();
      return $policy
        ->hasDirective($policy
        ->isReportOnly() ? 'style-src' : 'script-src');
    }))
      ->willReturnCallback(function ($eventName, $event) {
      $policy = $event
        ->getPolicy();
      $policy
        ->setDirective('font-src', [
        Csp::POLICY_SELF,
      ]);
    });
    $this->response->headers
      ->expects($this
      ->exactly(2))
      ->method('set')
      ->withConsecutive([
      $this
        ->equalTo('Content-Security-Policy-Report-Only'),
      $this
        ->equalTo("font-src 'self'; style-src *"),
    ], [
      $this
        ->equalTo('Content-Security-Policy'),
      $this
        ->equalTo("font-src 'self'; script-src 'self'"),
    ]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * An empty or missing directive list should not output a header.
   *
   * @covers ::onKernelResponse
   */
  public function testEmptyDirective() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => FALSE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [],
        ],
        'enforce' => [
          'enable' => TRUE,
        ],
      ],
    ]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->never())
      ->method('set');
    $this->response
      ->getCacheableMetadata()
      ->expects($this
      ->once())
      ->method('addCacheTags')
      ->with([
      'config:csp.settings',
    ]);
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Check the policy with CSS optimization disabled.
   *
   * @covers ::onKernelResponse
   */
  public function testUnoptimizedResponse() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => FALSE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'self',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'self',
            ],
          ],
        ],
        'enforce' => [
          'enable' => FALSE,
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->once())
      ->method('set')
      ->with($this
      ->equalTo('Content-Security-Policy-Report-Only'), $this
      ->equalTo("script-src 'self' 'unsafe-inline'; style-src 'self'"));
    $this->response
      ->getCacheableMetadata()
      ->expects($this
      ->once())
      ->method('addCacheTags')
      ->with([
      'config:csp.settings',
    ]);
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Check the policy with CSS optimization enabled.
   *
   * @covers ::onKernelResponse
   */
  public function testOptimizedResponse() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => TRUE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'self',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'self',
            ],
          ],
        ],
        'enforce' => [
          'enable' => FALSE,
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->once())
      ->method('set')
      ->with($this
      ->equalTo('Content-Security-Policy-Report-Only'), $this
      ->equalTo("script-src 'self' 'unsafe-inline'; style-src 'self'"));
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Check the policy with enforcement enabled.
   *
   * @covers ::onKernelResponse
   */
  public function testEnforcedResponse() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => TRUE,
      ],
      'csp.settings' => [
        'enforce' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'self',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'self',
            ],
          ],
        ],
        'report-only' => [
          'enable' => FALSE,
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->once())
      ->method('set')
      ->with($this
      ->equalTo('Content-Security-Policy'), $this
      ->equalTo("script-src 'self' 'unsafe-inline'; style-src 'self'"));
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Check the generated headers with both policies enabled.
   *
   * @covers ::onKernelResponse
   */
  public function testBothPolicies() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => TRUE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'any',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'any',
              'flags' => [
                'unsafe-inline',
              ],
            ],
          ],
        ],
        'enforce' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'self',
            ],
            'style-src' => [
              'base' => 'self',
            ],
          ],
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->exactly(2))
      ->method('set')
      ->withConsecutive([
      $this
        ->equalTo('Content-Security-Policy-Report-Only'),
      $this
        ->equalTo("script-src * 'unsafe-inline'; style-src * 'unsafe-inline'"),
    ], [
      $this
        ->equalTo('Content-Security-Policy'),
      $this
        ->equalTo("script-src 'self'; style-src 'self'"),
    ]);
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Test that library sources are included.
   *
   * @covers ::onKernelResponse
   */
  public function testWithLibraryDirective() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => TRUE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'any',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'self',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src-elem' => [
              'base' => 'self',
            ],
          ],
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([
      'style-src' => [
        'example.com',
      ],
      'style-src-elem' => [
        'example.com',
      ],
    ]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->once())
      ->method('set')
      ->with($this
      ->equalTo('Content-Security-Policy-Report-Only'), $this
      ->equalTo("script-src * 'unsafe-inline'; style-src 'self' 'unsafe-inline' example.com; style-src-elem 'self' example.com"));
    $subscriber
      ->onKernelResponse($this->event);
  }

  /**
   * Test that library sources do not override a disabled directive.
   *
   * @covers ::onKernelResponse
   */
  public function testDisabledLibraryDirective() {

    /** @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject $configFactory */
    $configFactory = $this
      ->getConfigFactoryStub([
      'system.performance' => [
        'css.preprocess' => TRUE,
      ],
      'csp.settings' => [
        'report-only' => [
          'enable' => TRUE,
          'directives' => [
            'script-src' => [
              'base' => 'any',
              'flags' => [
                'unsafe-inline',
              ],
            ],
            'style-src' => [
              'base' => 'self',
              'flags' => [
                'unsafe-inline',
              ],
            ],
          ],
        ],
      ],
    ]);
    $this->libraryPolicy
      ->expects($this
      ->any())
      ->method('getSources')
      ->willReturn([
      'style-src' => [
        'example.com',
      ],
      'style-src-elem' => [
        'example.com',
      ],
    ]);
    $subscriber = new ResponseCspSubscriber($configFactory, $this->libraryPolicy, $this->reportingHandlerPluginManager, $this->eventDispatcher);
    $this->response->headers
      ->expects($this
      ->once())
      ->method('set')
      ->with($this
      ->equalTo('Content-Security-Policy-Report-Only'), $this
      ->equalTo("script-src * 'unsafe-inline'; style-src 'self' 'unsafe-inline' example.com"));
    $subscriber
      ->onKernelResponse($this->event);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
ResponseCspSubscriberTest::$event protected property Mock Response Event.
ResponseCspSubscriberTest::$eventDispatcher private property The Event Dispatcher Service.
ResponseCspSubscriberTest::$libraryPolicy private property The Library Policy service.
ResponseCspSubscriberTest::$reportingHandlerPluginManager private property The Reporting Handler Plugin Manager service.
ResponseCspSubscriberTest::$response protected property Mock HTTP Response.
ResponseCspSubscriberTest::setUp public function Overrides UnitTestCase::setUp
ResponseCspSubscriberTest::testBothPolicies public function Check the generated headers with both policies enabled.
ResponseCspSubscriberTest::testDisabledLibraryDirective public function Test that library sources do not override a disabled directive.
ResponseCspSubscriberTest::testEmptyDirective public function An empty or missing directive list should not output a header.
ResponseCspSubscriberTest::testEnforcedResponse public function Check the policy with enforcement enabled.
ResponseCspSubscriberTest::testOptimizedResponse public function Check the policy with CSS optimization enabled.
ResponseCspSubscriberTest::testPolicyAlterEvent public function Check that Policy Alter events are dispatched.
ResponseCspSubscriberTest::testSubscribedEvents public function Check that the subscriber listens to the Response event.
ResponseCspSubscriberTest::testUnoptimizedResponse public function Check the policy with CSS optimization disabled.
ResponseCspSubscriberTest::testWithLibraryDirective public function Test that library sources are included.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getBlockMockWithMachineName Deprecated protected function Mocks a block with a block plugin. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.