diff options
author | David Rothstein <drothstein@gmail.com> | 2013-11-20 15:45:59 -0500 |
---|---|---|
committer | David Rothstein <drothstein@gmail.com> | 2013-11-20 15:45:59 -0500 |
commit | 782d1155c62c0a879bf587c7e40c3a13bcf6879c (patch) | |
tree | 380060c81a7ebd76870cfd7fb566933b3a7c6efd /modules | |
parent | bf704d6ffe55d66a440a55a9d43e8846d46d2440 (diff) | |
download | brdo-782d1155c62c0a879bf587c7e40c3a13bcf6879c.tar.gz brdo-782d1155c62c0a879bf587c7e40c3a13bcf6879c.tar.bz2 |
Drupal 7.24
Diffstat (limited to 'modules')
-rw-r--r-- | modules/color/color.module | 49 | ||||
-rw-r--r-- | modules/image/image.field.inc | 2 | ||||
-rw-r--r-- | modules/openid/openid.inc | 23 | ||||
-rw-r--r-- | modules/openid/openid.test | 7 | ||||
-rw-r--r-- | modules/openid/tests/openid_test.install | 2 | ||||
-rw-r--r-- | modules/overlay/overlay.module | 4 | ||||
-rw-r--r-- | modules/simpletest/tests/file.test | 2 | ||||
-rw-r--r-- | modules/simpletest/tests/form.test | 20 | ||||
-rw-r--r-- | modules/system/system.install | 37 | ||||
-rw-r--r-- | modules/system/system.test | 47 | ||||
-rw-r--r-- | modules/user/user.module | 6 | ||||
-rw-r--r-- | modules/user/user.pages.inc | 2 |
12 files changed, 168 insertions, 33 deletions
diff --git a/modules/color/color.module b/modules/color/color.module index 53c54fbf6..5b441aabd 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -240,6 +240,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) { $form['palette'][$name] = array( '#type' => 'textfield', '#title' => check_plain($names[$name]), + '#value_callback' => 'color_palette_color_value', '#default_value' => $value, '#size' => 8, ); @@ -295,6 +296,52 @@ function theme_color_scheme_form($variables) { } /** + * Determines the value for a palette color field. + * + * @param $element + * The form element whose value is being populated. + * @param $input + * The incoming input to populate the form element. If this is FALSE, + * the element's default value should be returned. + * @param $form_state + * A keyed array containing the current state of the form. + * + * @return + * The data that will appear in the $form_state['values'] collection for this + * element. Return nothing to use the default. + */ +function color_palette_color_value($element, $input = FALSE, $form_state = array()) { + // If we suspect a possible cross-site request forgery attack, only accept + // hexadecimal CSS color strings from user input, to avoid problems when this + // value is used in the JavaScript preview. + if ($input !== FALSE) { + // Start with the provided value for this textfield, and validate that if + // necessary, falling back on the default value. + $value = form_type_textfield_value($element, $input, $form_state); + if (!$value || !isset($form_state['complete form']['#token']) || color_valid_hexadecimal_string($value) || drupal_valid_token($form_state['values']['form_token'], $form_state['complete form']['#token'])) { + return $value; + } + else { + return $element['#default_value']; + } + } +} + +/** + * Determines if a hexadecimal CSS color string is valid. + * + * @param $color + * The string to check. + * + * @return + * TRUE if the string is a valid hexadecimal CSS color string, or FALSE if it + * isn't. + */ +function color_valid_hexadecimal_string($color) { + return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color); +} + +/** * Form validation handler for color_scheme_form(). * * @see color_scheme_form_submit() @@ -302,7 +349,7 @@ function theme_color_scheme_form($variables) { function color_scheme_form_validate($form, &$form_state) { // Only accept hexadecimal CSS color strings to avoid XSS upon use. foreach ($form_state['values']['palette'] as $key => $color) { - if (!preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color)) { + if (!color_valid_hexadecimal_string($color)) { form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title']))); } } diff --git a/modules/image/image.field.inc b/modules/image/image.field.inc index 23547385d..6d1867cb0 100644 --- a/modules/image/image.field.inc +++ b/modules/image/image.field.inc @@ -351,7 +351,7 @@ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langc if ($field['cardinality'] == 1) { // If there's only one field, return it as delta 0. if (empty($elements[0]['#default_value']['fid'])) { - $elements[0]['#description'] = theme('file_upload_help', array('description' => $instance['description'], 'upload_validators' => $elements[0]['#upload_validators'])); + $elements[0]['#description'] = theme('file_upload_help', array('description' => field_filter_xss($instance['description']), 'upload_validators' => $elements[0]['#upload_validators'])); } } else { diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc index 74a08d576..d7ef663b4 100644 --- a/modules/openid/openid.inc +++ b/modules/openid/openid.inc @@ -380,6 +380,9 @@ function _openid_parse_message($message) { /** * Return a nonce value - formatted per OpenID spec. + * + * NOTE: This nonce is not cryptographically secure and only suitable for use + * by the test framework. */ function _openid_nonce() { // YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters. @@ -549,7 +552,7 @@ function _openid_dh_rand($stop) { } do { - $bytes = "\x00" . _openid_get_bytes($nbytes); + $bytes = "\x00" . drupal_random_bytes($nbytes); $n = _openid_dh_binary_to_long($bytes); // Keep looping if this value is in the low duplicated range. } while (_openid_math_cmp($n, $duplicate) < 0); @@ -558,23 +561,7 @@ function _openid_dh_rand($stop) { } function _openid_get_bytes($num_bytes) { - $f = &drupal_static(__FUNCTION__); - $bytes = ''; - if (!isset($f)) { - $f = @fopen(OPENID_RAND_SOURCE, "r"); - } - if (!$f) { - // pseudorandom used - $bytes = ''; - for ($i = 0; $i < $num_bytes; $i += 4) { - $bytes .= pack('L', mt_rand()); - } - $bytes = substr($bytes, 0, $num_bytes); - } - else { - $bytes = fread($f, $num_bytes); - } - return $bytes; + return drupal_random_bytes($num_bytes); } function _openid_response($str = NULL) { diff --git a/modules/openid/openid.test b/modules/openid/openid.test index 292c5317c..41af3f82f 100644 --- a/modules/openid/openid.test +++ b/modules/openid/openid.test @@ -695,13 +695,6 @@ class OpenIDTestCase extends DrupalWebTestCase { } /** - * Test _openid_get_bytes(). - */ - function testOpenidGetBytes() { - $this->assertEqual(strlen(_openid_get_bytes(20)), 20, '_openid_get_bytes() returned expected result.'); - } - - /** * Test _openid_signature(). */ function testOpenidSignature() { diff --git a/modules/openid/tests/openid_test.install b/modules/openid/tests/openid_test.install index 3bd4978f1..d30e2dc4d 100644 --- a/modules/openid/tests/openid_test.install +++ b/modules/openid/tests/openid_test.install @@ -13,5 +13,5 @@ function openid_test_install() { // Generate a MAC key (Message Authentication Code) used for signing messages. // The variable is base64-encoded, because variables cannot contain non-UTF-8 // data. - variable_set('openid_test_mac_key', base64_encode(_openid_get_bytes(20))); + variable_set('openid_test_mac_key', drupal_random_key(20)); } diff --git a/modules/overlay/overlay.module b/modules/overlay/overlay.module index 728198680..7b2fc9393 100644 --- a/modules/overlay/overlay.module +++ b/modules/overlay/overlay.module @@ -146,6 +146,10 @@ function overlay_init() { // If this page shouldn't be rendered inside the overlay, redirect to the // parent. elseif (!path_is_admin($current_path)) { + // Prevent open redirects by ensuring the current path is not an absolute URL. + if (url_is_external($current_path)) { + $current_path = '<front>'; + } overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render')))); } diff --git a/modules/simpletest/tests/file.test b/modules/simpletest/tests/file.test index 0f2cdb64b..7802be3f2 100644 --- a/modules/simpletest/tests/file.test +++ b/modules/simpletest/tests/file.test @@ -952,7 +952,7 @@ class FileDirectoryTest extends FileTestCase { $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), 'Successfully re-created the .htaccess file in the files directory.', 'File'); // Verify contents of .htaccess file. $file = file_get_contents(file_default_scheme() . '://.htaccess'); - $this->assertEqual($file, "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks", 'The .htaccess file contains the proper content.', 'File'); + $this->assertEqual($file, file_htaccess_lines(FALSE), 'The .htaccess file contains the proper content.', 'File'); } /** diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index a1506ccdc..8b63be4fc 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -82,6 +82,10 @@ class FormsTestCase extends DrupalWebTestCase { $form_state['input'][$element] = $empty; $form_state['input']['form_id'] = $form_id; $form_state['method'] = 'post'; + + // The form token CSRF protection should not interfere with this test, + // so we bypass it by marking this test form as programmed. + $form_state['programmed'] = TRUE; drupal_prepare_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state); $errors = form_get_errors(); @@ -614,6 +618,18 @@ class FormValidationTestCase extends DrupalWebTestCase { $this->drupalPost(NULL, array(), 'Save'); $this->assertNoFieldByName('name', 'Form element was hidden.'); $this->assertText('Name value: element_validate_access', 'Value for inaccessible form element exists.'); + + // Verify that #validate handlers don't run if the CSRF token is invalid. + $this->drupalLogin($this->drupalCreateUser()); + $this->drupalGet('form-test/validate'); + $edit = array( + 'name' => 'validate', + 'form_token' => 'invalid token' + ); + $this->drupalPost(NULL, $edit, 'Save'); + $this->assertNoFieldByName('name', '#value changed by #validate', 'Form element #value was not altered.'); + $this->assertNoText('Name value: value changed by form_set_value() in #validate', 'Form element value in $form_state was not altered.'); + $this->assertText('The form has become outdated. Copy any unsaved work in the form below'); } /** @@ -941,6 +957,10 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { $form_state['input'] = $edit; $form_state['input']['form_id'] = $form_id; + // The form token CSRF protection should not interfere with this test, + // so we bypass it by marking this test form as programmed. + $form_state['programmed'] = TRUE; + drupal_prepare_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state); diff --git a/modules/system/system.install b/modules/system/system.install index a58e855ad..afe4ebc0e 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -258,6 +258,39 @@ function system_requirements($phase) { $requirements['settings.php']['title'] = $t('Configuration file'); } + // Test the contents of the .htaccess files. + if ($phase == 'runtime') { + // Try to write the .htaccess files first, to prevent false alarms in case + // (for example) the /tmp directory was wiped. + file_ensure_htaccess(); + $htaccess_files['public://.htaccess'] = array( + 'title' => $t('Public files directory'), + 'directory' => variable_get('file_public_path', conf_path() . '/files'), + ); + if ($private_files_directory = variable_get('file_private_path')) { + $htaccess_files['private://.htaccess'] = array( + 'title' => $t('Private files directory'), + 'directory' => $private_files_directory, + ); + } + $htaccess_files['temporary://.htaccess'] = array( + 'title' => $t('Temporary files directory'), + 'directory' => variable_get('file_temporary_path', file_directory_temp()), + ); + foreach ($htaccess_files as $htaccess_file => $info) { + // Check for the string which was added to the recommended .htaccess file + // in the latest security update. + if (!file_exists($htaccess_file) || !($contents = @file_get_contents($htaccess_file)) || strpos($contents, 'Drupal_Security_Do_Not_Remove_See_SA_2013_003') === FALSE) { + $requirements[$htaccess_file] = array( + 'title' => $info['title'], + 'value' => $t('Not fully protected'), + 'severity' => REQUIREMENT_ERROR, + 'description' => $t('See <a href="@url">@url</a> for information about the recommended .htaccess file which should be added to the %directory directory to help protect against arbitrary code execution.', array('@url' => 'http://drupal.org/SA-CORE-2013-003', '%directory' => $info['directory'])), + ); + } + } + } + // Report cron status. if ($phase == 'runtime') { // Cron warning threshold defaults to two days. @@ -516,7 +549,7 @@ function system_install() { ->execute(); // Populate the cron key variable. - $cron_key = drupal_hash_base64(drupal_random_bytes(55)); + $cron_key = drupal_random_key(); variable_set('cron_key', $cron_key); } @@ -1743,7 +1776,7 @@ function system_update_7000() { * Generate a cron key and save it in the variables table. */ function system_update_7001() { - variable_set('cron_key', drupal_hash_base64(drupal_random_bytes(55))); + variable_set('cron_key', drupal_random_key()); } /** diff --git a/modules/system/system.test b/modules/system/system.test index 99e0cbe95..f4fb047d1 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -2712,3 +2712,50 @@ class TokenScanTest extends DrupalWebTestCase { } } +/** + * Test case for drupal_valid_token(). + */ +class SystemValidTokenTest extends DrupalUnitTestCase { + + /** + * Flag to indicate whether PHP error reportings should be asserted. + * + * @var bool + */ + protected $assertErrors = TRUE; + + public static function getInfo() { + return array( + 'name' => 'Token validation', + 'description' => 'Test the security token validation.', + 'group' => 'System', + ); + } + + /** + * Tests invalid invocations of drupal_valid_token() that must return FALSE. + */ + public function testTokenValidation() { + // The following checks will throw PHP notices, so we disable error + // assertions. + $this->assertErrors = FALSE; + $this->assertFalse(drupal_valid_token(NULL, new stdClass()), 'Token NULL, value object returns FALSE.'); + $this->assertFalse(drupal_valid_token(0, array()), 'Token 0, value array returns FALSE.'); + $this->assertFalse(drupal_valid_token('', array()), "Token '', value array returns FALSE."); + $this->assertFalse('' === drupal_get_token(array()), 'Token generation does not return an empty string on invalid parameters.'); + $this->assertErrors = TRUE; + + $this->assertFalse(drupal_valid_token(TRUE, 'foo'), 'Token TRUE, value foo returns FALSE.'); + $this->assertFalse(drupal_valid_token(0, 'foo'), 'Token 0, value foo returns FALSE.'); + } + + /** + * Overrides DrupalTestCase::errorHandler(). + */ + public function errorHandler($severity, $message, $file = NULL, $line = NULL) { + if ($this->assertErrors) { + return parent::errorHandler($severity, $message, $file, $line); + } + return TRUE; + } +} diff --git a/modules/user/user.module b/modules/user/user.module index 512420706..7227a1e74 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -717,10 +717,14 @@ function user_password($length = 10) { // Loop the number of times specified by $length. for ($i = 0; $i < $length; $i++) { + do { + // Find a secure random number within the range needed. + $index = ord(drupal_random_bytes(1)); + } while ($index > $len); // Each iteration, pick a random character from the // allowable string and append it to the password: - $pass .= $allowable_characters[mt_rand(0, $len)]; + $pass .= $allowable_characters[$index]; } return $pass; diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 4cdbc40fa..c14548cf4 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -137,7 +137,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp)); drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.')); // Let the user's password be changed without the current password check. - $token = drupal_hash_base64(drupal_random_bytes(55)); + $token = drupal_random_key(); $_SESSION['pass_reset_' . $user->uid] = $token; drupal_goto('user/' . $user->uid . '/edit', array('query' => array('pass-reset-token' => $token))); } |