Commit 782d1155 authored by David_Rothstein's avatar David_Rothstein

Drupal 7.24

parent bf704d6f
Drupal 7.24, 2013-11-20
----------------------
- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
Drupal 7.23, 2013-08-07 Drupal 7.23, 2013-08-07
----------------------- -----------------------
- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module - Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/** /**
* The current system version. * The current system version.
*/ */
define('VERSION', '7.23'); define('VERSION', '7.24');
/** /**
* Core API compatibility. * Core API compatibility.
...@@ -1932,6 +1932,33 @@ function drupal_block_denied($ip) { ...@@ -1932,6 +1932,33 @@ function drupal_block_denied($ip) {
} }
} }
/**
* Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range).
*
* @param $byte_count
* The number of random bytes to fetch and base64 encode.
*
* @return string
* The base64 encoded result will have a length of up to 4 * $byte_count.
*/
function drupal_random_key($byte_count = 32) {
return drupal_base64_encode(drupal_random_bytes($byte_count));
}
/**
* Returns a URL-safe, base64 encoded version of the supplied string.
*
* @param $string
* The string to convert to base64.
*
* @return string
*/
function drupal_base64_encode($string) {
$data = base64_encode($string);
// Modify the output so it's safe to use in URLs.
return strtr($data, array('+' => '-', '/' => '_', '=' => ''));
}
/** /**
* Returns a string of highly randomized bytes (over the full 8-bit range). * Returns a string of highly randomized bytes (over the full 8-bit range).
* *
...@@ -1945,38 +1972,34 @@ function drupal_block_denied($ip) { ...@@ -1945,38 +1972,34 @@ function drupal_block_denied($ip) {
*/ */
function drupal_random_bytes($count) { function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes. // $random_state does not use drupal_static as it stores random bytes.
static $random_state, $bytes, $php_compatible; static $random_state, $bytes, $has_openssl;
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page. $missing_bytes = $count - strlen($bytes);
if (!isset($random_state)) {
$random_state = print_r($_SERVER, TRUE); if ($missing_bytes > 0) {
if (function_exists('getmypid')) {
// Further initialize with the somewhat random PHP process ID.
$random_state .= getmypid();
}
$bytes = '';
}
if (strlen($bytes) < $count) {
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes() // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
// locking on Windows and rendered it unusable. // locking on Windows and rendered it unusable.
if (!isset($php_compatible)) { if (!isset($has_openssl)) {
$php_compatible = version_compare(PHP_VERSION, '5.3.4', '>='); $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
} }
// /dev/urandom is available on many *nix systems and is considered the
// best commonly available pseudo-random source. // openssl_random_pseudo_bytes() will find entropy in a system-dependent
if ($fh = @fopen('/dev/urandom', 'rb')) { // way.
if ($has_openssl) {
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
}
// Else, read directly from /dev/urandom, which is available on many *nix
// systems and is considered cryptographically secure.
elseif ($fh = @fopen('/dev/urandom', 'rb')) {
// PHP only performs buffered reads, so in reality it will always read // PHP only performs buffered reads, so in reality it will always read
// at least 4096 bytes. Thus, it costs nothing extra to read and store // at least 4096 bytes. Thus, it costs nothing extra to read and store
// that much so as to speed any additional invocations. // that much so as to speed any additional invocations.
$bytes .= fread($fh, max(4096, $count)); $bytes .= fread($fh, max(4096, $missing_bytes));
fclose($fh); fclose($fh);
} }
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
// way. // If we couldn't get enough entropy, this simple hash-based PRNG will
elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) {
$bytes .= openssl_random_pseudo_bytes($count - strlen($bytes));
}
// If /dev/urandom is not available or returns no bytes, this loop will
// generate a good set of pseudo-random bytes on any system. // generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed // Note that it may be important that our $random_state is passed
// through hash() prior to being rolled into $output, that the two hash() // through hash() prior to being rolled into $output, that the two hash()
...@@ -1984,9 +2007,23 @@ function drupal_random_bytes($count) { ...@@ -1984,9 +2007,23 @@ function drupal_random_bytes($count) {
// the microtime() - is prepended rather than appended. This is to avoid // the microtime() - is prepended rather than appended. This is to avoid
// directly leaking $random_state via the $output stream, which could // directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers. // allow for trivial prediction of further "random" numbers.
while (strlen($bytes) < $count) { if (strlen($bytes) < $count) {
$random_state = hash('sha256', microtime() . mt_rand() . $random_state); // Initialize on the first call. The contents of $_SERVER includes a mix of
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE); // user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
$random_state = print_r($_SERVER, TRUE);
if (function_exists('getmypid')) {
// Further initialize with the somewhat random PHP process ID.
$random_state .= getmypid();
}
$bytes = '';
}
do {
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
}
while (strlen($bytes) < $count);
} }
} }
$output = substr($bytes, 0, $count); $output = substr($bytes, 0, $count);
...@@ -1997,17 +2034,21 @@ function drupal_random_bytes($count) { ...@@ -1997,17 +2034,21 @@ function drupal_random_bytes($count) {
/** /**
* Calculates a base-64 encoded, URL-safe sha-256 hmac. * Calculates a base-64 encoded, URL-safe sha-256 hmac.
* *
* @param $data * @param string $data
* String to be validated with the hmac. * String to be validated with the hmac.
* @param $key * @param string $key
* A secret string key. * A secret string key.
* *
* @return * @return string
* A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and * A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
* any = padding characters removed. * any = padding characters removed.
*/ */
function drupal_hmac_base64($data, $key) { function drupal_hmac_base64($data, $key) {
$hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE)); // Casting $data and $key to strings here is necessary to avoid empty string
// results of the hash function if they are not scalar values. As this
// function is used in security-critical contexts like token validation it is
// important that it never returns an empty string.
$hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE));
// Modify the hmac so it's safe to use in URLs. // Modify the hmac so it's safe to use in URLs.
return strtr($hmac, array('+' => '-', '/' => '_', '=' => '')); return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
} }
......
...@@ -5042,7 +5042,7 @@ function drupal_json_output($var = NULL) { ...@@ -5042,7 +5042,7 @@ function drupal_json_output($var = NULL) {
*/ */
function drupal_get_private_key() { function drupal_get_private_key() {
if (!($key = variable_get('drupal_private_key', 0))) { if (!($key = variable_get('drupal_private_key', 0))) {
$key = drupal_hash_base64(drupal_random_bytes(55)); $key = drupal_random_key();
variable_set('drupal_private_key', $key); variable_set('drupal_private_key', $key);
} }
return $key; return $key;
...@@ -5081,7 +5081,7 @@ function drupal_get_token($value = '') { ...@@ -5081,7 +5081,7 @@ function drupal_get_token($value = '') {
*/ */
function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) { function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
global $user; global $user;
return (($skip_anonymous && $user->uid == 0) || ($token == drupal_get_token($value))); return (($skip_anonymous && $user->uid == 0) || ($token === drupal_get_token($value)));
} }
function _drupal_bootstrap_full() { function _drupal_bootstrap_full() {
...@@ -5114,6 +5114,10 @@ function _drupal_bootstrap_full() { ...@@ -5114,6 +5114,10 @@ function _drupal_bootstrap_full() {
module_load_all(); module_load_all();
// Make sure all stream wrappers are registered. // Make sure all stream wrappers are registered.
file_get_stream_wrappers(); file_get_stream_wrappers();
// Ensure mt_rand is reseeded, to prevent random values from one page load
// being exploited to predict random values in subsequent page loads.
$seed = unpack("L", drupal_random_bytes(4));
mt_srand($seed[1]);
$test_info = &$GLOBALS['drupal_test_info']; $test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site'])) { if (!empty($test_info['in_child_site'])) {
......
...@@ -470,8 +470,11 @@ function file_ensure_htaccess() { ...@@ -470,8 +470,11 @@ function file_ensure_htaccess() {
* @param $private * @param $private
* FALSE indicates that $directory should be an open and public directory. * FALSE indicates that $directory should be an open and public directory.
* The default is TRUE which indicates a private and protected directory. * The default is TRUE which indicates a private and protected directory.
* @param $force_overwrite
* Set to TRUE to attempt to overwrite the existing .htaccess file if one is
* already present. Defaults to FALSE.
*/ */
function file_create_htaccess($directory, $private = TRUE) { function file_create_htaccess($directory, $private = TRUE, $force_overwrite = FALSE) {
if (file_uri_scheme($directory)) { if (file_uri_scheme($directory)) {
$directory = file_stream_wrapper_uri_normalize($directory); $directory = file_stream_wrapper_uri_normalize($directory);
} }
...@@ -480,19 +483,12 @@ function file_create_htaccess($directory, $private = TRUE) { ...@@ -480,19 +483,12 @@ function file_create_htaccess($directory, $private = TRUE) {
} }
$htaccess_path = $directory . '/.htaccess'; $htaccess_path = $directory . '/.htaccess';
if (file_exists($htaccess_path)) { if (file_exists($htaccess_path) && !$force_overwrite) {
// Short circuit if the .htaccess file already exists. // Short circuit if the .htaccess file already exists.
return; return;
} }
if ($private) { $htaccess_lines = file_htaccess_lines($private);
// Private .htaccess file.
$htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nDeny from all\nOptions None\nOptions +FollowSymLinks";
}
else {
// Public .htaccess file.
$htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks";
}
// Write the .htaccess file. // Write the .htaccess file.
if (file_put_contents($htaccess_path, $htaccess_lines)) { if (file_put_contents($htaccess_path, $htaccess_lines)) {
...@@ -504,6 +500,45 @@ function file_create_htaccess($directory, $private = TRUE) { ...@@ -504,6 +500,45 @@ function file_create_htaccess($directory, $private = TRUE) {
} }
} }
/**
* Returns the standard .htaccess lines that Drupal writes to file directories.
*
* @param $private
* (Optional) Set to FALSE to return the .htaccess lines for an open and
* public directory. The default is TRUE, which returns the .htaccess lines
* for a private and protected directory.
*
* @return
* A string representing the desired contents of the .htaccess file.
*
* @see file_create_htaccess()
*/
function file_htaccess_lines($private = TRUE) {
$lines = <<<EOF
# Turn off all options we don't need.
Options None
Options +FollowSymLinks
# Set the catch-all handler to prevent scripts from being executed.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
<Files *>
# Override the handler again if we're run later in the evaluation list.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
</Files>
# If we know how to do it safely, disable the PHP engine entirely.
<IfModule mod_php5.c>
php_flag engine off
</IfModule>
EOF;
if ($private) {
$lines = "Deny from all\n\n" . $lines;
}
return $lines;
}
/** /**
* Loads file objects from the database. * Loads file objects from the database.
* *
......
...@@ -462,7 +462,7 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { ...@@ -462,7 +462,7 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
$form['#build_id'] = $old_form['#build_id']; $form['#build_id'] = $old_form['#build_id'];
} }
else { else {
$form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); $form['#build_id'] = 'form-' . drupal_random_key();
} }
// #action defaults to request_uri(), but in case of Ajax and other partial // #action defaults to request_uri(), but in case of Ajax and other partial
...@@ -977,7 +977,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) { ...@@ -977,7 +977,7 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
// @see drupal_build_form() // @see drupal_build_form()
// @see drupal_rebuild_form() // @see drupal_rebuild_form()
if (!isset($form['#build_id'])) { if (!isset($form['#build_id'])) {
$form['#build_id'] = 'form-' . drupal_hash_base64(uniqid(mt_rand(), TRUE) . mt_rand()); $form['#build_id'] = 'form-' . drupal_random_key();
} }
$form['form_build_id'] = array( $form['form_build_id'] = array(
'#type' => 'hidden', '#type' => 'hidden',
...@@ -1129,6 +1129,12 @@ function drupal_validate_form($form_id, &$form, &$form_state) { ...@@ -1129,6 +1129,12 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
// Setting this error will cause the form to fail validation. // Setting this error will cause the form to fail validation.
form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url))); form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
// Stop here and don't run any further validation handlers, because they
// could invoke non-safe operations which opens the door for CSRF
// vulnerabilities.
$validated_forms[$form_id] = TRUE;
return;
} }
} }
......
...@@ -766,6 +766,15 @@ function install_system_module(&$install_state) { ...@@ -766,6 +766,15 @@ function install_system_module(&$install_state) {
// Install system.module. // Install system.module.
drupal_install_system(); drupal_install_system();
// Call file_ensure_htaccess() to ensure that all of Drupal's standard
// directories (e.g., the public and private files directories) have
// appropriate .htaccess files. These directories will have already been
// created by this point in the installer, since Drupal creates them during
// the install_verify_requirements() task. Note that we cannot call
// file_ensure_htaccess() any earlier than this, since it relies on
// system.module in order to work.
file_ensure_htaccess();
// Enable the user module so that sessions can be recorded during the // Enable the user module so that sessions can be recorded during the
// upcoming bootstrap step. // upcoming bootstrap step.
module_enable(array('user'), FALSE); module_enable(array('user'), FALSE);
...@@ -981,7 +990,7 @@ function install_settings_form_submit($form, &$form_state) { ...@@ -981,7 +990,7 @@ function install_settings_form_submit($form, &$form_state) {
'required' => TRUE, 'required' => TRUE,
); );
$settings['drupal_hash_salt'] = array( $settings['drupal_hash_salt'] = array(
'value' => drupal_hash_base64(drupal_random_bytes(55)), 'value' => drupal_random_key(),
'required' => TRUE, 'required' => TRUE,
); );
drupal_rewrite_settings($settings); drupal_rewrite_settings($settings);
......
...@@ -263,10 +263,10 @@ function drupal_session_initialize() { ...@@ -263,10 +263,10 @@ function drupal_session_initialize() {
// Less random sessions (which are much faster to generate) are used for // Less random sessions (which are much faster to generate) are used for
// anonymous users than are generated in drupal_session_regenerate() when // anonymous users than are generated in drupal_session_regenerate() when
// a user becomes authenticated. // a user becomes authenticated.
session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE))); session_id(drupal_random_key());
if ($is_https && variable_get('https', FALSE)) { if ($is_https && variable_get('https', FALSE)) {
$insecure_session_name = substr(session_name(), 1); $insecure_session_name = substr(session_name(), 1);
$session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE)); $session_id = drupal_random_key();
$_COOKIE[$insecure_session_name] = $session_id; $_COOKIE[$insecure_session_name] = $session_id;
} }
} }
...@@ -360,7 +360,7 @@ function drupal_session_regenerate() { ...@@ -360,7 +360,7 @@ function drupal_session_regenerate() {
$old_insecure_session_id = $_COOKIE[$insecure_session_name]; $old_insecure_session_id = $_COOKIE[$insecure_session_name];
} }
$params = session_get_cookie_params(); $params = session_get_cookie_params();
$session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)); $session_id = drupal_random_key();
// If a session cookie lifetime is set, the session will expire // If a session cookie lifetime is set, the session will expire
// $params['lifetime'] seconds from the current request. If it is not set, // $params['lifetime'] seconds from the current request. If it is not set,
// it will expire when the browser is closed. // it will expire when the browser is closed.
...@@ -372,7 +372,7 @@ function drupal_session_regenerate() { ...@@ -372,7 +372,7 @@ function drupal_session_regenerate() {
if (drupal_session_started()) { if (drupal_session_started()) {
$old_session_id = session_id(); $old_session_id = session_id();
} }
session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55))); session_id(drupal_random_key());
if (isset($old_session_id)) { if (isset($old_session_id)) {
$params = session_get_cookie_params(); $params = session_get_cookie_params();
......
...@@ -240,6 +240,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) { ...@@ -240,6 +240,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) {
$form['palette'][$name] = array( $form['palette'][$name] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => check_plain($names[$name]), '#title' => check_plain($names[$name]),
'#value_callback' => 'color_palette_color_value',
'#default_value' => $value, '#default_value' => $value,
'#size' => 8, '#size' => 8,
); );
...@@ -294,6 +295,52 @@ function theme_color_scheme_form($variables) { ...@@ -294,6 +295,52 @@ function theme_color_scheme_form($variables) {
return $output; return $output;
} }
/**
* 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(). * Form validation handler for color_scheme_form().
* *
...@@ -302,7 +349,7 @@ function theme_color_scheme_form($variables) { ...@@ -302,7 +349,7 @@ function theme_color_scheme_form($variables) {
function color_scheme_form_validate($form, &$form_state) { function color_scheme_form_validate($form, &$form_state) {
// Only accept hexadecimal CSS color strings to avoid XSS upon use. // Only accept hexadecimal CSS color strings to avoid XSS upon use.
foreach ($form_state['values']['palette'] as $key => $color) { 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']))); form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
} }
} }
......
...@@ -351,7 +351,7 @@ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langc ...@@ -351,7 +351,7 @@ function image_field_widget_form(&$form, &$form_state, $field, $instance, $langc
if ($field['cardinality'] == 1) { if ($field['cardinality'] == 1) {
// If there's only one field, return it as delta 0. // If there's only one field, return it as delta 0.
if (empty($elements[0]['#default_value']['fid'])) { 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 { else {
......
...@@ -380,6 +380,9 @@ function _openid_parse_message($message) { ...@@ -380,6 +380,9 @@ function _openid_parse_message($message) {
/** /**
* Return a nonce value - formatted per OpenID spec. * 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() { function _openid_nonce() {
// YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters. // YYYY-MM-DDThh:mm:ssZ, plus some optional extra unique characters.
...@@ -549,7 +552,7 @@ function _openid_dh_rand($stop) { ...@@ -549,7 +552,7 @@ function _openid_dh_rand($stop) {
} }
do { do {
$bytes = "\x00" . _openid_get_bytes($nbytes); $bytes = "\x00" . drupal_random_bytes($nbytes);
$n = _openid_dh_binary_to_long($bytes); $n = _openid_dh_binary_to_long($bytes);
// Keep looping if this value is in the low duplicated range. // Keep looping if this value is in the low duplicated range.
} while (_openid_math_cmp($n, $duplicate) < 0); } while (_openid_math_cmp($n, $duplicate) < 0);
...@@ -558,23 +561,7 @@ function _openid_dh_rand($stop) { ...@@ -558,23 +561,7 @@ function _openid_dh_rand($stop) {
} }
function _openid_get_bytes($num_bytes) { function _openid_get_bytes($num_bytes) {
$f = &drupal_static(__FUNCTION__); return drupal_random_bytes($num_bytes);
$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;
} }
function _openid_response($str = NULL) { function _openid_response($str = NULL) {
......
...@@ -694,13 +694,6 @@ class OpenIDTestCase extends DrupalWebTestCase { ...@@ -694,13 +694,6 @@ class OpenIDTestCase extends DrupalWebTestCase {
$this->assertEqual(_openid_dh_xorsecret('123456790123456790123456790', "abc123ABC\x00\xFF"), "\xa4'\x06\xbe\xf1.\x00y\xff\xc2\xc1", '_openid_dh_xorsecret() returned expected result.'); $this->assertEqual(_openid_dh_xorsecret('123456790123456790123456790', "abc123ABC\x00\xFF"), "\xa4'\x06\xbe\xf1.\x00y\xff\xc2\xc1", '_openid_dh_xorsecret() returned expected result.');
} }
/**
* Test _openid_get_bytes().
*/
function testOpenidGetBytes() {
$this->assertEqual(strlen(_openid_get_bytes(20)), 20, '_openid_get_bytes() returned expected result.');
}
/** /**
* Test _openid_signature(). * Test _openid_signature().
*/ */
......
...@@ -13,5 +13,5 @@ function openid_test_install() { ...@@ -13,5 +13,5 @@ function openid_test_install() {
// Generate a MAC key (Message Authentication Code) used for signing messages. // Generate a MAC key (Message Authentication Code) used for signing messages.
// The variable is base64-encoded, because variables cannot contain non-UTF-8 // The variable is base64-encoded, because variables cannot contain non-UTF-8
// data. // data.
variable_set('openid_test_mac_key', base64_encode(_openid_get_bytes(20))); variable_set('openid_test_mac_key', drupal_random_key(20));
} }
...@@ -146,6 +146,10 @@ function overlay_init() { ...@@ -146,6 +146,10 @@ function overlay_init() {
// If this page shouldn't be rendered inside the overlay, redirect to the // If this page shouldn't be rendered inside the overlay, redirect to the
// parent. // parent.
elseif (!path_is_admin($current_path)) { 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')))); overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
} }
......
...@@ -952,7 +952,7 @@ class FileDirectoryTest extends FileTestCase { ...@@ -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'); $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), 'Successfully re-created the .htaccess file in the files directory.', 'File');
// Verify contents of .htaccess file. // Verify contents of .htaccess file.
$file = file_get_contents(file_default_scheme() . '://.htaccess'); $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');
} }
/** /**
......
...@@ -82,6 +82,10 @@ class FormsTestCase extends DrupalWebTestCase { ...@@ -82,6 +82,10 @@ class FormsTestCase extends DrupalWebTestCase {
$form_state['input'][$element] = $empty; $form_state['input'][$element] = $empty;
$form_state['input']['form_id'] = $form_id; $form_state['input']['form_id'] = $form_id;
$form_state['method'] = 'post'; $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_prepare_form($form_id, $form, $form_state);
drupal_process_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state);
$errors = form_get_errors(); $errors = form_get_errors();
...@@ -614,6 +618,18 @@ class FormValidationTestCase extends DrupalWebTestCase { ...@@ -614,6 +618,18 @@ class FormValidationTestCase extends DrupalWebTestCase {
$this->drupalPost(NULL, array(), 'Save'); $this->drupalPost(NULL, array(), 'Save');
$this->assertNoFieldByName('name', 'Form element was hidden.'); $this->assertNoFieldByName('name', 'Form element was hidden.');
$this->assertText('Name value: element_validate_access', 'Value for inaccessible form element exists.'); $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 { ...@@ -941,6 +957,10 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase {
$form_state['input'] = $edit; $form_state['input'] = $edit;
$form_state['input']['form_id'] = $form_id; $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_prepare_form($form_id, $form, $form_state);
drupal_process_form($form_id, $form, $form_state); drupal_process_form($form_id, $form, $form_state);
......