View source
<?php
namespace Drupal\Tests\seckit\Functional;
use Drupal\seckit\SeckitInterface;
use Drupal\Tests\BrowserTestBase;
use Psr\Http\Message\RequestInterface;
class SecKitTestCaseTest extends BrowserTestBase {
private $admin;
private $reportPath;
public static $modules = [
'seckit',
'filter',
];
protected $defaultTheme = 'stark';
protected $originHeader = FALSE;
public function setUp() {
parent::setUp();
$this->admin = $this
->drupalCreateUser([
'administer seckit',
]);
$this
->drupalLogin($this->admin);
$route_provider = \Drupal::service('router.route_provider');
$route = $route_provider
->getRouteByName('seckit.report');
$path = $route
->getPath();
$this->reportPath = ltrim($path, '/');
$client = $this
->getHttpClient();
$handler_stack = $client
->getConfig('handler');
$handler_stack
->push($this
->secKitRequestHeader());
}
public function testDisabledCsp() {
$form['seckit_xss[csp][checkbox]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', NULL);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', NULL);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', NULL);
}
public function testCspHasAllDirectives() {
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => TRUE,
'seckit_xss[csp][vendor-prefix][webkit]' => TRUE,
'seckit_xss[csp][default-src]' => '*',
'seckit_xss[csp][script-src]' => '*',
'seckit_xss[csp][object-src]' => '*',
'seckit_xss[csp][style-src]' => '*',
'seckit_xss[csp][img-src]' => '*',
'seckit_xss[csp][media-src]' => '*',
'seckit_xss[csp][frame-src]' => '*',
'seckit_xss[csp][frame-ancestors]' => '*',
'seckit_xss[csp][child-src]' => '*',
'seckit_xss[csp][font-src]' => '*',
'seckit_xss[csp][connect-src]' => '*',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][upgrade-req]' => TRUE,
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'default-src *; script-src *; object-src *; style-src *; img-src *; media-src *; frame-src *; frame-ancestors *; child-src *; font-src *; connect-src *; report-uri ' . base_path() . $this->reportPath . '; upgrade-insecure-requests';
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', $expected);
}
public function testCspWithoutVendorPrefixes() {
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => FALSE,
'seckit_xss[csp][vendor-prefix][webkit]' => FALSE,
'seckit_xss[csp][default-src]' => '*',
'seckit_xss[csp][script-src]' => '*',
'seckit_xss[csp][object-src]' => '*',
'seckit_xss[csp][style-src]' => '*',
'seckit_xss[csp][img-src]' => '*',
'seckit_xss[csp][media-src]' => '*',
'seckit_xss[csp][frame-src]' => '*',
'seckit_xss[csp][frame-ancestors]' => '*',
'seckit_xss[csp][child-src]' => '*',
'seckit_xss[csp][font-src]' => '*',
'seckit_xss[csp][connect-src]' => '*',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][upgrade-req]' => TRUE,
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'default-src *; script-src *; object-src *; style-src *; img-src *; media-src *; frame-src *; frame-ancestors *; child-src *; font-src *; connect-src *; report-uri ' . base_path() . $this->reportPath . '; upgrade-insecure-requests';
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', NULL);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', NULL);
}
public function testCspWithCspVendorPrefix() {
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => TRUE,
'seckit_xss[csp][vendor-prefix][webkit]' => FALSE,
'seckit_xss[csp][default-src]' => '*',
'seckit_xss[csp][script-src]' => '*',
'seckit_xss[csp][object-src]' => '*',
'seckit_xss[csp][style-src]' => '*',
'seckit_xss[csp][img-src]' => '*',
'seckit_xss[csp][media-src]' => '*',
'seckit_xss[csp][frame-src]' => '*',
'seckit_xss[csp][frame-ancestors]' => '*',
'seckit_xss[csp][child-src]' => '*',
'seckit_xss[csp][font-src]' => '*',
'seckit_xss[csp][connect-src]' => '*',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][upgrade-req]' => TRUE,
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'default-src *; script-src *; object-src *; style-src *; img-src *; media-src *; frame-src *; frame-ancestors *; child-src *; font-src *; connect-src *; report-uri ' . base_path() . $this->reportPath . '; upgrade-insecure-requests';
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', NULL);
}
public function testCspWithWebkitCspVendorPrefix() {
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => FALSE,
'seckit_xss[csp][vendor-prefix][webkit]' => TRUE,
'seckit_xss[csp][default-src]' => '*',
'seckit_xss[csp][script-src]' => '*',
'seckit_xss[csp][object-src]' => '*',
'seckit_xss[csp][style-src]' => '*',
'seckit_xss[csp][img-src]' => '*',
'seckit_xss[csp][media-src]' => '*',
'seckit_xss[csp][frame-src]' => '*',
'seckit_xss[csp][frame-ancestors]' => '*',
'seckit_xss[csp][child-src]' => '*',
'seckit_xss[csp][font-src]' => '*',
'seckit_xss[csp][connect-src]' => '*',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][upgrade-req]' => TRUE,
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'default-src *; script-src *; object-src *; style-src *; img-src *; media-src *; frame-src *; frame-ancestors *; child-src *; font-src *; connect-src *; report-uri ' . base_path() . $this->reportPath . '; upgrade-insecure-requests';
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', NULL);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', $expected);
}
public function testCspPolicyUriDirectiveOnly() {
$this
->markTestSkipped('Test/code needs to be fixed.');
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => TRUE,
'seckit_xss[csp][vendor-prefix][webkit]' => TRUE,
'seckit_xss[csp][default-src]' => '*',
'seckit_xss[csp][script-src]' => '*',
'seckit_xss[csp][object-src]' => '*',
'seckit_xss[csp][style-src]' => '*',
'seckit_xss[csp][img-src]' => '*',
'seckit_xss[csp][media-src]' => '*',
'seckit_xss[csp][frame-src]' => '*',
'seckit_xss[csp][child-src]' => '*',
'seckit_xss[csp][font-src]' => '*',
'seckit_xss[csp][connect-src]' => '*',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][policy-uri]' => 'http://mysite.com/csp.xml',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'policy-uri http://mysite.com/csp.xml';
$this
->assertEqual($expected, $this
->drupalGetHeader('Content-Security-Policy'), t('Content-Security-Policy has only policy-uri.'));
$this
->assertEqual($expected, $this
->drupalGetHeader('X-Content-Security-Policy'), t('X-Content-Security-Policy has only policy-uri.'));
$this
->assertEqual($expected, $this
->drupalGetHeader('X-WebKit-CSP'), t('X-WebKit-CSP has only policy-uri.'));
}
public function testCspAllDirectivesEmpty() {
$form = [
'seckit_xss[csp][checkbox]' => TRUE,
'seckit_xss[csp][vendor-prefix][x]' => TRUE,
'seckit_xss[csp][vendor-prefix][webkit]' => TRUE,
'seckit_xss[csp][default-src]' => 'self',
'seckit_xss[csp][script-src]' => '',
'seckit_xss[csp][object-src]' => '',
'seckit_xss[csp][img-src]' => '',
'seckit_xss[csp][media-src]' => '',
'seckit_xss[csp][style-src]' => '',
'seckit_xss[csp][frame-src]' => '',
'seckit_xss[csp][frame-ancestors]' => '',
'seckit_xss[csp][child-src]' => '',
'seckit_xss[csp][font-src]' => '',
'seckit_xss[csp][connect-src]' => '',
'seckit_xss[csp][report-uri]' => $this->reportPath,
'seckit_xss[csp][upgrade-req]' => FALSE,
'seckit_xss[csp][policy-uri]' => '',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = "default-src self; report-uri " . base_path() . $this->reportPath;
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', $expected);
}
public function testReportOnlyCsp() {
$form['seckit_xss[csp][checkbox]'] = TRUE;
$form['seckit_xss[csp][vendor-prefix][x]'] = TRUE;
$form['seckit_xss[csp][vendor-prefix][webkit]'] = TRUE;
$form['seckit_xss[csp][report-only]'] = TRUE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderContains('Content-Security-Policy-Report-Only', 'report-uri');
$this
->assertSession()
->responseHeaderContains('X-Content-Security-Policy-Report-Only', 'report-uri');
$this
->assertSession()
->responseHeaderContains('X-WebKit-CSP-Report-Only', 'report-uri');
}
public function testCspReportUri() {
$report_uris = [
[
'uri' => '//example.com/csp-report',
'absolute' => TRUE,
'valid' => TRUE,
],
[
'uri' => 'https://example.com/report-uri',
'absolute' => TRUE,
'valid' => TRUE,
],
[
'uri' => 'http://in<val>.id/url',
'absolute' => TRUE,
'valid' => FALSE,
],
[
'uri' => $this->reportPath,
'absolute' => FALSE,
'valid' => TRUE,
],
[
'uri' => 'filter/tips',
'absolute' => FALSE,
'valid' => TRUE,
],
[
'uri' => 'non-existent-path',
'absolute' => FALSE,
'valid' => FALSE,
],
[
'uri' => '/' . $this->reportPath,
'absolute' => FALSE,
'valid' => TRUE,
],
];
foreach ($report_uris as $report_uri) {
$form['seckit_xss[csp][checkbox]'] = TRUE;
$form['seckit_xss[csp][vendor-prefix][x]'] = TRUE;
$form['seckit_xss[csp][vendor-prefix][webkit]'] = TRUE;
$form['seckit_xss[csp][default-src]'] = 'self';
$form['seckit_xss[csp][report-uri]'] = $report_uri['uri'];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
if ($report_uri['valid']) {
$base_path = $report_uri['absolute'] ? '' : base_path();
$expected = 'default-src self; report-uri ' . $base_path . $report_uri['uri'];
if (!$report_uri['absolute'] && strpos($report_uri['uri'], '/') === 0) {
$expected = 'default-src self; report-uri ' . $base_path . ltrim($report_uri['uri'], '/');
}
$this
->assertSession()
->responseHeaderEquals('Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-Content-Security-Policy', $expected);
$this
->assertSession()
->responseHeaderEquals('X-WebKit-CSP', $expected);
}
else {
if ($report_uri['absolute']) {
$expected = 'The CSP report-uri seems absolute but does not seem to be a valid URI.';
$uri_type = 'absolute';
}
else {
$expected = 'The CSP report-uri seems relative but does not seem to be a valid path.';
$uri_type = 'relative';
}
$this
->assertSession()
->responseContains($expected, sprintf('Invalid %s setting for CSP report-uri was rejected.', $uri_type));
}
}
}
public function testXxssProtectionIsDisabled() {
$form['seckit_xss[x_xss][select]'] = SeckitInterface::X_XSS_DISABLE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-XSS-Protection', NULL);
}
public function testXxssProtectionIs0() {
$form['seckit_xss[x_xss][select]'] = SeckitInterface::X_XSS_0;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-XSS-Protection', '0');
}
public function testXxssProtectionIs1() {
$form['seckit_xss[x_xss][select]'] = SeckitInterface::X_XSS_1;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-XSS-Protection', '1');
}
public function testXxssProtectionIs1Block() {
$form['seckit_xss[x_xss][select]'] = SeckitInterface::X_XSS_1_BLOCK;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-XSS-Protection', '1; mode=block');
}
public function testOriginAllowsSite() {
$form['seckit_csrf[origin]'] = TRUE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this->originHeader = \Drupal::request()
->getSchemeAndHttpHost();
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->statusCodeEquals(200);
}
public function testOriginAllowsSpecifiedSource() {
$form = [
'seckit_csrf[origin]' => TRUE,
'seckit_csrf[origin_whitelist]' => 'http://www.example.com',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this->originHeader = 'http://www.example.com';
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->statusCodeEquals(200);
}
public function testOriginAllowsSpecifiedSourceMultiWhitelist() {
$form = [
'seckit_csrf[origin]' => TRUE,
'seckit_csrf[origin_whitelist]' => 'http://www.example.com, https://www.example.com, https://example.com:8080',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this->originHeader = 'http://www.example.com';
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->statusCodeEquals(200);
}
public function testOriginDeny() {
$form['seckit_csrf[origin]'] = TRUE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this->originHeader = 'http://www.example.com';
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertEqual([], $_POST, t('POST is empty.'));
$this
->assertSession()
->statusCodeEquals(403);
}
public function testXframeOptionsIsDisabled() {
$form['seckit_clickjacking[x_frame]'] = SeckitInterface::X_FRAME_DISABLE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-Frame-Options', NULL);
}
public function testXframeOptionsIsSameOrigin() {
$form['seckit_clickjacking[x_frame]'] = SeckitInterface::X_FRAME_SAMEORIGIN;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-Frame-Options', 'SAMEORIGIN');
}
public function testXframeOptionsIsDeny() {
$form['seckit_clickjacking[x_frame]'] = SeckitInterface::X_FRAME_DENY;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-Frame-Options', 'DENY');
}
public function testXframeOptionsIsAllowFrom() {
$form['seckit_clickjacking[x_frame]'] = SeckitInterface::X_FRAME_ALLOW_FROM;
$form['seckit_clickjacking[x_frame_allow_from]'] = 'http://www.google.com';
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('X-Frame-Options', 'ALLOW-FROM http://www.google.com');
}
public function testJsCssNoscript() {
$form['seckit_clickjacking[js_css_noscript]'] = TRUE;
$form['seckit_clickjacking[noscript_message]'] = 'Sorry, your JavaScript is disabled.';
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$config = \Drupal::config('seckit.settings');
$noscript_message = $config
->get('seckit_clickjacking.noscript_message');
$noscript_message = $noscript_message ? $noscript_message : $config
->get('seckit_clickjacking.noscript_message');
$path = base_path() . drupal_get_path('module', 'seckit');
$code = <<<EOT
<script type="text/javascript" src="{<span class="php-variable">$path</span>}/js/seckit.document_write.js"></script>
<link type="text/css" rel="stylesheet" id="seckit-clickjacking-no-body" media="all" href="{<span class="php-variable">$path</span>}/css/seckit.no_body.css" />
<!-- stop SecKit protection -->
<noscript>
<link type="text/css" rel="stylesheet" id="seckit-clickjacking-noscript-tag" media="all" href="{<span class="php-variable">$path</span>}/css/seckit.noscript_tag.css" />
<div id="seckit-noscript-tag">
{<span class="php-variable">$noscript_message</span>}
</div>
</noscript>
EOT;
$this
->assertSession()
->responseContains($code, t('JavaScript + CSS + Noscript protection is loaded.'));
}
public function testDisabledHsts() {
$form['seckit_ssl[hsts]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Strict-Transport-Security', NULL);
}
public function testHstsAllDirectves() {
$form = [
'seckit_ssl[hsts]' => TRUE,
'seckit_ssl[hsts_max_age]' => 1000,
'seckit_ssl[hsts_subdomains]' => 1,
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'max-age=1000; includeSubDomains';
$this
->assertSession()
->responseHeaderEquals('Strict-Transport-Security', $expected);
}
public function testDisabledFromOrigin() {
$form['seckit_various[from_origin]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('From-Origin', NULL);
}
public function testEnabledFromOrigin() {
$form = [
'seckit_various[from_origin]' => TRUE,
'seckit_various[from_origin_destination]' => 'same',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('From-Origin', 'same');
}
public function testDisabledReferrerPolicy() {
$form['seckit_various[referrer_policy]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Referrer-Policy', NULL);
}
public function testEnabledReferrerPolicy() {
$form = [
'seckit_various[referrer_policy]' => TRUE,
'seckit_various[referrer_policy_policy]' => 'no-referrer-when-downgrade',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Referrer-Policy', 'no-referrer-when-downgrade');
}
public function testDisabledExpectCt() {
$form['seckit_ct[expect_ct]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Expect-CT', NULL);
}
public function testEnableExpectCt() {
$form = [
'seckit_ct[expect_ct]' => TRUE,
'seckit_ct[max_age]' => 86400,
'seckit_ct[enforce]' => TRUE,
'seckit_ct[report_uri]' => 'https://www.example.com/report',
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = 'max-age=86400, enforce, report-uri="https://www.example.com/report"';
$this
->assertSession()
->responseHeaderEquals('Expect-CT', $expected);
}
public function testDisabledFeaturePolicy() {
$form['seckit_fp[feature_policy]'] = FALSE;
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$this
->assertSession()
->responseHeaderEquals('Feature-Policy', NULL);
}
public function testEnabledFeaturePolicy() {
$form = [
'seckit_fp[feature_policy]' => TRUE,
'seckit_fp[feature_policy_policy]' => "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'",
];
$this
->drupalPostForm('admin/config/system/seckit', $form, t('Save configuration'));
$expected = "accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'";
$this
->assertSession()
->responseHeaderEquals('Feature-Policy', $expected);
}
protected function secKitRequestHeader() {
return function (callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
if ($this->originHeader) {
$request = $request
->withHeader('origin', $this->originHeader);
}
return $handler($request, $options);
};
};
}
}