From 92a68e32f668743e27241d9e473bad9e1d92cc80 Mon Sep 17 00:00:00 2001 From: mcdruid Date: Fri, 27 May 2022 12:07:35 +0100 Subject: [PATCH] Issue #2819535 by mcdruid, Kingdutch, SumeetJaggi, cafuego, liannario, David_Rothstein, Diego_Mow, Fabianx: x-content-type-options nosniff ignored for anonymous cached pages --- includes/common.inc | 1 + modules/simpletest/tests/bootstrap.test | 22 ++++++++++++++++ modules/simpletest/tests/system_test.module | 28 +++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/includes/common.inc b/includes/common.inc index 15d11b2828e..54ebf96092d 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2696,6 +2696,7 @@ function drupal_deliver_html_page($page_callback_result) { if ($frame_options && is_null(drupal_get_http_header('X-Frame-Options'))) { drupal_add_http_header('X-Frame-Options', $frame_options); } + drupal_add_http_header('X-Content-Type-Options', 'nosniff'); if (variable_get('block_interest_cohort', TRUE)) { $permissions_policy = drupal_get_http_header('Permissions-Policy'); diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test index b2412242e3f..6bee03e12f4 100644 --- a/modules/simpletest/tests/bootstrap.test +++ b/modules/simpletest/tests/bootstrap.test @@ -187,6 +187,7 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase { $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.'); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.'); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.'); + $this->assertEqual($this->drupalGetHeader('X-Content-Type-Options'), 'nosniff', 'X-Content-Type-Options header was sent.'); // Check replacing default headers. $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT'))); @@ -251,6 +252,27 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase { $this->drupalGet(''); $this->assertRaw('', 'Page was delivered after compression mode is changed (compression support disabled).'); } + + /** + * Test page cache headers. + */ + function testPageCacheHeaders() { + variable_set('cache', 1); + // First request should store a response in the page cache. + $this->drupalGet('system-test/page-cache-headers'); + + // The test callback should remove the query string leaving the same path + // as the previous request, which we'll try to retrieve from cache_page. + $this->drupalGet('system-test/page-cache-headers', array('query' => array('return_headers' => 'TRUE'))); + + $headers = json_decode($this->drupalGetHeader('Page-Cache-Headers'), TRUE); + if (is_null($headers)) { + $this->fail('No headers were retrieved from the page cache.'); + } + else { + $this->assertEqual($headers['X-Content-Type-Options'], 'nosniff', 'X-Content-Type-Options header retrieved from response in the page cache.'); + } + } } class BootstrapVariableTestCase extends DrupalWebTestCase { diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module index b00d9b075be..34a3b46f943 100644 --- a/modules/simpletest/tests/system_test.module +++ b/modules/simpletest/tests/system_test.module @@ -148,6 +148,12 @@ function system_test_menu() { 'type' => MENU_CALLBACK, ); + $items['system-test/page-cache-headers'] = array( + 'page callback' => 'system_test_page_cache_headers', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -230,6 +236,28 @@ function system_test_redirect_invalid_scheme() { exit; } +/** + * Menu callback to test headers stored in the page cache. + */ +function system_test_page_cache_headers() { + if (!isset($_GET['return_headers'])) { + return t('Content to store in the page cache if it is enabled.'); + } + global $base_root; + // Remove the test query param but try to preserve any remaining query string. + $url = parse_url($base_root . request_uri()); + $query_parts = explode('&', $url['query']); + $query_string = implode('&', array_diff($query_parts, array('return_headers=TRUE'))); + $request_uri = $url['path'] . '?' . $query_string; + $cache = cache_get($base_root . $request_uri, 'cache_page'); + // If there are any headers stored in the cache, output them. + if (isset($cache->data['headers'])) { + drupal_add_http_header('Page-Cache-Headers', json_encode($cache->data['headers'])); + return 'Headers from cache_page returned in the Page-Cache-Headers http response header.'; + } + return 'No headers retrieved from cache_page.'; +} + /** * Implements hook_modules_installed(). */