diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 60cbd2f..b74828f 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -72,6 +72,9 @@ function filter_theme() { 'filter_guidelines' => array( 'variables' => array('format' => NULL), ), + 'filter_html_image_secure_image' => array( + 'variables' => array('image' => NULL), + ), ); } @@ -1778,52 +1781,70 @@ function _filter_html_escape_tips($filter, $format, $long = FALSE) { function _filter_html_image_secure_process($text) { // Find the path (e.g. '/') to Drupal root. $base_path = base_path(); - $base_path_length = strlen($base_path); + $base_path_length = drupal_strlen($base_path); // Find the directory on the server where index.php resides. $local_dir = DRUPAL_ROOT . '/'; - $alt_text = t('Image removed.'); - $title_text = t('This image has been removed. For security reasons, only images from the local domain are allowed.'); - $html_dom = filter_dom_load($text); $images = $html_dom->getElementsByTagName('img'); foreach ($images as $image) { $src = $image->getAttribute('src'); - // Remove absolute URLs pointed to the local domain, in order to prevent - // mixed content errors. + // Remove absolute URLs pointing to the local domain to prevent mixed + // content errors. $image->setAttribute('src', preg_replace('|^https?://' . $_SERVER['HTTP_HOST'] . '|', '', $src)); - // Verify $src starts with $base_path. This also ensures that external - // images can't be referenced because $base_path always starts with a - // slash. Another insurance against that will be prefixing the image - // source with the Drupal root before running getimagesize() on it. + // Verify that $src starts with $base_path. + // This also ensures that external images cannot be referenced. $src = $image->getAttribute('src'); - if (substr($src, 0, $base_path_length) == $base_path) { - // Remove the base path() to get the path relative to the Drupal root. - // Also, make sure we're dealing with a real image here. - $local_image_path = $local_dir . substr($src, $base_path_length); + if (drupal_substr($src, 0, $base_path_length) === $base_path) { + // Remove the $base_path to get the path relative to the Drupal root. + // Ensure the path refers to an actual image by prefixing the image source + // with the Drupal root and running getimagesize() on it. + $local_image_path = $local_dir . drupal_substr($src, $base_path_length); if (@getimagesize($local_image_path)) { - // The image has the right path, nothing to do. Erroneous images - // are dealt with below. + // The image has the right path. Erroneous images are dealt with below. continue; } } - // Replace invalid images with an error indicator. - $image->setAttribute('src', $base_path . 'core/misc/message-16-error.png'); - $image->setAttribute('alt', $alt_text); - $image->setAttribute('title', $title_text); + // Replace an invalid image with an error indicator. + theme('filter_html_image_secure_image', array('image' => $image)); } $text = filter_dom_serialize($html_dom); - return $text; } /** + * Formats an image DOM element that has an invalid source. + * + * @param DOMElement $image + * An IMG node to format, parsed from the filtered text. + * + * @return void + * Unlike other theme functions, the passed in $image is altered by reference. + * + * @see _filter_html_image_secure_process() + * @ingroup themeable + */ +function theme_filter_html_image_secure_image(&$variables) { + $image = $variables['image']; + + // Turn an invalid image into an error indicator. + $image->setAttribute('src', base_path() . 'core/misc/message-16-error.png'); + $image->setAttribute('alt', t('Image removed.')); + $image->setAttribute('title', t('This image has been removed. For security reasons, only images from the local domain are allowed.')); + + // Add a CSS class to aid in styling. + $class = ($image->getAttribute('class') ? trim($image->getAttribute('class')) . ' ' : ''); + $class .= 'filter-image-invalid'; + $image->setAttribute('class', $class); +} + +/** * Filter tips callback for secure HTML image filter. */ function _filter_html_image_secure_tips($filter, $format, $long = FALSE) { - return t('Only images hosted on this site may be used in <img> tags. Others are replaced with an error icon.'); + return t('Only images hosted on this site may be used in <img> tags.'); } /** diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php index 62fc83c..a7e38ac 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php @@ -63,6 +63,7 @@ function setUp() { 'skip comment approval', filter_permission_name($filtered_html_format), )); + $this->drupalLogin($this->web_user); // Setup a node to comment and test on. $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); @@ -75,8 +76,6 @@ function setUp() { function testImageSource() { global $base_url; - $this->drupalLogin($this->web_user); - $public_files_path = variable_get('file_public_path', conf_path() . '/files'); $http_base_url = preg_replace('/^https?/', 'http', $base_url); @@ -93,7 +92,9 @@ function testImageSource() { $test_images = $this->drupalGetTestFiles('image'); $test_image = $test_images[0]->filename; - // keys become the value of img src, value is what the filter should convert them to. + // Create a list of test image sources. + // The keys become the value of the IMG 'src' attribute, the values are the + // expected filter conversions. $images = array( $http_base_url . '/' . $druplicon => base_path() . $druplicon, $https_base_url . '/' . $druplicon => base_path() . $druplicon, @@ -109,10 +110,10 @@ function testImageSource() { ); $comment = array(); foreach ($images as $image => $converted) { - // Include the image source for debugging. - $comment[] = $image; - // Hash image for the test attribute because it might contain characters - // confusing xpath. + // Output the image source as plain text for debugging. + $comment[] = $image . ':'; + // Hash the image source in a custom test attribute, because it might + // contain characters that confuse XPath. $comment[] = ''; } $edit = array( @@ -132,7 +133,7 @@ function testImageSource() { $this->assertEqual((string) $element['src'], $converted); } } - $this->assertTrue($found, t('@image was found.', array('@image' => $image))); + $this->assertTrue($found, format_string('@image was found.', array('@image' => $image))); } } }