Index: modules/filter/filter.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.admin.inc,v retrieving revision 1.42 diff -u -p -r1.42 filter.admin.inc --- modules/filter/filter.admin.inc 28 Aug 2009 16:23:04 -0000 1.42 +++ modules/filter/filter.admin.inc 30 Aug 2009 18:52:13 -0000 @@ -112,6 +112,9 @@ function filter_admin_format_page($forma * @see filter_admin_format_form_submit() */ function filter_admin_format_form(&$form_state, $format) { + if (!empty($format->format) && filter_format_is_insecure($format->format)) { + drupal_set_message(t('This text format is configured insecurely.'), 'error', FALSE); + } $default = ($format->format == variable_get('filter_default_format', 1)); if ($default) { $help = t('All roles for the default format must be enabled and cannot be changed.'); Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.287 diff -u -p -r1.287 filter.module --- modules/filter/filter.module 30 Aug 2009 06:04:09 -0000 1.287 +++ modules/filter/filter.module 30 Aug 2009 18:52:14 -0000 @@ -15,6 +15,18 @@ define('FILTER_FORMAT_DEFAULT', 0); /** + * Status which indicates that a filter allows XSS attacks even if the content + * passed in to the filter was previously sanitized. + */ +define('FILTER_XSS_ALLOWS', 1); + +/** + * Status which indicates that a filter prevents XSS attacks even if the + * content passed in to the filter was previously unsanitized. + */ +define('FILTER_XSS_PREVENTS', 2); + +/** * Implement hook_help(). */ function filter_help($path, $arg) { @@ -516,6 +528,40 @@ function filter_list_format($format) { } /** + * Determines if a text format is configured insecurely. + * + * @param $format + * The ID of the format to check. + * @return + * TRUE if the format is configured insecurely, FALSE otherwise. + */ +function filter_format_is_insecure($format) { + // All text formats start off as insecure, because unfiltered HTML is by + // definition not safe. + $insecure = TRUE; + $filters = filter_list_format($format); + $filter_info = filter_get_filters(); + foreach ($filters as $name => $filter) { + if (isset($filter_info[$name]['security callback']) && function_exists($filter_info[$name]['security callback'])) { + $callback_function = $filter_info[$name]['security callback']; + $filter_effect = $callback_function($filter); + // If the filter explicitly allows XSS, temporarily mark the format as + // insecure regardless of what filtering was performed by previous + // filters. + if ($filter_effect == FILTER_XSS_ALLOWS) { + $insecure = TRUE; + } + // If the filter explicitly prevents XSS, temporarily mark the format as + // secure regardless of what filtering was performed by previous filters. + elseif ($filter_effect == FILTER_XSS_PREVENTS) { + $insecure = FALSE; + } + } + } + return $insecure; +} + +/** * @name Filtering functions * @{ * Modules which need to have content filtered can use these functions to @@ -736,6 +782,18 @@ function filter_dom_serialize($dom_docum } /** + * Splits a string containing HTML tags into an array of tag names. + * + * @param $tag_string + * A string containing HTML tags; for example, ' '. + * @return + * An array of tag names; for example, array('a', 'em', 'strong'). + */ +function _filter_split_tags($tag_string) { + return preg_split('/\s+|<|>/', $tag_string, -1, PREG_SPLIT_NO_EMPTY); +} + +/** * Format a link to the more extensive filter tips. * * @ingroup themeable @@ -768,6 +826,7 @@ function filter_filter_info() { 'title' => t('Limit allowed HTML tags'), 'description' => t('Allows you to restrict the HTML tags the user can use. It will also remove harmful content such as JavaScript events, JavaScript URLs and CSS styles from those tags that are not removed.'), 'process callback' => '_filter_html', + 'security callback' => '_filter_html_check_security', 'settings callback' => '_filter_html_settings', 'default settings' => array( 'allowed_html' => '
    1. ', @@ -801,6 +860,7 @@ function filter_filter_info() { 'title' => t('Escape all HTML'), 'description' => t('Escapes all HTML tags, so they will be visible instead of being effective.'), 'process callback' => '_filter_html_escape', + 'security callback' => '_filter_html_escape_check_security', 'tips callback' => '_filter_html_escape_tips', ); return $filters; @@ -834,10 +894,21 @@ function _filter_html_settings(&$form_st } /** + * Security callback for the HTML filter. + */ +function _filter_html_check_security($filter) { + $allowed_tags = _filter_split_tags($filter->settings['allowed_html']); + $dangerous_tags = array('object', 'embed'); // Obviously we can expand this later! + if (!array_intersect($dangerous_tags, $allowed_tags)) { + return FILTER_XSS_PREVENTS; + } +} + +/** * HTML filter. Provides filtering of input into accepted HTML. */ function _filter_html($text, $filter) { - $allowed_tags = preg_split('/\s+|<|>/', $filter->settings['allowed_html'], -1, PREG_SPLIT_NO_EMPTY); + $allowed_tags = _filter_split_tags($filter->settings['allowed_html']); $text = filter_xss($text, $allowed_tags); if ($filter->settings['filter_html_nofollow']) { @@ -1000,6 +1071,15 @@ function _filter_html_escape($text) { } /** + * Security callback for the HTML escaping filter. + */ +function _filter_html_escape_check_security($filter) { + // Because this filter escapes all HTML, it prevents XSS attacks from going + // through. + return FILTER_XSS_PREVENTS; +} + +/** * @} End of "Standard filters". */