/** * Checks that harmful protocols are stripped. */ function testBadProtocolStripping() { // Ensure that check_url() strips out harmful protocols, and encodes for // HTML. // Ensure \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() can // be used to return a plain-text string stripped of harmful protocols. $url = 'javascript:http://www.example.com/?x=1&y=2'; $expected_plain = 'http://www.example.com/?x=1&y=2'; $expected_html = 'http://www.example.com/?x=1&y=2'; $this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.'); $this->assertIdentical(UrlHelper::filterBadProtocol($url), $expected_html, '\\Drupal\\Component\\Utility\\UrlHelper::filterBadProtocol() filters a URL and encodes it for HTML.'); $this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\\Drupal\\Component\\Utility\\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.'); }
/** * Creates a comment, then tests the tokens generated from it. */ function testCommentTokenReplacement() { $token_service = \Drupal::token(); $language_interface = \Drupal::languageManager()->getCurrentLanguage(); $url_options = array('absolute' => TRUE, 'language' => $language_interface); // Change the title of the admin user. $this->adminUser->name->value = 'This is a title with some special & > " stuff.'; $this->adminUser->save(); $this->drupalLogin($this->adminUser); // Set comment variables. $this->setCommentSubject(TRUE); // Create a node and a comment. $node = $this->drupalCreateNode(['type' => 'article', 'title' => '<script>alert("123")</script>']); $parent_comment = $this->postComment($node, $this->randomMachineName(), $this->randomMachineName(), TRUE); // Post a reply to the comment. $this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $parent_comment->id()); $child_comment = $this->postComment(NULL, $this->randomMachineName(), $this->randomMachineName()); $comment = Comment::load($child_comment->id()); $comment->setHomepage('http://example.org/'); // Add HTML to ensure that sanitation of some fields tested directly. $comment->setSubject('<blink>Blinking Comment</blink>'); // Generate and test tokens. $tests = array(); $tests['[comment:cid]'] = $comment->id(); $tests['[comment:hostname]'] = $comment->getHostname(); $tests['[comment:author]'] = Html::escape($comment->getAuthorName()); $tests['[comment:mail]'] = $this->adminUser->getEmail(); $tests['[comment:homepage]'] = UrlHelper::filterBadProtocol($comment->getHomepage()); $tests['[comment:title]'] = Html::escape($comment->getSubject()); $tests['[comment:body]'] = $comment->comment_body->processed; $tests['[comment:langcode]'] = $comment->language()->getId(); $tests['[comment:url]'] = $comment->url('canonical', $url_options + array('fragment' => 'comment-' . $comment->id())); $tests['[comment:edit-url]'] = $comment->url('edit-form', $url_options); $tests['[comment:created]'] = \Drupal::service('date.formatter')->format($comment->getCreatedTime(), 'medium', array('langcode' => $language_interface->getId())); $tests['[comment:created:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getCreatedTime(), array('langcode' => $language_interface->getId())); $tests['[comment:changed:since]'] = \Drupal::service('date.formatter')->formatTimeDiffSince($comment->getChangedTimeAcrossTranslations(), array('langcode' => $language_interface->getId())); $tests['[comment:parent:cid]'] = $comment->hasParentComment() ? $comment->getParentComment()->id() : NULL; $tests['[comment:parent:title]'] = $parent_comment->getSubject(); $tests['[comment:entity]'] = Html::escape($node->getTitle()); // Test node specific tokens. $tests['[comment:entity:nid]'] = $comment->getCommentedEntityId(); $tests['[comment:entity:title]'] = Html::escape($node->getTitle()); $tests['[comment:author:uid]'] = $comment->getOwnerId(); $tests['[comment:author:name]'] = Html::escape($this->adminUser->getDisplayName()); $base_bubbleable_metadata = BubbleableMetadata::createFromObject($comment); $metadata_tests = []; $metadata_tests['[comment:cid]'] = $base_bubbleable_metadata; $metadata_tests['[comment:hostname]'] = $base_bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata; $bubbleable_metadata->addCacheableDependency($this->adminUser); $metadata_tests['[comment:author]'] = $bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata; $bubbleable_metadata->addCacheableDependency($this->adminUser); $metadata_tests['[comment:mail]'] = $bubbleable_metadata; $metadata_tests['[comment:homepage]'] = $base_bubbleable_metadata; $metadata_tests['[comment:title]'] = $base_bubbleable_metadata; $metadata_tests['[comment:body]'] = $base_bubbleable_metadata; $metadata_tests['[comment:langcode]'] = $base_bubbleable_metadata; $metadata_tests['[comment:url]'] = $base_bubbleable_metadata; $metadata_tests['[comment:edit-url]'] = $base_bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:created]'] = $bubbleable_metadata->addCacheTags(['rendered']); $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:created:since]'] = $bubbleable_metadata->setCacheMaxAge(0); $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:changed:since]'] = $bubbleable_metadata->setCacheMaxAge(0); $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:parent:cid]'] = $bubbleable_metadata->addCacheTags(['comment:1']); $metadata_tests['[comment:parent:title]'] = $bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:entity]'] = $bubbleable_metadata->addCacheTags(['node:2']); // Test node specific tokens. $metadata_tests['[comment:entity:nid]'] = $bubbleable_metadata; $metadata_tests['[comment:entity:title]'] = $bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata; $metadata_tests['[comment:author:uid]'] = $bubbleable_metadata->addCacheTags(['user:2']); $metadata_tests['[comment:author:name]'] = $bubbleable_metadata; // Test to make sure that we generated something for each token. $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.'); foreach ($tests as $input => $expected) { $bubbleable_metadata = new BubbleableMetadata(); $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId()), $bubbleable_metadata); $this->assertEqual($output, $expected, new FormattableMarkup('Comment token %token replaced.', ['%token' => $input])); $this->assertEqual($bubbleable_metadata, $metadata_tests[$input]); } // Test anonymous comment author. $author_name = 'This is a random & " > string'; $comment->setOwnerId(0)->setAuthorName($author_name); $input = '[comment:author]'; $output = $token_service->replace($input, array('comment' => $comment), array('langcode' => $language_interface->getId())); $this->assertEqual($output, Html::escape($author_name), format_string('Comment author token %token replaced.', array('%token' => $input))); // Load node so comment_count gets computed. $node = Node::load($node->id()); // Generate comment tokens for the node (it has 2 comments, both new). $tests = array(); $tests['[entity:comment-count]'] = 2; $tests['[entity:comment-count-new]'] = 2; foreach ($tests as $input => $expected) { $output = $token_service->replace($input, array('entity' => $node, 'node' => $node), array('langcode' => $language_interface->getId())); $this->assertEqual($output, $expected, format_string('Node comment token %token replaced.', array('%token' => $input))); } }
/** * Tests bad protocol filtering and escaping. * * @dataProvider providerTestFilterBadProtocol * @covers ::setAllowedProtocols * @covers ::filterBadProtocol * * @param string $uri * Protocol URI. * @param string $expected * Expected escaped value. * @param array $protocols * Protocols to allow. */ public function testFilterBadProtocol($uri, $expected, $protocols) { UrlHelper::setAllowedProtocols($protocols); $this->assertEquals($expected, UrlHelper::filterBadProtocol($uri)); // Multiple calls to UrlHelper::filterBadProtocol() do not cause double // escaping. $this->assertEquals($expected, UrlHelper::filterBadProtocol(UrlHelper::filterBadProtocol($uri))); }
/** * Tests bad protocol filtering and escaping. * * @dataProvider providerTestFilterBadProtocol * @covers ::setAllowedProtocols * @covers ::filterBadProtocol * * @param string $uri * Protocol URI. * @param string $expected * Expected escaped value. * @param array $protocols * Protocols to allow. */ public function testFilterBadProtocol($uri, $expected, $protocols) { UrlHelper::setAllowedProtocols($protocols); $filtered = UrlHelper::filterBadProtocol($uri); $this->assertEquals($expected, $filtered); }
/** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); // Custom variables validation. foreach ($form_state->getValue(['piwik_custom_var', 'slots']) as $custom_var) { $form_state->setValue(['piwik_custom_var', 'slots', $custom_var['slot'], 'name'], trim($custom_var['name'])); $form_state->setValue(['piwik_custom_var', 'slots', $custom_var['slot'], 'value'], trim($custom_var['value'])); // Validate empty names/values. if (empty($custom_var['name']) && !empty($custom_var['value'])) { $form_state->setErrorByName("piwik_custom_var][slots][" . $custom_var['slot'] . "][name", t('The custom variable @slot-number requires a <em>Name</em> if a <em>Value</em> has been provided.', ['@slot-number' => $custom_var['slot']])); } elseif (!empty($custom_var['name']) && empty($custom_var['value'])) { $form_state->setErrorByName("piwik_custom_var][slots][" . $custom_var['slot'] . "][value", t('The custom variable @slot-number requires a <em>Value</em> if a <em>Name</em> has been provided.', ['@slot-number' => $custom_var['slot']])); } } $form_state->setValue('piwik_custom_var', $form_state->getValue(['piwik_custom_var', 'slots'])); // Trim some text area values. $form_state->setValue('piwik_site_id', trim($form_state->getValue('piwik_site_id'))); $form_state->setValue('piwik_visibility_request_path_pages', trim($form_state->getValue('piwik_visibility_request_path_pages'))); $form_state->setValue('piwik_codesnippet_before', trim($form_state->getValue('piwik_codesnippet_before'))); $form_state->setValue('piwik_codesnippet_after', trim($form_state->getValue('piwik_codesnippet_after'))); $form_state->setValue('piwik_visibility_user_role_roles', array_filter($form_state->getValue('piwik_visibility_user_role_roles'))); $form_state->setValue('piwik_trackmessages', array_filter($form_state->getValue('piwik_trackmessages'))); if (!preg_match('/^\\d{1,}$/', $form_state->getValue('piwik_site_id'))) { $form_state->setErrorByName('piwik_site_id', t('A valid Piwik site ID is an integer only.')); } $url = $form_state->getValue('piwik_url_http') . 'piwik.php'; try { $result = \Drupal::httpClient()->get($url); if ($result->getStatusCode() != 200 && $form_state->getValue('piwik_url_skiperror') == FALSE) { $form_state->setErrorByName('piwik_url_http', t('The validation of "@url" failed with error "@error" (HTTP code @code).', ['@url' => UrlHelper::filterBadProtocol($url), '@error' => $result->getReasonPhrase(), '@code' => $result->getStatusCode()])); } } catch (RequestException $exception) { $form_state->setErrorByName('piwik_url_http', t('The validation of "@url" failed with an exception "@error" (HTTP code @code).', ['@url' => UrlHelper::filterBadProtocol($url), '@error' => $exception->getMessage(), '@code' => $exception->getCode()])); } $piwik_url_https = $form_state->getValue('piwik_url_https'); if (!empty($piwik_url_https)) { $url = $piwik_url_https . 'piwik.php'; try { $result = \Drupal::httpClient()->get($url); if ($result->getStatusCode() != 200 && $form_state->getValue('piwik_url_skiperror') == FALSE) { $form_state->setErrorByName('piwik_url_https', t('The validation of "@url" failed with error "@error" (HTTP code @code).', ['@url' => UrlHelper::filterBadProtocol($url), '@error' => $result->getReasonPhrase(), '@code' => $result->getStatusCode()])); } } catch (RequestException $exception) { $form_state->setErrorByName('piwik_url_https', t('The validation of "@url" failed with an exception "@error" (HTTP code @code).', ['@url' => UrlHelper::filterBadProtocol($url), '@error' => $exception->getMessage(), '@code' => $exception->getCode()])); } } // Verify that every path is prefixed with a slash, but don't check PHP // code snippets. if ($form_state->getValue('piwik_visibility_request_path_mode') != 2) { $pages = preg_split('/(\\r\\n?|\\n)/', $form_state->getValue('piwik_visibility_request_path_pages')); foreach ($pages as $page) { if (strpos($page, '/') !== 0 && $page !== '<front>') { $form_state->setErrorByName('piwik_visibility_request_path_pages', t('Path "@page" not prefixed with slash.', ['@page' => $page])); // Drupal forms show one error only. break; } } } // Clear obsolete local cache if cache has been disabled. if ($form_state->isValueEmpty('piwik_cache') && $form['advanced']['piwik_cache']['#default_value']) { piwik_clear_js_cache(); } // This is for the Newbie's who cannot read a text area description. if (preg_match('/(.*)<\\/?script(.*)>(.*)/i', $form_state->getValue('piwik_codesnippet_before'))) { $form_state->setErrorByName('piwik_codesnippet_before', t('Do not include the <script> tags in the javascript code snippets.')); } if (preg_match('/(.*)<\\/?script(.*)>(.*)/i', $form_state->getValue('piwik_codesnippet_after'))) { $form_state->setErrorByName('piwik_codesnippet_after', t('Do not include the <script> tags in the javascript code snippets.')); } }