annotate core/modules/search/tests/src/Functional/SearchCommentTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@17 1 <?php
Chris@17 2
Chris@17 3 namespace Drupal\Tests\search\Functional;
Chris@17 4
Chris@17 5 use Behat\Mink\Exception\ResponseTextException;
Chris@17 6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
Chris@17 7 use Drupal\comment\Tests\CommentTestTrait;
Chris@17 8 use Drupal\field\Entity\FieldConfig;
Chris@17 9 use Drupal\Tests\BrowserTestBase;
Chris@17 10 use Drupal\Tests\Traits\Core\CronRunTrait;
Chris@17 11 use Drupal\user\RoleInterface;
Chris@17 12 use Drupal\filter\Entity\FilterFormat;
Chris@17 13
Chris@17 14 /**
Chris@17 15 * Tests integration searching comments.
Chris@17 16 *
Chris@17 17 * @group search
Chris@17 18 */
Chris@17 19 class SearchCommentTest extends BrowserTestBase {
Chris@17 20
Chris@17 21 use CommentTestTrait;
Chris@17 22 use CronRunTrait;
Chris@17 23
Chris@17 24 /**
Chris@17 25 * {@inheritdoc}
Chris@17 26 */
Chris@17 27 protected static $modules = ['filter', 'node', 'comment', 'search'];
Chris@17 28
Chris@17 29 /**
Chris@17 30 * Test subject for comments.
Chris@17 31 *
Chris@17 32 * @var string
Chris@17 33 */
Chris@17 34 protected $commentSubject;
Chris@17 35
Chris@17 36 /**
Chris@17 37 * ID for the administrator role.
Chris@17 38 *
Chris@17 39 * @var string
Chris@17 40 */
Chris@17 41 protected $adminRole;
Chris@17 42
Chris@17 43 /**
Chris@17 44 * A user with various administrative permissions.
Chris@17 45 *
Chris@17 46 * @var \Drupal\user\UserInterface
Chris@17 47 */
Chris@17 48 protected $adminUser;
Chris@17 49
Chris@17 50 /**
Chris@17 51 * Test node for searching.
Chris@17 52 *
Chris@17 53 * @var \Drupal\node\NodeInterface
Chris@17 54 */
Chris@17 55 protected $node;
Chris@17 56
Chris@17 57 protected function setUp() {
Chris@17 58 parent::setUp();
Chris@17 59
Chris@17 60 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
Chris@17 61 $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
Chris@17 62
Chris@17 63 $full_html_format = FilterFormat::create([
Chris@17 64 'format' => 'full_html',
Chris@17 65 'name' => 'Full HTML',
Chris@17 66 'weight' => 1,
Chris@17 67 'filters' => [],
Chris@17 68 ]);
Chris@17 69 $full_html_format->save();
Chris@17 70
Chris@17 71 // Create and log in an administrative user having access to the Full HTML
Chris@17 72 // text format.
Chris@17 73 $permissions = [
Chris@17 74 'administer filters',
Chris@17 75 $full_html_format->getPermissionName(),
Chris@17 76 'administer permissions',
Chris@17 77 'create page content',
Chris@17 78 'post comments',
Chris@17 79 'skip comment approval',
Chris@17 80 'access comments',
Chris@17 81 ];
Chris@17 82 $this->adminUser = $this->drupalCreateUser($permissions);
Chris@17 83 $this->drupalLogin($this->adminUser);
Chris@17 84 // Add a comment field.
Chris@17 85 $this->addDefaultCommentField('node', 'article');
Chris@17 86 }
Chris@17 87
Chris@17 88 /**
Chris@17 89 * Verify that comments are rendered using proper format in search results.
Chris@17 90 */
Chris@17 91 public function testSearchResultsComment() {
Chris@17 92 $node_storage = $this->container->get('entity.manager')->getStorage('node');
Chris@17 93 // Create basic_html format that escapes all HTML.
Chris@17 94 $basic_html_format = FilterFormat::create([
Chris@17 95 'format' => 'basic_html',
Chris@17 96 'name' => 'Basic HTML',
Chris@17 97 'weight' => 1,
Chris@17 98 'filters' => [
Chris@17 99 'filter_html_escape' => ['status' => 1],
Chris@17 100 ],
Chris@17 101 'roles' => [RoleInterface::AUTHENTICATED_ID],
Chris@17 102 ]);
Chris@17 103 $basic_html_format->save();
Chris@17 104
Chris@17 105 $comment_body = 'Test comment body';
Chris@17 106
Chris@17 107 // Make preview optional.
Chris@17 108 $field = FieldConfig::loadByName('node', 'article', 'comment');
Chris@17 109 $field->setSetting('preview', DRUPAL_OPTIONAL);
Chris@17 110 $field->save();
Chris@17 111
Chris@17 112 // Allow anonymous users to search content.
Chris@17 113 $edit = [
Chris@17 114 RoleInterface::ANONYMOUS_ID . '[search content]' => 1,
Chris@17 115 RoleInterface::ANONYMOUS_ID . '[access comments]' => 1,
Chris@17 116 RoleInterface::ANONYMOUS_ID . '[post comments]' => 1,
Chris@17 117 ];
Chris@17 118 $this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
Chris@17 119
Chris@17 120 // Create a node.
Chris@17 121 $node = $this->drupalCreateNode(['type' => 'article']);
Chris@17 122 // Post a comment using 'Full HTML' text format.
Chris@17 123 $edit_comment = [];
Chris@17 124 $edit_comment['subject[0][value]'] = 'Test comment subject';
Chris@17 125 $edit_comment['comment_body[0][value]'] = '<h1>' . $comment_body . '</h1>';
Chris@17 126 $full_html_format_id = 'full_html';
Chris@17 127 $edit_comment['comment_body[0][format]'] = $full_html_format_id;
Chris@17 128 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment, t('Save'));
Chris@17 129
Chris@17 130 // Post a comment with an evil script tag in the comment subject and a
Chris@17 131 // script tag nearby a keyword in the comment body. Use the 'FULL HTML' text
Chris@17 132 // format so the script tag stored.
Chris@17 133 $edit_comment2 = [];
Chris@17 134 $edit_comment2['subject[0][value]'] = "<script>alert('subjectkeyword');</script>";
Chris@17 135 $edit_comment2['comment_body[0][value]'] = "nearbykeyword<script>alert('somethinggeneric');</script>";
Chris@17 136 $edit_comment2['comment_body[0][format]'] = $full_html_format_id;
Chris@17 137 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment2, t('Save'));
Chris@17 138
Chris@17 139 // Post a comment with a keyword inside an evil script tag in the comment
Chris@17 140 // body. Use the 'FULL HTML' text format so the script tag is stored.
Chris@17 141 $edit_comment3 = [];
Chris@17 142 $edit_comment3['subject[0][value]'] = 'asubject';
Chris@17 143 $edit_comment3['comment_body[0][value]'] = "<script>alert('insidekeyword');</script>";
Chris@17 144 $edit_comment3['comment_body[0][format]'] = $full_html_format_id;
Chris@17 145 $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment3, t('Save'));
Chris@17 146
Chris@17 147 // Invoke search index update.
Chris@17 148 $this->drupalLogout();
Chris@17 149 $this->cronRun();
Chris@17 150
Chris@17 151 // Search for the comment subject.
Chris@17 152 $edit = [
Chris@17 153 'keys' => "'" . $edit_comment['subject[0][value]'] . "'",
Chris@17 154 ];
Chris@17 155 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 156 $node_storage->resetCache([$node->id()]);
Chris@17 157 $node2 = $node_storage->load($node->id());
Chris@17 158 $this->assertText($node2->label(), 'Node found in search results.');
Chris@17 159 $this->assertText($edit_comment['subject[0][value]'], 'Comment subject found in search results.');
Chris@17 160
Chris@17 161 // Search for the comment body.
Chris@17 162 $edit = [
Chris@17 163 'keys' => "'" . $comment_body . "'",
Chris@17 164 ];
Chris@17 165 $this->drupalPostForm(NULL, $edit, t('Search'));
Chris@17 166 $this->assertText($node2->label(), 'Node found in search results.');
Chris@17 167
Chris@17 168 // Verify that comment is rendered using proper format.
Chris@17 169 $this->assertText($comment_body, 'Comment body text found in search results.');
Chris@17 170 $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.');
Chris@17 171 $this->assertNoEscaped($edit_comment['comment_body[0][value]'], 'HTML in comment body is not escaped.');
Chris@17 172
Chris@17 173 // Search for the evil script comment subject.
Chris@17 174 $edit = [
Chris@17 175 'keys' => 'subjectkeyword',
Chris@17 176 ];
Chris@17 177 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 178
Chris@17 179 // Verify the evil comment subject is escaped in search results.
Chris@17 180 $this->assertRaw('&lt;script&gt;alert(&#039;<strong>subjectkeyword</strong>&#039;);');
Chris@17 181 $this->assertNoRaw('<script>');
Chris@17 182
Chris@17 183 // Search for the keyword near the evil script tag in the comment body.
Chris@17 184 $edit = [
Chris@17 185 'keys' => 'nearbykeyword',
Chris@17 186 ];
Chris@17 187 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 188
Chris@17 189 // Verify that nearby script tag in the evil comment body is stripped from
Chris@17 190 // search results.
Chris@17 191 $this->assertRaw('<strong>nearbykeyword</strong>');
Chris@17 192 $this->assertNoRaw('<script>');
Chris@17 193
Chris@17 194 // Search for contents inside the evil script tag in the comment body.
Chris@17 195 $edit = [
Chris@17 196 'keys' => 'insidekeyword',
Chris@17 197 ];
Chris@17 198 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 199
Chris@17 200 // @todo Verify the actual search results.
Chris@17 201 // https://www.drupal.org/node/2551135
Chris@17 202
Chris@17 203 // Verify there is no script tag in search results.
Chris@17 204 $this->assertNoRaw('<script>');
Chris@17 205
Chris@17 206 // Hide comments.
Chris@17 207 $this->drupalLogin($this->adminUser);
Chris@17 208 $node->set('comment', CommentItemInterface::HIDDEN);
Chris@17 209 $node->save();
Chris@17 210
Chris@17 211 // Invoke search index update.
Chris@17 212 $this->drupalLogout();
Chris@17 213 $this->cronRun();
Chris@17 214
Chris@17 215 // Search for $title.
Chris@17 216 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 217 $this->assertText(t('Your search yielded no results.'));
Chris@17 218 }
Chris@17 219
Chris@17 220 /**
Chris@17 221 * Verify access rules for comment indexing with different permissions.
Chris@17 222 */
Chris@17 223 public function testSearchResultsCommentAccess() {
Chris@17 224 $comment_body = 'Test comment body';
Chris@17 225 $this->commentSubject = 'Test comment subject';
Chris@17 226 $roles = $this->adminUser->getRoles(TRUE);
Chris@17 227 $this->adminRole = $roles[0];
Chris@17 228
Chris@17 229 // Create a node.
Chris@17 230 // Make preview optional.
Chris@17 231 $field = FieldConfig::loadByName('node', 'article', 'comment');
Chris@17 232 $field->setSetting('preview', DRUPAL_OPTIONAL);
Chris@17 233 $field->save();
Chris@17 234 $this->node = $this->drupalCreateNode(['type' => 'article']);
Chris@17 235
Chris@17 236 // Post a comment using 'Full HTML' text format.
Chris@17 237 $edit_comment = [];
Chris@17 238 $edit_comment['subject[0][value]'] = $this->commentSubject;
Chris@17 239 $edit_comment['comment_body[0][value]'] = '<h1>' . $comment_body . '</h1>';
Chris@17 240 $this->drupalPostForm('comment/reply/node/' . $this->node->id() . '/comment', $edit_comment, t('Save'));
Chris@17 241
Chris@17 242 $this->drupalLogout();
Chris@17 243 $this->setRolePermissions(RoleInterface::ANONYMOUS_ID);
Chris@17 244 $this->assertCommentAccess(FALSE, 'Anon user has search permission but no access comments permission, comments should not be indexed');
Chris@17 245
Chris@17 246 $this->setRolePermissions(RoleInterface::ANONYMOUS_ID, TRUE);
Chris@17 247 $this->assertCommentAccess(TRUE, 'Anon user has search permission and access comments permission, comments should be indexed');
Chris@17 248
Chris@17 249 $this->drupalLogin($this->adminUser);
Chris@17 250 $this->drupalGet('admin/people/permissions');
Chris@17 251
Chris@17 252 // Disable search access for authenticated user to test admin user.
Chris@17 253 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, FALSE, FALSE);
Chris@17 254
Chris@17 255 $this->setRolePermissions($this->adminRole);
Chris@17 256 $this->assertCommentAccess(FALSE, 'Admin user has search permission but no access comments permission, comments should not be indexed');
Chris@17 257
Chris@17 258 $this->drupalGet('node/' . $this->node->id());
Chris@17 259 $this->setRolePermissions($this->adminRole, TRUE);
Chris@17 260 $this->assertCommentAccess(TRUE, 'Admin user has search permission and access comments permission, comments should be indexed');
Chris@17 261
Chris@17 262 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID);
Chris@17 263 $this->assertCommentAccess(FALSE, 'Authenticated user has search permission but no access comments permission, comments should not be indexed');
Chris@17 264
Chris@17 265 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE);
Chris@17 266 $this->assertCommentAccess(TRUE, 'Authenticated user has search permission and access comments permission, comments should be indexed');
Chris@17 267
Chris@17 268 // Verify that access comments permission is inherited from the
Chris@17 269 // authenticated role.
Chris@17 270 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE, FALSE);
Chris@17 271 $this->setRolePermissions($this->adminRole);
Chris@17 272 $this->assertCommentAccess(TRUE, 'Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments');
Chris@17 273
Chris@17 274 // Verify that search content permission is inherited from the authenticated
Chris@17 275 // role.
Chris@17 276 $this->setRolePermissions(RoleInterface::AUTHENTICATED_ID, TRUE, TRUE);
Chris@17 277 $this->setRolePermissions($this->adminRole, TRUE, FALSE);
Chris@17 278 $this->assertCommentAccess(TRUE, 'Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search');
Chris@17 279 }
Chris@17 280
Chris@17 281 /**
Chris@17 282 * Set permissions for role.
Chris@17 283 */
Chris@17 284 public function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) {
Chris@17 285 $permissions = [
Chris@17 286 'access comments' => $access_comments,
Chris@17 287 'search content' => $search_content,
Chris@17 288 ];
Chris@17 289 user_role_change_permissions($rid, $permissions);
Chris@17 290 }
Chris@17 291
Chris@17 292 /**
Chris@17 293 * Update search index and search for comment.
Chris@17 294 */
Chris@17 295 public function assertCommentAccess($assume_access, $message) {
Chris@17 296 // Invoke search index update.
Chris@17 297 search_mark_for_reindex('node_search', $this->node->id());
Chris@17 298 $this->cronRun();
Chris@17 299
Chris@17 300 // Search for the comment subject.
Chris@17 301 $edit = [
Chris@17 302 'keys' => "'" . $this->commentSubject . "'",
Chris@17 303 ];
Chris@17 304 $this->drupalPostForm('search/node', $edit, t('Search'));
Chris@17 305
Chris@17 306 try {
Chris@17 307 if ($assume_access) {
Chris@17 308 $this->assertSession()->pageTextContains($this->node->label());
Chris@17 309 $this->assertSession()->pageTextContains($this->commentSubject);
Chris@17 310 }
Chris@17 311 else {
Chris@17 312 $this->assertSession()->pageTextContains(t('Your search yielded no results.'));
Chris@17 313 }
Chris@17 314 }
Chris@17 315 catch (ResponseTextException $exception) {
Chris@17 316 $this->fail($message);
Chris@17 317 }
Chris@17 318 }
Chris@17 319
Chris@17 320 /**
Chris@17 321 * Verify that 'add new comment' does not appear in search results or index.
Chris@17 322 */
Chris@17 323 public function testAddNewComment() {
Chris@17 324 // Create a node with a short body.
Chris@17 325 $settings = [
Chris@17 326 'type' => 'article',
Chris@17 327 'title' => 'short title',
Chris@17 328 'body' => [['value' => 'short body text']],
Chris@17 329 ];
Chris@17 330
Chris@17 331 $user = $this->drupalCreateUser([
Chris@17 332 'search content',
Chris@17 333 'create article content',
Chris@17 334 'access content',
Chris@17 335 'post comments',
Chris@17 336 'access comments',
Chris@17 337 ]);
Chris@17 338 $this->drupalLogin($user);
Chris@17 339
Chris@17 340 $node = $this->drupalCreateNode($settings);
Chris@17 341 // Verify that if you view the node on its own page, 'add new comment'
Chris@17 342 // is there.
Chris@17 343 $this->drupalGet('node/' . $node->id());
Chris@17 344 $this->assertText(t('Add new comment'));
Chris@17 345
Chris@17 346 // Run cron to index this page.
Chris@17 347 $this->drupalLogout();
Chris@17 348 $this->cronRun();
Chris@17 349
Chris@17 350 // Search for 'comment'. Should be no results.
Chris@17 351 $this->drupalLogin($user);
Chris@17 352 $this->drupalPostForm('search/node', ['keys' => 'comment'], t('Search'));
Chris@17 353 $this->assertText(t('Your search yielded no results'));
Chris@17 354
Chris@17 355 // Search for the node title. Should be found, and 'Add new comment' should
Chris@17 356 // not be part of the search snippet.
Chris@17 357 $this->drupalPostForm('search/node', ['keys' => 'short'], t('Search'));
Chris@17 358 $this->assertText($node->label(), 'Search for keyword worked');
Chris@17 359 $this->assertNoText(t('Add new comment'));
Chris@17 360 }
Chris@17 361
Chris@17 362 }