Commit 7b55855e authored by cilefen's avatar cilefen

SA-CORE-2017-001 by alexpott, benjy, Berdir, casey, cilefen, dawehner, Heine,...

SA-CORE-2017-001 by alexpott, benjy, Berdir, casey, cilefen, dawehner, Heine, klausi, larowlan, Mixologic, mlhess, pwolanin, samuel.mortenson, Wim Leers, xjm, YesCT
parent d20187f4
...@@ -3642,16 +3642,16 @@ ...@@ -3642,16 +3642,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "4.8.27", "version": "4.8.28",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90" "reference": "558a3a0d28b4cb7e4a593a4fbd2220e787076225"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c062dddcb68e44b563f66ee319ddae2b5a322a90", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/558a3a0d28b4cb7e4a593a4fbd2220e787076225",
"reference": "c062dddcb68e44b563f66ee319ddae2b5a322a90", "reference": "558a3a0d28b4cb7e4a593a4fbd2220e787076225",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -3710,7 +3710,7 @@ ...@@ -3710,7 +3710,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2016-07-21T06:48:14+00:00" "time": "2016-11-14T06:25:28+00:00"
}, },
{ {
"name": "phpunit/phpunit-mock-objects", "name": "phpunit/phpunit-mock-objects",
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
"jcalderonzumba/gastonjs": "~1.0.2", "jcalderonzumba/gastonjs": "~1.0.2",
"jcalderonzumba/mink-phantomjs-driver": "~0.3.1", "jcalderonzumba/mink-phantomjs-driver": "~0.3.1",
"mikey179/vfsStream": "~1.2", "mikey179/vfsStream": "~1.2",
"phpunit/phpunit": "~4.8", "phpunit/phpunit": ">=4.8.28 <5",
"symfony/css-selector": "~2.8" "symfony/css-selector": "~2.8"
}, },
"replace": { "replace": {
......
...@@ -32,6 +32,7 @@ entity.block.enable: ...@@ -32,6 +32,7 @@ entity.block.enable:
op: enable op: enable
requirements: requirements:
_entity_access: 'block.enable' _entity_access: 'block.enable'
_csrf_token: 'TRUE'
entity.block.disable: entity.block.disable:
path: '/admin/structure/block/manage/{block}/disable' path: '/admin/structure/block/manage/{block}/disable'
...@@ -40,6 +41,7 @@ entity.block.disable: ...@@ -40,6 +41,7 @@ entity.block.disable:
op: disable op: disable
requirements: requirements:
_entity_access: 'block.disable' _entity_access: 'block.disable'
_csrf_token: 'TRUE'
block.admin_display: block.admin_display:
path: '/admin/structure/block' path: '/admin/structure/block'
......
...@@ -312,4 +312,18 @@ public function testBlockValidateErrors() { ...@@ -312,4 +312,18 @@ public function testBlockValidateErrors() {
$this->assertTrue($error_class, 'Plugin error class found in parent form.'); $this->assertTrue($error_class, 'Plugin error class found in parent form.');
} }
/**
* Tests that the enable/disable routes are protected from CSRF.
*/
public function testRouteProtection() {
// Get the first block generated in our setUp method.
/** @var \Drupal\block\BlockInterface $block */
$block = reset($this->blocks);
// Ensure that the enable and disable routes are protected.
$this->drupalGet('admin/structure/block/manage/' . $block->id() . '/disable');
$this->assertResponse(403);
$this->drupalGet('admin/structure/block/manage/' . $block->id() . '/enable');
$this->assertResponse(403);
}
} }
...@@ -520,8 +520,8 @@ function editor_file_download($uri) { ...@@ -520,8 +520,8 @@ function editor_file_download($uri) {
if ($file->isPermanent()) { if ($file->isPermanent()) {
$referencing_entity_is_accessible = FALSE; $referencing_entity_is_accessible = FALSE;
$references = empty($usage_list['editor']) ? [] : $usage_list['editor']; $references = empty($usage_list['editor']) ? [] : $usage_list['editor'];
foreach ($references as $entity_type => $entity_ids) { foreach ($references as $entity_type => $entity_ids_usage_count) {
$referencing_entities = entity_load_multiple($entity_type, $entity_ids); $referencing_entities = entity_load_multiple($entity_type, array_keys($entity_ids_usage_count));
/** @var \Drupal\Core\Entity\EntityInterface $referencing_entity */ /** @var \Drupal\Core\Entity\EntityInterface $referencing_entity */
foreach ($referencing_entities as $referencing_entity) { foreach ($referencing_entities as $referencing_entity) {
if ($referencing_entity->access('view', NULL, TRUE)->isAllowed()) { if ($referencing_entity->access('view', NULL, TRUE)->isAllowed()) {
......
...@@ -68,9 +68,18 @@ public function testEditorPrivateFileReferenceFilter() { ...@@ -68,9 +68,18 @@ public function testEditorPrivateFileReferenceFilter() {
$file->setPermanent(); $file->setPermanent();
$file->save(); $file->save();
// Create some nodes to ensure file usage count does not match the ID's
// of the nodes we are going to check.
for ($i = 0; $i < 5; $i++) {
$this->drupalCreateNode([
'type' => 'page',
'uid' => $author->id(),
]);
}
// Create a node with its body field properly pointing to the just-created // Create a node with its body field properly pointing to the just-created
// file. // file.
$node = $this->drupalCreateNode([ $published_node = $this->drupalCreateNode([
'type' => 'page', 'type' => 'page',
'body' => [ 'body' => [
'value' => '<img alt="alt" data-entity-type="file" data-entity-uuid="' . $file->uuid() . '" src="' . $src . '" />', 'value' => '<img alt="alt" data-entity-type="file" data-entity-uuid="' . $file->uuid() . '" src="' . $src . '" />',
...@@ -79,19 +88,44 @@ public function testEditorPrivateFileReferenceFilter() { ...@@ -79,19 +88,44 @@ public function testEditorPrivateFileReferenceFilter() {
'uid' => $author->id(), 'uid' => $author->id(),
]); ]);
// Create an unpublished node with its body field properly pointing to the
// just-created file.
$unpublished_node = $this->drupalCreateNode([
'type' => 'page',
'status' => NODE_NOT_PUBLISHED,
'body' => [
'value' => '<img alt="alt" data-entity-type="file" data-entity-uuid="' . $file->uuid() . '" src="' . $src . '" />',
'format' => 'private_images',
],
'uid' => $author->id(),
]);
// Do the actual test. The image should be visible for anonymous users, // Do the actual test. The image should be visible for anonymous users,
// because they can view the referencing entity. // because they can view the published node. Even though they can't view
$this->drupalGet($node->toUrl()); // the unpublished node.
$this->drupalGet($published_node->toUrl());
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
$this->drupalGet($unpublished_node->toUrl());
$this->assertSession()->statusCodeEquals(403);
$this->drupalGet($src); $this->drupalGet($src);
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
// When the published node is also unpublished, the image should also
// become inaccessible to anonymous users.
$published_node->setPublished(FALSE)->save();
$this->drupalGet($published_node->toUrl());
$this->assertSession()->statusCodeEquals(403);
$this->drupalGet($src);
$this->assertSession()->statusCodeEquals(403);
// Disallow anonymous users to view the entity, which then should also // Disallow anonymous users to view the entity, which then should also
// disallow them to view the image. // disallow them to view the image.
$published_node->setPublished(TRUE)->save();
Role::load(RoleInterface::ANONYMOUS_ID) Role::load(RoleInterface::ANONYMOUS_ID)
->revokePermission('access content') ->revokePermission('access content')
->save(); ->save();
$this->drupalGet($node->toUrl()); $this->drupalGet($published_node->toUrl());
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
$this->drupalGet($src); $this->drupalGet($src);
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
......
...@@ -37,6 +37,7 @@ entity.search_page.enable: ...@@ -37,6 +37,7 @@ entity.search_page.enable:
op: 'enable' op: 'enable'
requirements: requirements:
_entity_access: 'search_page.update' _entity_access: 'search_page.update'
_csrf_token: 'TRUE'
entity.search_page.disable: entity.search_page.disable:
path: '/admin/config/search/pages/manage/{search_page}/disable' path: '/admin/config/search/pages/manage/{search_page}/disable'
...@@ -45,6 +46,7 @@ entity.search_page.disable: ...@@ -45,6 +46,7 @@ entity.search_page.disable:
op: 'disable' op: 'disable'
requirements: requirements:
_entity_access: 'search_page.disable' _entity_access: 'search_page.disable'
_csrf_token: 'TRUE'
entity.search_page.set_default: entity.search_page.set_default:
path: '/admin/config/search/pages/manage/{search_page}/set-default' path: '/admin/config/search/pages/manage/{search_page}/set-default'
...@@ -52,6 +54,7 @@ entity.search_page.set_default: ...@@ -52,6 +54,7 @@ entity.search_page.set_default:
_controller: '\Drupal\search\Controller\SearchController::setAsDefault' _controller: '\Drupal\search\Controller\SearchController::setAsDefault'
requirements: requirements:
_entity_access: 'search_page.update' _entity_access: 'search_page.update'
_csrf_token: 'TRUE'
entity.search_page.delete_form: entity.search_page.delete_form:
path: '/admin/config/search/pages/manage/{search_page}/delete' path: '/admin/config/search/pages/manage/{search_page}/delete'
......
...@@ -75,6 +75,16 @@ public function getFormId() { ...@@ -75,6 +75,16 @@ public function getFormId() {
public function buildForm(array $form, FormStateInterface $form_state) { public function buildForm(array $form, FormStateInterface $form_state) {
// Set up the form to submit using GET to the correct search page. // Set up the form to submit using GET to the correct search page.
$entity_id = $this->searchPageRepository->getDefaultSearchPage(); $entity_id = $this->searchPageRepository->getDefaultSearchPage();
$form = [];
// SearchPageRepository::getDefaultSearchPage() depends on search.settings.
// The dependency needs to be added before the conditional return, otherwise
// the block would get cached without the necessary cacheablity metadata in
// case there is no default search page and would not be invalidated if that
// changes.
$this->renderer->addCacheableDependency($form, $this->configFactory->get('search.settings'));
if (!$entity_id) { if (!$entity_id) {
$form['message'] = [ $form['message'] = [
'#markup' => $this->t('Search is currently disabled'), '#markup' => $this->t('Search is currently disabled'),
...@@ -103,9 +113,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { ...@@ -103,9 +113,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'#name' => '', '#name' => '',
]; ];
// SearchPageRepository::getDefaultSearchPage() depends on search.settings.
$this->renderer->addCacheableDependency($form, $this->configFactory->get('search.settings'));
return $form; return $form;
} }
......
...@@ -154,8 +154,7 @@ public function testSearchModuleDisabling() { ...@@ -154,8 +154,7 @@ public function testSearchModuleDisabling() {
// Test each plugin if it's enabled as the only search plugin. // Test each plugin if it's enabled as the only search plugin.
foreach ($entities as $entity_id => $entity) { foreach ($entities as $entity_id => $entity) {
// Set this as default. $this->setDefaultThroughUi($entity_id);
$this->drupalGet("admin/config/search/pages/manage/$entity_id/set-default");
// Run a search from the correct search URL. // Run a search from the correct search URL.
$info = $plugin_info[$entity_id]; $info = $plugin_info[$entity_id];
...@@ -187,13 +186,16 @@ public function testSearchModuleDisabling() { ...@@ -187,13 +186,16 @@ public function testSearchModuleDisabling() {
$entity->disable()->save(); $entity->disable()->save();
} }
// Set the node search as default.
$this->setDefaultThroughUi('node_search');
// Test with all search plugins enabled. When you go to the search // Test with all search plugins enabled. When you go to the search
// page or run search, all plugins should be shown. // page or run search, all plugins should be shown.
foreach ($entities as $entity) { foreach ($entities as $entity) {
$entity->enable()->save(); $entity->enable()->save();
} }
// Set the node search as default.
$this->drupalGet('admin/config/search/pages/manage/node_search/set-default'); \Drupal::service('router.builder')->rebuild();
$paths = [ $paths = [
['path' => 'search/node', 'options' => ['query' => ['keys' => 'pizza']]], ['path' => 'search/node', 'options' => ['query' => ['keys' => 'pizza']]],
...@@ -316,6 +318,19 @@ public function testMultipleSearchPages() { ...@@ -316,6 +318,19 @@ public function testMultipleSearchPages() {
$this->verifySearchPageOperations($first_id, FALSE, FALSE, FALSE, FALSE); $this->verifySearchPageOperations($first_id, FALSE, FALSE, FALSE, FALSE);
} }
/**
* Tests that the enable/disable/default routes are protected from CSRF.
*/
public function testRouteProtection() {
// Ensure that the enable and disable routes are protected.
$this->drupalGet('admin/config/search/pages/manage/node_search/enable');
$this->assertResponse(403);
$this->drupalGet('admin/config/search/pages/manage/node_search/disable');
$this->assertResponse(403);
$this->drupalGet('admin/config/search/pages/manage/node_search/set-default');
$this->assertResponse(403);
}
/** /**
* Checks that the search page operations match expectations. * Checks that the search page operations match expectations.
* *
...@@ -373,4 +388,17 @@ protected function assertDefaultSearch($expected, $message = '', $group = 'Other ...@@ -373,4 +388,17 @@ protected function assertDefaultSearch($expected, $message = '', $group = 'Other
$this->assertIdentical($search_page_repository->getDefaultSearchPage(), $expected, $message, $group); $this->assertIdentical($search_page_repository->getDefaultSearchPage(), $expected, $message, $group);
} }
/**
* Sets a search page as the default in the UI.
*
* @param string $entity_id
* The search page entity ID to enable.
*/
protected function setDefaultThroughUi($entity_id) {
$this->drupalGet('admin/config/search/pages');
preg_match('|href="([^"]+' . $entity_id . '/set-default[^"]+)"|', $this->getRawContent(), $matches);
$this->drupalGet($this->getAbsoluteUrl($matches[1]));
}
} }
...@@ -901,7 +901,7 @@ protected function drupalGet($path, array $options = [], array $headers = []) { ...@@ -901,7 +901,7 @@ protected function drupalGet($path, array $options = [], array $headers = []) {
} }
if ($path instanceof Url) { if ($path instanceof Url) {
$path = $path->toString(); $path = $path->setAbsolute()->toString(TRUE)->getGeneratedUrl();
} }
$verbose = 'GET request to: ' . $path . $verbose = 'GET request to: ' . $path .
...@@ -2127,7 +2127,7 @@ protected function buildUrl($path, array $options = []) { ...@@ -2127,7 +2127,7 @@ protected function buildUrl($path, array $options = []) {
$url_options = $path->getOptions(); $url_options = $path->getOptions();
$options = $url_options + $options; $options = $url_options + $options;
$path->setOptions($options); $path->setOptions($options);
return $path->setAbsolute()->toString(); return $path->setAbsolute()->toString(TRUE)->getGeneratedUrl();
} }
// The URL generator service is not necessarily available yet; e.g., in // The URL generator service is not necessarily available yet; e.g., in
// interactive installer tests. // interactive installer tests.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment