Index: modules/filter/filter.install =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.install,v retrieving revision 1.9 diff -u -p -r1.9 filter.install --- modules/filter/filter.install 14 Apr 2008 17:48:37 -0000 1.9 +++ modules/filter/filter.install 23 Oct 2008 17:20:53 -0000 @@ -99,6 +99,57 @@ function filter_schema() { } /** + * Implementation of hook_requirements(). + * + * Display error messages for sites which allow anonymous users + * (or authenticated users with open registration enabled) + * to use a format without the HTML filter. + */ +function filter_requirements($phase) { + if ($phase == 'runtime') { + $requirements = array(); + $formats = filter_formats(); + $insecure = array(); + $filters = array(); + $result = db_query("SELECT fid, format FROM {filters} WHERE module = 'filter' AND delta = 0"); + while ($filter = db_fetch_object($result)) { + $filters[$filter->format] = $filter->fid; + } + foreach ($formats as $id => $format) { + if (!isset($filters[$format->format])) { + if (variable_get('filter_default_format', 1) == $format->format) { + $insecure['default'] = 1; + } + if (strstr($format->roles, ",1,")) { + $insecure['anonymous'] = 1; + } + if (strstr($format->roles, ",2,") && variable_get('user_register', 1) > 0) { + $insecure['authenticated'] = 1; + } + } + } + if (count($insecure)) { + if (isset($insecure['default'])) { + $description = t('The default input format does not filter HTML tags.') . ' '; + } + if (isset($insecure['anonymous'])) { + $description .= t('Anonymous users have access to a format which does not filter HTML tags.') . ' '; + } + if (isset($insecure['authenticated'])) { + $description .= t('Authenticated users have access to a format which does not filter HTML tags and user registration is open.') . ' '; + } + $requirements['filter'] = array( + 'title' => t('HTML Filtration'), + 'value' => t('Insecure'), + 'description' => $description . t('This configuration creates a security vulnerability. You should modify your filters and set the default to Filtered HTML.', array('@input-filters' => url('admin/settings/filters'))), + 'severity' => REQUIREMENT_ERROR, + ); + } + return $requirements; + } +} + +/** * Add a weight column to the filter formats table. */ function filter_update_7000() { Index: modules/filter/filter.test =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v retrieving revision 1.8 diff -u -p -r1.8 filter.test --- modules/filter/filter.test 12 Oct 2008 04:30:06 -0000 1.8 +++ modules/filter/filter.test 23 Oct 2008 17:20:53 -0000 @@ -236,3 +236,113 @@ class FilterTestCase extends DrupalWebTe } } } + +class FilterWarningTestCase extends DrupalWebTestCase { + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('Filter Security Warning'), + 'description' => t('Test the warning messages for insecure HTML filter configurations.'), + 'group' => t('Filter'), + ); + } + + /** + * Implementation of setUp(). + */ + function setUp() { + parent::setUp(); + + $admin_user = $this->drupalCreateUser(array('administer filters', 'administer site configuration')); + $this->drupalLogin($admin_user); + } + + /** + * Set the default format to contain / not contain the HTML filter and check for error messages. + */ + function testSecureHTMLDefault() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + + // Create a format which filters HTML + $safe_format_auth = $this->createFormat(0, 2); + + // Create a format which converts line breaks but does not filter HTML + $unsafe_format_auth = $this->createFormat(1, 2); + + variable_set('filter_default_format', $safe_format_auth); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('The default input format does not filter HTML tags.'), t('No default HTML filter security error shown on status page when default format filters HTML.')); + + variable_set('filter_default_format', $unsafe_format_auth); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('The default input format does not filter HTML tags.'), t('Default HTML filter security error shown on status page when default format does not filter HTML.')); + } + + /** + * Give / remove anonymous users access to a format without the HTML filter + * and check for error messages. + */ + function testSecureHTMLAnonymous() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + + // Create a format which filters HTML for anonymous users + $safe_format_anon = $this->createFormat(0, 1); + variable_set('filter_default_format', $safe_format_anon); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Anonymous users have access to a format which does not filter HTML tags.'), t('No anonymous user HTML filter security error shown on status page when inapplicable.')); + + + // Add a format which does not filter HTML for anonymous users + $unsafe_format_anon = $this->createFormat(1, 1); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Anonymous users have access to a format which does not filter HTML tags.'), t('Anonymous user HTML filter security error shown on status page when applicable.')); + } + + /** + * Give / remove authenticated users access to a format without the HTML filter + * when user registration is open and check for error messages. + */ + function testSecureHTMLOpenAuthenticated() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + variable_set('user_register', 1); + + // Create a format which filters HTML + $safe_format_auth = $this->createFormat(0, 2); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Authenticated users have access to a format which does not filter HTML tags and user registration is open.'), t('Authenticated user HTML filter security error not shown on status page when inapplicable.')); + + // Create a format which converts line breaks but does not filter HTML + $unsafe_format_auth = $this->createFormat(1, 2); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Authenticated users have access to a format which does not filter HTML tags and user registration is open.'), t('Authenticated user HTML filter security error shown on status page when applicable.')); + + variable_set('user_register', 0); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Authenticated users have access to a format which does not filter HTML tags and user registration is open.'), t('Authenticated user HTML filter security error not shown on status page when inapplicable.')); + + // When user registration is open but approval needed, error also shows + variable_set('user_register', 2); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Authenticated users have access to a format which does not filter HTML tags and user registration is open.'), t('Authenticated user HTML filter security error shown on status page when applicable.')); + } + + /** + * Given a filter number and a role id, + * create a new test format and return its format id. + */ + function createFormat($filter, $role) { + $edit = array( + 'name' => $this->randomName(), + 'roles[' . $role .']' => TRUE, + 'filters[filter/' . $filter . ']' => TRUE, + ); + $this->drupalPost('admin/settings/filters/add', $edit, t('Save configuration')); + return db_result(db_query("SELECT format FROM {filter_formats} WHERE name = '%s'", $edit['name'])); + } +} + Index: modules/php/php.install =================================================================== RCS file: /cvs/drupal/drupal/modules/php/php.install,v retrieving revision 1.2 diff -u -p -r1.2 php.install --- modules/php/php.install 14 Apr 2008 17:48:41 -0000 1.2 +++ modules/php/php.install 23 Oct 2008 17:20:53 -0000 @@ -27,3 +27,54 @@ function php_install() { function php_disable() { drupal_set_message(t('The PHP module has been disabled. Please note that any existing content that was using the PHP filter will now be visible in plain text. This might pose a security risk by exposing sensitive information, if any, used in the PHP code.')); } + +/** + * Implementation of hook_requirements(). + * + * Display error messages for sites which allow anonymous users + * (or authenticated users with open registration enabled) + * to use a format with the PHP filter. + */ +function php_requirements($phase) { + if ($phase == 'runtime') { + $requirements = array(); + $formats = filter_formats(); + $insecure = array(); + $filters = array(); + $result = db_query("SELECT fid, format FROM {filters} WHERE module = 'filter' AND delta = 0"); + while ($filter = db_fetch_object($result)) { + $filters[$filter->format] = $filter->fid; + } + foreach ($formats as $id => $format) { + if (!isset($filters[$format->format])) { + if (variable_get('filter_default_format', 1) == $format->format) { + $insecure['default'] = 1; + } + if (strstr($format->roles, ",1,")) { + $insecure['anonymous'] = 1; + } + if (strstr($format->roles, ",2,") && variable_get('user_register', 1) > 0) { + $insecure['authenticated'] = 1; + } + } + } + if (count($insecure)) { + if (isset($insecure['default'])) { + $description = t('The default input format allows PHP evaluation.') . ' '; + } + if (isset($insecure['anonymous'])) { + $description .= t('Anonymous users have access to a format which allows PHP evaluation.') . ' '; + } + if (isset($insecure['authenticated'])) { + $description .= t('Authenticated users have access to a format which allows PHP evaluation and user registration is open.') . ' '; + } + $requirements['php_filter'] = array( + 'title' => t('PHP Evaluator Access'), + 'value' => t('Insecure'), + 'description' => $description . t('This configuration creates a security vulnerability. You should modify your filters to restrict access to the PHP filter.', array('@input-filters' => url('admin/settings/filters'))), + 'severity' => REQUIREMENT_ERROR, + ); + } + return $requirements; + } +} Index: modules/php/php.test =================================================================== RCS file: /cvs/drupal/drupal/modules/php/php.test,v retrieving revision 1.5 diff -u -p -r1.5 php.test --- modules/php/php.test 20 Oct 2008 13:04:27 -0000 1.5 +++ modules/php/php.test 23 Oct 2008 17:20:53 -0000 @@ -115,4 +115,113 @@ class PHPAccessTestCase extends PHPTestC $this->drupalGet('node/' . $node->nid . '/edit'); $this->assertNoFieldByName('body_format', '3', t('Format not available.')); } -} \ No newline at end of file +} + + +class PHPWarningTestCase extends DrupalWebTestCase { + + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('PHP Filter Security Warning'), + 'description' => t('Test the warning messages for insecure PHP filter configurations.'), + 'group' => t('PHP'), + ); + } + + /** + * Implementation of setUp(). + */ + function setUp() { + parent::setUp('php'); + $admin_user = $this->drupalCreateUser(array('administer filters', 'administer site configuration')); + $this->drupalLogin($admin_user); + } + + /** + * Set the default format to contain / not contain the PHP filter and check for error messages. + */ + function testSecurePHPDefault() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + + // Create a format which does not evaluate PHP + $safe_format_auth = $this->createFormat(0, 2); + + // Create a format which evaluates PHP + $unsafe_format_auth = $this->createFormat(3, 2); + variable_set('filter_default_format', $safe_format_auth); + + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('The default input format allows PHP evaluation.'), t('No default PHP filter security error shown on status page when default format evaluates PHP.')); + variable_set('filter_default_format', $unsafe_format_auth); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('The default input format allows PHP evaluation.'), t('Default PHP filter security error shown on status page when default format evaluates PHP.')); + } + + /** + * Give / remove anonymous users access to a format with the PHP filter + * and check for error messages. + */ + function testSecurePHPAnonymous() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + + // Create a format which does not evaluate PHP for anonymous users + $safe_format_anon = $this->createFormat(0, 1); + variable_set('filter_default_format', $safe_format_anon); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Anonymous users have access to a format which allows PHP evaluation.'), t('No anonymous user PHP filter security error shown on status page when inapplicable.')); + + + // Add a format which evaluates PHP for anonymous users + $unsafe_format_anon = $this->createFormat(3, 1); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Anonymous users have access to a format which allows PHP evaluation.'), t('Anonymous user PHP filter security error shown on status page when applicable.')); + } + + /** + * Give / remove authenticated users access to a format with the PHP filter + * when user registration is open and check for error messages. + */ + function testSecurePHPOpenAuthenticated() { + // Start fresh with all new formats for testing + db_query("DELETE FROM {filter_formats}"); + variable_set('user_register', 1); + + // Create a format which does not filter PHP + $safe_format_auth = $this->createFormat(0, 2); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Authenticated users have access to a format which allows PHP evaluation and user registration is open.'), t('Authenticated user PHP filter security error not shown on status page when inapplicable.')); + + // Create a format which evaluates PHP for authenticated users + $unsafe_format_auth = $this->createFormat(3, 2); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Authenticated users have access to a format which allows PHP evaluation and user registration is open.'), t('Authenticated user PHP filter security error shown on status page when applicable.')); + + variable_set('user_register', 0); + $this->drupalGet('admin/reports/status'); + $this->assertNoText(t('Authenticated users have access to a format which allows PHP evaluation and user registration is open.'), t('Authenticated user PHP filter security error not shown on status page when inapplicable.')); + + // When user registration is open but approval needed, error also shows + variable_set('user_register', 2); + $this->drupalGet('admin/reports/status'); + $this->assertText(t('Authenticated users have access to a format which allows PHP evaluation and user registration is open.'), t('Authenticated user PHP filter security error shown on status page when applicable.')); + } + + /** + * Given a filter number and a role id, + * create a new test format and return its format id. + */ + function createFormat($filter, $role) { + $edit = array( + 'name' => $this->randomName(), + 'roles[' . $role .']' => TRUE, + 'filters[filter/' . $filter . ']' => TRUE, + ); + $this->drupalPost('admin/settings/filters/add', $edit, t('Save configuration')); + return db_result(db_query("SELECT format FROM {filter_formats} WHERE name = '%s'", $edit['name'])); + } +}