diff options
| -rw-r--r-- | CHANGES.txt | 42 | ||||
| -rw-r--r-- | CONTRIBUTORS.txt | 5 | ||||
| -rw-r--r-- | actions/profile/edit.php | 42 | ||||
| -rw-r--r-- | engine/classes/ElggMenuBuilder.php | 32 | ||||
| -rw-r--r-- | engine/classes/ElggMenuItem.php | 3 | ||||
| -rw-r--r-- | engine/classes/ElggObject.php | 2 | ||||
| -rw-r--r-- | engine/lib/output.php | 46 | ||||
| -rw-r--r-- | engine/lib/upgrades/2010052601.php | 12 | ||||
| -rw-r--r-- | install/ElggInstaller.php | 2 | ||||
| -rw-r--r-- | install/cli/sample_installer.php | 35 | ||||
| -rw-r--r-- | js/lib/elgglib.js | 2 | ||||
| -rw-r--r-- | mod/blog/views/default/forms/blog/save.php | 9 | ||||
| -rw-r--r-- | mod/groups/actions/groups/edit.php | 8 | ||||
| -rw-r--r-- | mod/messages/pages/messages/read.php | 2 | ||||
| -rw-r--r-- | version.php | 4 | ||||
| -rw-r--r-- | views/default/forms/profile/edit.php | 3 | ||||
| -rw-r--r-- | views/default/object/plugin/elements/dependencies.php | 2 | ||||
| -rw-r--r-- | views/default/output/email.php | 4 | 
18 files changed, 202 insertions, 53 deletions
| diff --git a/CHANGES.txt b/CHANGES.txt index 7a3422d7d..5eb9aca0d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,45 @@ +Version 1.8.9 +(November 11, 2012 from https://github.com/Elgg/Elgg/tree/1.8) + + Contributing Developers: +  * Brett Profitt +  * Cash Costello +  * Evan Winslow +  * Jeroen Dalsem +  * Jerome Bakker +  * Matt Beckett +  * Paweł Sroka +  * Sem +  * Steve Clay + + Security Enhancements: +  * Sample CLI installer cannot break site +  * Removed XSS vulnerabilities in titles and user profiles + + Enhancements: +  * UX: A group's owner can transfer ownership to another member +  * UX: Search queries persist in the search box +  * Several (X)HTML validation improvements +  * Improved performance via more aggressive entity and metadata caching +  * BC: 1.7 group profile URLs forward correctly + + Bugfixes: +  * UX: Titles containing HTML tokens are never mangled +  * UX: Empty user profile values saved properly +  * UX: Blog creator always mentioned in activity stream (not user who published it) +  * UI: Fixed ordering of registered menu items in some cases +  * UI: Embed dialog does not break file inputs +  * UI: Datepicker now respects language +  * UI: More reliable display of access input in widgets +  * UI: Group edit form is sticky +  * UI: Site categories are sticky in forms +  * API: Language fallback works in Javascript +  * API: Fallback to default viewtype if invalid one given +  * API: Notices reported for missing language keys +  * Memcache now safe to use; never bypasses access control +  * BC: upgrade shows comments consistently in activity stream + +  Version 1.8.8  (July 11, 2012 from https://github.com/Elgg/Elgg/tree/1.8) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 0163757e7..a8e74d3a4 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,6 +1,7 @@  The following have made notable contributions to the Elgg Project.  (List in alphabetical order.) +Steve Clay - http://www.mrclay.org/, https://twitter.com/mrclay_org  Cash Costello - cash@elgg.org, http://cashcostello.com/ @@ -20,10 +21,10 @@ Tom Read - MITRE http://mitre.org/  Justin Richer - MITRE http://mitre.org/ -Dave Tosh - davidgtosh@gmail.com,  http://twitter.com/davetosh +Dave Tosh - davidgtosh@gmail.com, http://twitter.com/davetosh  Ben Werdmuller - http://benwerd.com/ -Nicholas Whitt - nick.whitt@gmail.com,  http://twitter.com/nogoodnick +Nicholas Whitt - nick.whitt@gmail.com, http://twitter.com/nogoodnick  Evan Winslow - evan@elgg.org, http://evanwinslow.com/ diff --git a/actions/profile/edit.php b/actions/profile/edit.php index 8ca60f246..89bf2bc0b 100644 --- a/actions/profile/edit.php +++ b/actions/profile/edit.php @@ -25,7 +25,7 @@ if (!is_array($accesslevel)) {   * wrapper for recursive array walk decoding   */  function profile_array_decoder(&$v) { -	$v = html_entity_decode($v, ENT_COMPAT, 'UTF-8'); +	$v = _elgg_html_decode($v);  }  $profile_fields = elgg_get_config('profile_fields'); @@ -37,7 +37,7 @@ foreach ($profile_fields as $shortname => $valuetype) {  	if (is_array($value)) {  		array_walk_recursive($value, 'profile_array_decoder');  	} else { -		$value = html_entity_decode($value, ENT_COMPAT, 'UTF-8'); +		$value = _elgg_html_decode($value);  	}  	// limit to reasonable sizes @@ -51,7 +51,7 @@ foreach ($profile_fields as $shortname => $valuetype) {  	if ($valuetype == 'tags') {  		$value = string_to_tag_array($value);  	} - +	  	$input[$shortname] = $value;  } @@ -71,24 +71,30 @@ if (sizeof($input) > 0) {  	foreach ($input as $shortname => $value) {  		$options = array(  			'guid' => $owner->guid, -			'metadata_name' => $shortname +			'metadata_name' => $shortname, +			'limit' => false  		);  		elgg_delete_metadata($options); -		if (isset($accesslevel[$shortname])) { -			$access_id = (int) $accesslevel[$shortname]; -		} else { -			// this should never be executed since the access level should always be set -			$access_id = ACCESS_DEFAULT; -		} -		if (is_array($value)) { -			$i = 0; -			foreach ($value as $interval) { -				$i++; -				$multiple = ($i > 1) ? TRUE : FALSE; -				create_metadata($owner->guid, $shortname, $interval, 'text', $owner->guid, $access_id, $multiple); +		 +		if(!is_null($value) && ($value !== '')){ +			// only create metadata for non empty values (0 is allowed) to prevent metadata records with empty string values #4858 +			 +			if (isset($accesslevel[$shortname])) { +				$access_id = (int) $accesslevel[$shortname]; +			} else { +				// this should never be executed since the access level should always be set +				$access_id = ACCESS_DEFAULT; +			} +			if (is_array($value)) { +				$i = 0; +				foreach ($value as $interval) { +					$i++; +					$multiple = ($i > 1) ? TRUE : FALSE; +					create_metadata($owner->guid, $shortname, $interval, 'text', $owner->guid, $access_id, $multiple); +				} +			} else { +				create_metadata($owner->getGUID(), $shortname, $value, 'text', $owner->getGUID(), $access_id);  			} -		} else { -			create_metadata($owner->getGUID(), $shortname, $value, 'text', $owner->getGUID(), $access_id);  		}  	} diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index 06000c923..f43599999 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -204,6 +204,9 @@ class ElggMenuBuilder {  		// sort each section  		foreach ($this->menu as $index => $section) { +			foreach ($section as $key => $node) { +				$section[$key]->original_order = $key; +			}  			usort($section, $sort_callback);  			$this->menu[$index] = $section; @@ -232,10 +235,14 @@ class ElggMenuBuilder {  	 * @return bool  	 */  	public static function compareByText($a, $b) { -		$a = $a->getText(); -		$b = $b->getText(); +		$at = $a->getText(); +		$bt = $b->getText(); -		return strnatcmp($a, $b); +		$result = strnatcmp($at, $bt); +		if ($result === 0) { +			return $a->original_order - $b->original_order; +		} +		return $result;  	}  	/** @@ -246,10 +253,14 @@ class ElggMenuBuilder {  	 * @return bool  	 */  	public static function compareByName($a, $b) { -		$a = $a->getName(); -		$b = $b->getName(); +		$an = $a->getName(); +		$bn = $b->getName(); -		return strcmp($a, $b); +		$result = strcmp($an, $bn); +		if ($result === 0) { +			return $a->original_order - $b->original_order; +		} +		return $result;  	}  	/** @@ -260,9 +271,12 @@ class ElggMenuBuilder {  	 * @return bool  	 */  	public static function compareByWeight($a, $b) { -		$a = $a->getWeight(); -		$b = $b->getWeight(); +		$aw = $a->getWeight(); +		$bw = $b->getWeight(); -		return $a > $b; +		if ($aw == $bw) { +			return $a->original_order - $b->original_order; +		} +		return $aw - $bw;  	}  } diff --git a/engine/classes/ElggMenuItem.php b/engine/classes/ElggMenuItem.php index 4bc9144d4..fe25f3ddd 100644 --- a/engine/classes/ElggMenuItem.php +++ b/engine/classes/ElggMenuItem.php @@ -542,6 +542,9 @@ class ElggMenuItem {  	 * @return void  	 */  	public function sortChildren($sortFunction) { +		foreach ($this->data['children'] as $key => $node) { +			$this->data['children'][$key]->original_order = $key; +		}  		usort($this->data['children'], $sortFunction);  	} diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php index b4bae6825..fa6296c8c 100644 --- a/engine/classes/ElggObject.php +++ b/engine/classes/ElggObject.php @@ -223,7 +223,7 @@ class ElggObject extends ElggEntity {  		// must be member of group  		if (elgg_instanceof($this->getContainerEntity(), 'group')) { -			if (!$this->getContainerEntity()->canWriteToContainer(get_user($user_guid))) { +			if (!$this->getContainerEntity()->canWriteToContainer($user_guid)) {  				return false;  			}  		} diff --git a/engine/lib/output.php b/engine/lib/output.php index 7bfc4be6e..352de863b 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -271,8 +271,8 @@ function elgg_normalize_url($url) {  		// '?query=test', #target  		return $url; -	} elseif (stripos($url, 'javascript:') === 0) { -		// 'javascript:' +	} elseif (stripos($url, 'javascript:') === 0 || stripos($url, 'mailto:') === 0) { +		// 'javascript:' and 'mailto:'  		// Not covered in FILTER_VALIDATE_URL  		return $url; @@ -398,3 +398,45 @@ function elgg_strip_tags($string) {  	return $string;  } + +/** + * Apply html_entity_decode() to a string while re-entitising HTML + * special char entities to prevent them from being decoded back to their + * unsafe original forms. + * + * This relies on html_entity_decode() not translating entities when + * doing so leaves behind another entity, e.g. &gt; if decoded would + * create > which is another entity itself. This seems to escape the + * usual behaviour where any two paired entities creating a HTML tag are + * usually decoded, i.e. a lone > is not decoded, but <foo> would + * be decoded to <foo> since it creates a full tag. + * + * Note: This function is poorly explained in the manual - which is really + * bad given its potential for misuse on user input already escaped elsewhere. + * Stackoverflow is littered with advice to use this function in the precise + * way that would lead to user input being capable of injecting arbitrary HTML. + * + * @param string $string + * + * @return string + * + * @author Pádraic Brady + * @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com) + * @license Released under dual-license GPL2/MIT by explicit permission of Pádraic Brady + * + * @access private + */ +function _elgg_html_decode($string) { +	$string = str_replace( +		array('>', '<', '&', '"', '''), +		array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'), +		$string +	); +	$string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8'); +	$string = str_replace( +		array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'), +		array('>', '<', '&', '"', '''), +		$string +	); +	return $string; +} diff --git a/engine/lib/upgrades/2010052601.php b/engine/lib/upgrades/2010052601.php index 5b477910f..a9cca6dc5 100644 --- a/engine/lib/upgrades/2010052601.php +++ b/engine/lib/upgrades/2010052601.php @@ -9,14 +9,14 @@ $params = array('type' => 'group',  $groups = elgg_get_entities($params);  if ($groups) {  	foreach ($groups as $group) { -		$group->name = html_entity_decode($group->name, ENT_COMPAT, 'UTF-8'); -		$group->description = html_entity_decode($group->description, ENT_COMPAT, 'UTF-8'); -		$group->briefdescription = html_entity_decode($group->briefdescription, ENT_COMPAT, 'UTF-8'); -		$group->website = html_entity_decode($group->website, ENT_COMPAT, 'UTF-8'); +		$group->name = _elgg_html_decode($group->name); +		$group->description = _elgg_html_decode($group->description); +		$group->briefdescription = _elgg_html_decode($group->briefdescription); +		$group->website = _elgg_html_decode($group->website);  		if ($group->interests) {  			$tags = $group->interests; -			foreach ($tags as $index=>$tag) { -				$tags[$index] = html_entity_decode($tag, ENT_COMPAT, 'UTF-8'); +			foreach ($tags as $index => $tag) { +				$tags[$index] = _elgg_html_decode($tag);  			}  			$group->interests = $tags;  		} diff --git a/install/ElggInstaller.php b/install/ElggInstaller.php index 03c84a43e..934b38d28 100644 --- a/install/ElggInstaller.php +++ b/install/ElggInstaller.php @@ -157,7 +157,7 @@ class ElggInstaller {  			'password',  		);  		foreach ($requiredParams as $key) { -			if (!array_key_exists($key, $params)) { +			if (empty($params[$key])) {  				$msg = elgg_echo('install:error:requiredfield', array($key));  				throw new InstallationException($msg);  			} diff --git a/install/cli/sample_installer.php b/install/cli/sample_installer.php index 954169a6a..0bae0cd23 100644 --- a/install/cli/sample_installer.php +++ b/install/cli/sample_installer.php @@ -3,10 +3,27 @@   * Sample cli installer script   */ +$enabled = false; + +// Do not edit below this line. ////////////////////////////// + + +if (!$enabled) { +	echo "To enable this script, change \$enabled to true.\n"; +	echo "You *must* disable this script after a successful installation.\n"; +	exit; +} + +if (PHP_SAPI !== 'cli') { +	echo "You must use the command line to run this script."; +	exit; +} +  require_once(dirname(dirname(__FILE__)) . "/ElggInstaller.php");  $installer = new ElggInstaller(); +// none of the following may be empty  $params = array(  	// database parameters  	'dbuser' => '', @@ -28,3 +45,21 @@ $params = array(  // install and create the .htaccess file  $installer->batchInstall($params, TRUE); + +// at this point installation has completed (otherwise an exception halted execution). + +// try to rewrite the script to disable it. +if (is_writable(__FILE__)) { +	$code = file_get_contents(__FILE__); +	if (preg_match('~\\$enabled\\s*=\\s*(true|1)\\s*;~i', $code)) { +		// looks safe to rewrite +		$code = preg_replace('~\\$enabled\\s*=\\s*(true|1)\\s*;~i', '$enabled = false;', $code); +		file_put_contents(__FILE__, $code); + +		echo "\nNote: This script has been disabled for your safety.\n"; +		exit; +	} +} + +echo "\nWarning: You *must* disable this script by setting \$enabled = false;.\n"; +echo "Leaving this script enabled could endanger your installation.\n"; diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index 81209ebd0..dc7c07165 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -283,7 +283,7 @@ elgg.normalize_url = function(url) {  	}  	// 'javascript:' -	else if (url.indexOf('javascript:') === 0) { +	else if (url.indexOf('javascript:') === 0 || url.indexOf('mailto:') === 0 ) {  		return url;  	} diff --git a/mod/blog/views/default/forms/blog/save.php b/mod/blog/views/default/forms/blog/save.php index a805541bd..7c3265c8d 100644 --- a/mod/blog/views/default/forms/blog/save.php +++ b/mod/blog/views/default/forms/blog/save.php @@ -53,7 +53,7 @@ $excerpt_label = elgg_echo('blog:excerpt');  $excerpt_input = elgg_view('input/text', array(  	'name' => 'excerpt',  	'id' => 'blog_excerpt', -	'value' => html_entity_decode($vars['excerpt'], ENT_COMPAT, 'UTF-8') +	'value' => _elgg_html_decode($vars['excerpt'])  ));  $body_label = elgg_echo('blog:body'); @@ -125,9 +125,10 @@ $draft_warning  	$excerpt_input  </div> -<label for="blog_description">$body_label</label> -$body_input -<br /> +<div> +	<label for="blog_description">$body_label</label> +	$body_input +</div>  <div>  	<label for="blog_tags">$tags_label</label> diff --git a/mod/groups/actions/groups/edit.php b/mod/groups/actions/groups/edit.php index a4169461a..2d7e1f023 100644 --- a/mod/groups/actions/groups/edit.php +++ b/mod/groups/actions/groups/edit.php @@ -8,15 +8,15 @@  // Load configuration  global $CONFIG; +elgg_make_sticky_form('groups'); +  /**   * wrapper for recursive array walk decoding   */  function profile_array_decoder(&$v) { -	$v = html_entity_decode($v, ENT_COMPAT, 'UTF-8'); +	$v = _elgg_html_decode($v);  } -elgg_make_sticky_form('groups'); -  // Get group fields  $input = array();  foreach ($CONFIG->group as $shortname => $valuetype) { @@ -25,7 +25,7 @@ foreach ($CONFIG->group as $shortname => $valuetype) {  	if (is_array($input[$shortname])) {  		array_walk_recursive($input[$shortname], 'profile_array_decoder');  	} else { -		$input[$shortname] = html_entity_decode($input[$shortname], ENT_COMPAT, 'UTF-8'); +		$input[$shortname] = _elgg_html_decode($input[$shortname]);  	}  	if ($valuetype == 'tags') { diff --git a/mod/messages/pages/messages/read.php b/mod/messages/pages/messages/read.php index eb36eaa4b..a64623564 100644 --- a/mod/messages/pages/messages/read.php +++ b/mod/messages/pages/messages/read.php @@ -38,7 +38,7 @@ if ($inbox) {  	);  	$body_params = array('message' => $message);  	$content .= elgg_view_form('messages/reply', $form_params, $body_params); -	$from_user = get_user($message->fromID); +	$from_user = get_user($message->fromId);  	if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid() && $from_user) {  		elgg_register_menu_item('title', array( diff --git a/version.php b/version.php index dda087c52..a2417d848 100644 --- a/version.php +++ b/version.php @@ -11,7 +11,7 @@  // YYYYMMDD = Elgg Date  // XX = Interim incrementer -$version = 2012071100; +$version = 2012111100;  // Human-friendly version name -$release = '1.8.8'; +$release = '1.8.9'; diff --git a/views/default/forms/profile/edit.php b/views/default/forms/profile/edit.php index 222935344..9538b779e 100644 --- a/views/default/forms/profile/edit.php +++ b/views/default/forms/profile/edit.php @@ -18,7 +18,8 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {  	foreach ($profile_fields as $shortname => $valtype) {  		$metadata = elgg_get_metadata(array(  			'guid' => $vars['entity']->guid, -			'metadata_name' => $shortname +			'metadata_name' => $shortname, +			'limit' => false  		));  		if ($metadata) {  			if (is_array($metadata)) { diff --git a/views/default/object/plugin/elements/dependencies.php b/views/default/object/plugin/elements/dependencies.php index 8abd61692..d8daedd33 100644 --- a/views/default/object/plugin/elements/dependencies.php +++ b/views/default/object/plugin/elements/dependencies.php @@ -29,6 +29,8 @@ foreach ($deps as $dep) {  	if ($dep['status']) {  		$class = "elgg-state-success elgg-dependency elgg-dependency-$type"; +	} elseif ($dep['type'] == 'suggests') { +		$class = "elgg-state-warning elgg-dependency elgg-dependency-$type";  	} else {  		$class = "elgg-state-error elgg-dependency elgg-dependency-$type";  	} diff --git a/views/default/output/email.php b/views/default/output/email.php index 00eefad1f..f5a8bc4b8 100644 --- a/views/default/output/email.php +++ b/views/default/output/email.php @@ -10,6 +10,8 @@   *   */ +$encoded_value = htmlspecialchars($vars['value'], ENT_QUOTES, 'UTF-8'); +  if (!empty($vars['value'])) { -	echo "<a href=\"mailto:" . $vars['value'] . "\">". htmlspecialchars($vars['value'], ENT_QUOTES, 'UTF-8', false) ."</a>"; +	echo "<a href=\"mailto:$encoded_value\">$encoded_value</a>";  }
\ No newline at end of file | 
