CVE-2018-14071: Improper Input Validation (CWE-20)
value: The Geo Mashup plugin before 1.10.4 for WordPress has insufficient sanitization of post editor and other user input.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 9.8
Published: 2018-07-16T13:29Z
Repository: https://github.com/cyberhobo/wordpress-geo-mashup
Language: PHP
Files changed: geo-mashup-db.php, geo-mashup-ui-managers.php, geo-mashup.php, geo-query.php
Commit date: 2018-07-11 20:20:20 -0700
Commit message: Strengthen sanitization, fixes #817

Diff: diff --git a/geo-mashup-db.php b/geo-mashup-db.php
index 96fb180..fa764b4 100644
--- a/geo-mashup-db.php
+++ b/geo-mashup-db.php
@@ -1474,10 +1474,65 @@ class GeoMashupDB {
 	 */
 	public static function get_post_locations( $query_args = '' ) {
 		return self::get_object_locations( $query_args );
 	}
 
+	/**
+	 * Sanitize an array of query arguments.
+	 *
+	 * @param array $query_args
+	 *
+	 * @return array
+	 */
+	public static function sanitize_query_args( $query_args ) {
+		array_walk_recursive($query_args, array( __CLASS__, 'sanitize_query_arg' ) );
+		return $query_args;
+	}
+
+	/**
+	 * Sanitize a single query argument.
+	 *
+	 * @param mixed $value May be modified.
+	 * @param string $name
+	 */
+	public static function sanitize_query_arg( &$value, $name ) {
+		switch ($name) {
+			case 'minlat':
+			case 'maxlat':
+			case 'minlng':
+			case 'maxlng':
+			case 'near_lat':
+			case 'near_lng':
+			case 'radius_km':
+			case 'radius_mi':
+				$value = (float) $value;
+				break;
+
+			case 'map_cat':
+			case 'object_ids':
+				$value = preg_replace( '/[^0-9,]', '', $value );
+				break;
+
+			case 'map_post_type':
+			case 'object_name':
+				$value = sanitize_key( $value );
+				break;
+
+			case 'limit':
+			case 'map_offset':
+				$value = (int) $value;
+				break;
+
+			case 'suppress_filters':
+				$value = (bool) $value;
+				break;
+
+			default:
+				$value = sanitize_text_field( $value );
+		}
+	}
+
 	/**
 	 * Get locations of objects.
 	 *
 	 * <code>
 	 * $results = GeoMashupDB::get_object_locations( array( 
diff --git a/geo-mashup-ui-managers.php b/geo-mashup-ui-managers.php
index 8ff7613..023acbe 100644
--- a/geo-mashup-ui-managers.php
+++ b/geo-mashup-ui-managers.php
@@ -1,7 +1,7 @@
 <?php
-/** 
+/**
  * Geo Mashup "core" implementation of location management user interfaces.
  *
  * Theoretically, everything done here could be done in a separate plugin.
  *
  * @package GeoMashup
@@ -34,11 +34,11 @@ class GeoMashupUIManager {
 		return $instances[$name];
 	}
 
 	/**
 	 * Queue UI styles to match the jQuery version.
-	 * 
+	 *
 	 * @since 1.3
 	 */
 	public function enqueue_jquery_styles() {
 		GeoMashup::register_style( 'jquery-smoothness', 'css/jquery-ui.1.7.smoothness.css', false, GEO_MASHUP_VERSION, 'screen' );
 		wp_enqueue_style( 'jquery-smoothness' );
@@ -49,26 +49,26 @@ class GeoMashupUIManager {
 	 *
 	 * @since 1.3
 	 */
 	public function enqueue_form_client_items() {
 		global $geo_mashup_options, $geo_mashup_custom;
-		
+
 		GeoMashup::register_style( 'geo-mashup-edit-form', 'css/location-editor.css', false, GEO_MASHUP_VERSION, 'screen' );
 		wp_enqueue_style( 'geo-mashup-edit-form' );
 
-		GeoMashup::register_script( 
-				'mxn', 
-				'js/mxn/mxn.js', 
-				null, 
-				GEO_MASHUP_VERSION , 
+		GeoMashup::register_script(
+				'mxn',
+				'js/mxn/mxn.js',
+				null,
+				GEO_MASHUP_VERSION ,
 				true);
-				
-		GeoMashup::register_script( 
-				'mxn-core', 
-				'js/mxn/mxn.core.js', 
-				array( 'mxn' ), 
-				GEO_MASHUP_VERSION, 
+
+		GeoMashup::register_script(
+				'mxn-core',
+				'js/mxn/mxn.core.js',
+				array( 'mxn' ),
+				GEO_MASHUP_VERSION,
 				true );
 
 		$map_api = $geo_mashup_options->get( 'overall', 'map_api' );
 		$copy_geodata = $geo_mashup_options->get( 'overall', 'copy_geodata' );
 		$geonames_username = $geo_mashup_options->get( 'overall', 'geonames_username' );
@@ -79,58 +79,58 @@ class GeoMashupUIManager {
 		$required_scripts = array( 'jquery');
 		if ( 'googlev3' == $map_api ) {
 			wp_register_script(
 					'google-maps-3',
 					'//maps.google.com/maps/api/js?key=' . $geo_mashup_options->get( 'overall', 'googlev3_key' ) . '&amp;language=' . GeoMashup::get_language_code(),
-					null, 
-					'', 
+					null,
+					'',
 					true );
-					
-			GeoMashup::register_script( 
-					'mxn-google-3', 
-					'js/mxn/mxn.googlev3.core.js', 
-					array( 'mxn-core', 'google-maps-3' ), 
-					GEO_MASHUP_VERSION, 
+
+			GeoMashup::register_script(
+					'mxn-google-3',
+					'js/mxn/mxn.googlev3.core.js',
+					array( 'mxn-core', 'google-maps-3' ),
+					GEO_MASHUP_VERSION,
 					true );
-					
-			GeoMashup::register_script( 
-					'mxn-google-3-gm', 
-					'js/mxn/mxn.googlev3.geo-mashup.js', 
-					array( 'mxn-google-3' ), 
-					GEO_MASHUP_VERSION, 
+
+			GeoMashup::register_script(
+					'mxn-google-3-gm',
+					'js/mxn/mxn.googlev3.geo-mashup.js',
+					array( 'mxn-google-3' ),
+					GEO_MASHUP_VERSION,
 					true );
-							
+
 			$required_scripts[] = 'mxn-google-3-gm';
 		} else if ( 'openlayers' == $map_api ) {
-			wp_register_script( 
-					'openlayers', 
+			wp_register_script(
+					'openlayers',
 					'//cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js',
-					null, 
-					'latest', 
+					null,
+					'latest',
 					true );
-					
-			wp_register_script( 
-					'openstreetmap', 
+
+			wp_register_script(
+					'openstreetmap',
 					'//www.openstreetmap.org/openlayers/OpenStreetMap.js',
 					 array( 'openlayers' ),
 					'latest',
 					 true );
-					 
-			GeoMashup::register_script( 
+
+			GeoMashup::register_script(
 					'mxn-openlayers',
-					'js/mxn/mxn.openlayers.core.js', 
-					array( 'mxn-core', 'openstreetmap' ), 
-					GEO_MASHUP_VERSION, 
+					'js/mxn/mxn.openlayers.core.js',
+					array( 'mxn-core', 'openstreetmap' ),
+					GEO_MASHUP_VERSION,
 					true );
-					
-			GeoMashup::register_script( 
-					'mxn-openlayers-gm', 
-					'js/mxn/mxn.openlayers.geo-mashup.js', 
-					array( 'mxn-openlayers' ), 
-					GEO_MASHUP_VERSION, 
+
+			GeoMashup::register_script(
+					'mxn-openlayers-gm',
+					'js/mxn/mxn.openlayers.geo-mashup.js',
+					array( 'mxn-openlayers' ),
+					GEO_MASHUP_VERSION,
 					true );
-					
+
 			$required_scripts[] = 'mxn-openlayers-gm';
 		} elseif ( 'leaflet' == $map_api ) {
 
 			wp_register_script(
 					'leaflet',
@@ -167,44 +167,44 @@ class GeoMashupUIManager {
 				true );
 
 			$required_scripts[] = 'mxn-leaflet-gm';
 		}
 
-		GeoMashup::register_script( 
-				'geo-mashup-location-editor', 
-				'js/location-editor.js', 
-				$required_scripts, 
-				GEO_MASHUP_VERSION, 
+		GeoMashup::register_script(
+				'geo-mashup-location-editor',
+				'js/location-editor.js',
+				$required_scripts,
+				GEO_MASHUP_VERSION,
 				true );
-				
+
 		wp_enqueue_script( 'geo-mashup-location-editor' );
 
-		GeoMashup::register_script( 
-				'jquery-ui-datepicker', 
-				'js/jquery-ui.1.7.3.datepicker.js', 
-				array( 'jquery', 'jquery-ui-core'), 
-				'1.7.3', 
+		GeoMashup::register_script(
+				'jquery-ui-datepicker',
+				'js/jquery-ui.1.7.3.datepicker.js',
+				array( 'jquery', 'jquery-ui-core'),
+				'1.7.3',
 				true );
-				
+
 		wp_enqueue_script( 'jquery-ui-datepicker' );
 
 		if ( isset( $geo_mashup_custom ) ) {
 			$custom_url = $geo_mashup_custom->file_url( 'location-editor.js' );
 			if ( ! empty( $custom_url ) ) {
-				wp_enqueue_script( 
-					'geo-mashup-location-editor-custom', 
-					$custom_url, 
+				wp_enqueue_script(
+					'geo-mashup-location-editor-custom',
+					$custom_url,
 					array( 'geo-mashup-location-editor' ),
 					null,
 					true );
 			}
 		}
 	}
 
 	/**
 	 * Determine the appropriate action from posted data.
-	 * 
+	 *
 	 * @since 1.3
 	 */
 	private function get_submit_action() {
 
 		$action = null;
@@ -224,11 +224,11 @@ class GeoMashupUIManager {
 
 		} else if ( isset( $_POST['geo_mashup_changed'] ) and 'true' == $_POST['geo_mashup_changed'] and ! empty( $_POST['geo_mashup_location'] ) ) {
 
 			// The geo mashup submit button wasn't used, but a change was made and the post saved
 			$action = 'save';
-				 
+
 		} else if ( isset( $_POST['geo_mashup_delete_location'] ) ) {
 
 			$action = 'delete';
 
 		} else if ( ! empty( $_POST['geo_mashup_location_id'] ) and empty( $_POST['geo_mashup_location'] ) ) {
@@ -240,11 +240,11 @@ class GeoMashupUIManager {
 		return $action;
 	}
 
 	/**
 	 * Save an object location from data posted by the location editor.
-	 * 
+	 *
 	 * @since 1.3
 	 * @uses GeoMashupDB::set_object_location()
 	 * @uses GeoMashupDB::delete_location()
 	 *
 	 * @param string $object_name The name of the object being edited.
@@ -255,68 +255,100 @@ class GeoMashupUIManager {
 
 		// Check the nonce
 		if ( empty( $_POST['geo_mashup_nonce'] ) || !wp_verify_nonce( $_POST['geo_mashup_nonce'], 'geo-mashup-edit' ) ) {
 			return new WP_Error( 'invalid_request', __( 'Object location not saved - invalid request.', 'GeoMashup' ) );
 		}
-		
+
 		$action = $this->get_submit_action();
 
-		if ( 'save' == $action or 'geocode' == $action ) {
+		$post_location = array();
+		$search_text = $geo_date = '';
+
+		if ( 'save' === $action || 'geocode' === $action ) {
+
+			$date_string = sanitize_text_field( $_POST['geo_mashup_date'] ) . ' ' .
+			               (int) $_POST['geo_mashup_hour'] . ':' .
+			               (int) $_POST['geo_mashup_minute'] . ':00';
+			$geo_date    = date( 'Y-m-d H:i:s', strtotime( $date_string ) );
+
+			$post_location['saved_name'] = sanitize_text_field( wp_unslash( $_POST['geo_mashup_location_name'] ) );
+
+			$search_text = sanitize_text_field( $_POST['geo_mashup_search'] );
+		}
+
 
-			$date_string = $_POST['geo_mashup_date'] . ' ' . $_POST['geo_mashup_hour'] . ':' . 
-				$_POST['geo_mashup_minute'] . ':00';
-			$geo_date = date( 'Y-m-d H:i:s', strtotime( $date_string ) );
+		if ( 'geocode' === $action && ! GeoMashupDB::geocode( $search_text, $post_location ) ) {
 
 			$post_location = array();
-			// If PHP has added slashes, WP will do it again before saving
-			$post_location['saved_name'] = stripslashes( $_POST['geo_mashup_location_name'] );
 
-			if ( 'geocode' == $action ) {
+		}
 
-				$status = GeoMashupDB::geocode( $_POST['geo_mashup_search'], $post_location );
-				if ( $status != 200 ) {
-					$post_location = array();
-				}
+		if ( 'save' === $action && ! empty( $_POST['geo_mashup_select'] ) ) {
 
-			} else {
+			$selected_items = explode( '|', $_POST['geo_mashup_select'] );
+			$post_location  = empty( $selected_items ) ? 0 : (int) $selected_items[0];
 
-				if ( ! empty( $_POST['geo_mashup_select'] ) ) {
-					$selected_items = explode( '|', $_POST['geo_mashup_select'] );
-					$post_location = intval( $selected_items[0] );
-				} else { 
-					$post_location['id'] = $_POST['geo_mashup_location_id'];
-					list( $lat, $lng ) = explode( ',', $_POST['geo_mashup_location'] );
-					$post_location['lat'] = trim( $lat );
-					$post_location['lng'] = trim( $lng );
-					$post_location['geoname'] = $_POST['geo_mashup_geoname'];
-					$post_location['address'] = stripslashes( $_POST['geo_mashup_address'] );
-					$post_location['postal_code'] = $_POST['geo_mashup_postal_code'];
-					$post_location['country_code'] = $_POST['geo_mashup_country_code'];
-					$post_location['admin_code'] = $_POST['geo_mashup_admin_code'];
-					$post_location['sub_admin_code'] = $_POST['geo_mashup_sub_admin_code'];
-					$post_location['locality_name'] = $_POST['geo_mashup_locality_name'];
-					if ( !empty( $_POST['geo_mashup_null_fields'] ) )
-						$post_location['set_null'] = $_POST['geo_mashup_null_fields'];
-				}
-			}
-			
-			if ( ! empty( $post_location ) ) {
-				$error = GeoMashupDB::set_object_location( $object_name, $object_id, $post_location, true, $geo_date );
-				if ( is_wp_error( $error ) ) 
-					return $error;
-			}
+		}
+
+		if ( 'save' === $action && empty( $_POST['geo_mashup_select'] ) ) {
+			$post_location['id'] = (int) $_POST['geo_mashup_location_id'];
+			list( $lat, $lng ) = explode( ',', $_POST['geo_mashup_location'] );
+			$post_location['lat']            = (float) $lat;
+			$post_location['lng']            = (float) $lng;
+			$post_location['geoname']        = sanitize_text_field( $_POST['geo_mashup_geoname'] );
+			$post_location['address']        = sanitize_text_field( wp_unslash( $_POST['geo_mashup_address'] ) );
+			$post_location['postal_code']    = sanitize_text_field( $_POST['geo_mashup_postal_code'] );
+			$post_location['country_code']   = $this->sanitize_country_code( $_POST['geo_mashup_country_code'] );
+			$post_location['admin_code']     = sanitize_text_field( $_POST['geo_mashup_admin_code'] );
+			$post_location['sub_admin_code'] = sanitize_text_field( $_POST['geo_mashup_sub_admin_code'] );
+			$post_location['locality_name']  = sanitize_text_field( $_POST['geo_mashup_locality_name'] );
+			$post_location['set_null']       = $this->sanitize_null_fields( $_POST['geo_mashup_null_fields'] );
+		}
+
+		$error = null;
 
-		} else if ( 'delete' == $action ) {
+		if ( ! empty( $post_location ) ) {
+
+			$error = GeoMashupDB::set_object_location(
+				$object_name,
+				$object_id,
+				$post_location,
+				true,
+				$geo_date
+			);
+
+		}
+
+		if ( 'delete' === $action ) {
 
 			$error = GeoMashupDB::delete_object_location( $object_name, $object_id );
-			if ( is_wp_error( $error ) ) 
-				return $error;
 
 		}
-		// If geodata was manually updated but Geo Mashup location was not,
-		// they may be out of sync now. Allowing that for now.
-		return true;
+
+		return is_wp_error( $error ) ? $error : true;
+	}
+
+	/**
+	 * Sanitize country codes to two uppercase alpha characters.
+	 *
+	 * @param string $code
+	 *
+	 * @return string
+	 */
+	private function sanitize_country_code( $code ) {
+		return substr( preg_replace( '/[^A-Z]/', '', $code), 0, 2 );
+	}
+
+	/**
+	 * Sanitize null fields to null or a safe string.
+	 *
+	 * @param string $fields
+	 *
+	 * @return string
+	 */
+	private function sanitize_null_fields( $fields ) {
+		return empty( $fields ) ? null : sanitize_text_field( $fields );
 	}
 }
 
 /**
  * A manager for user location user interfaces.
@@ -328,11 +360,11 @@ class GeoMashupUIManager {
  * @access public
  */
 class GeoMashupUserUIManager extends GeoMashupUIManager {
 	/**
 	 * Get the single instance of this class.
-	 * 
+	 *
 	 * @since 1.3
 	 * @uses parent::get_instance()
 	 *
      * @param string $name The class name, this class by default.
 	 * @return GeoMashupPostUIManager The instance.
@@ -358,11 +390,11 @@ class GeoMashupUserUIManager extends GeoMashupUIManager {
 	 *
 	 * init {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
 	 *
 	 * @since 1.3
-	 * @global array $geo_mashup_options 
+	 * @global array $geo_mashup_options
 	 * @global string $pagenow The WordPress-supplied requested filename.
 	 * @uses apply_filters geo_mashup_load_user_editor Returns a boolean that loads the editor when true.
 	 */
 	public function init() {
 		global $geo_mashup_options, $pagenow;
@@ -372,11 +404,11 @@ class GeoMashupUserUIManager extends GeoMashupUIManager {
 			$geo_mashup_options->get( 'overall', 'located_object_name', 'user' ) == 'true' &&
 			preg_match( '/(user-edit|profile).php/', $pagenow );
 		$enabled = apply_filters( 'geo_mashup_load_user_editor', $enabled );
 
 		// If enabled, register all the interface elements
-		if ( $enabled ) { 
+		if ( $enabled ) {
 
 			// Form generation
 			add_action( 'show_user_profile', array( &$this, 'print_form' ) );
 			add_action( 'edit_user_profile', array( &$this, 'print_form' ) );
 			// MAYBEDO: add location to registration page?
@@ -389,20 +421,20 @@ class GeoMashupUserUIManager extends GeoMashupUIManager {
 		}
 	}
 
 	/**
 	 * Print the user location editor form.
-	 * 
+	 *
 	 * @since 1.3
 	 * @uses edit-form.php
 	 */
 	public function print_form() {
 		global $user_id;
 
 		include_once( GEO_MASHUP_DIR_PATH . '/edit-form.php');
 		if ( isset( $_GET['user_id'] ) ) {
-			$object_id = $_GET['user_id'];
+			$object_id = (int) $_GET['user_id'];
 		} else {
 			$object_id = $user_id;
 		}
 		echo '<h3>' . __( 'Location', 'GeoMashup' ) . '</h3>';
 		geo_mashup_edit_form( 'user', $object_id, get_class( $this ) );
@@ -428,24 +460,20 @@ class GeoMashupUserUIManager extends GeoMashupUIManager {
 	/**
 	 * When a user is saved, also save any posted location.
 	 *
 	 * save_user {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
-	 * 
+	 *
 	 * @since 1.3
-	 * @return bool|WP_Error 
+	 * @return bool|WP_Error
 	 */
 	public function save_user() {
 		if ( empty( $_POST['user_id'] ) ) {
 			return false;
 		}
 
-		$user_id = $_POST['user_id'];
-
-		if ( !is_numeric( $user_id ) ) {
-			return $user_id;
-		}
+		$user_id = (int) $_POST['user_id'];
 
 		if ( !current_user_can( 'edit_user', $user_id ) ) {
 			return $user_id;
 		}
 
@@ -466,19 +494,19 @@ GeoMashupUserUIManager::get_instance();
  * @access public
  */
 class GeoMashupPostUIManager extends GeoMashupUIManager {
 	/**
 	 * Location found in geo_mashup_save_location shortcode.
-	 * 
+	 *
 	 * @since 1.3
 	 * @var array
 	 */
 	private $inline_location;
 
 	/**
 	 * Get the single instance of this class.
-	 * 
+	 *
 	 * @since 1.3
 	 * @uses parent::get_instance()
 	 *
      * @param string $name The class name, this class by default.
 	 * @return GeoMashupPostUIManager The instance.
@@ -501,13 +529,13 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 	/**
 	 * Initialize for use in relevant post editing requests.
 	 *
 	 * init {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
-	 * 
+	 *
 	 * @since 1.3
-	 * @global array $geo_mashup_options 
+	 * @global array $geo_mashup_options
 	 * @global string $pagenow The WordPress-supplied requested filename.
 	 * @uses apply_filters geo_mashup_load_location_editor Returns a boolean that loads the editor when true.
 	 */
 	public function init() {
 		global $geo_mashup_options, $pagenow;
@@ -580,16 +608,16 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		// If we're on a post editing page, queue up the form interface elements
 		if ( $load_location_editor ) {
 
 			$this->enqueue_form_client_items();
 
-		} 
+		}
 	}
 
 	/**
 	 * Add a location meta box to the post editors.
-	 * 
+	 *
 	 * admin_menu {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by Wordpress.
 	 *
 	 * @since 1.3
 	 */
@@ -601,11 +629,11 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		}
 	}
 
 	/**
 	 * Print the post editor form.
-	 * 
+	 *
 	 * @since 1.3
 	 * @uses edit-form.php
 	 */
 	public function print_form() {
 		global $post_ID;
@@ -631,19 +659,19 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		return parent::save_posted_object_location( $object_name, $object_id );
 	}
 
 	/**
 	 * When a post is saved, save any posted location for it.
-	 * 
+	 *
 	 * save_post {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
 	 *
 	 * @since 1.3
 	 * @uses GeoMashupDB::set_object_location()
 	 *
-	 * @param id $post_id 
-	 * @param object $post 
+	 * @param id $post_id
+	 * @param object $post
 	 * @return bool|WP_Error
 	 */
 	public function save_post($post_id, $post) {
 		if ( 'revision' == $post->post_type ) {
 			return;
@@ -672,11 +700,11 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 	/**
 	 * Extract inline save location shortcodes from post content before it is saved.
 	 *
 	 * content_save_pre {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
 	 * called by Wordpress.
-	 * 
+	 *
 	 * @since 1.3
 	 */
 	public function content_save_pre( $content ) {
 		// Piggyback on the shortcode interface to find inline tags [geo_mashup_save_location ...] 
 		add_shortcode( 'geo_mashup_save_location', 'is_null' );
@@ -684,19 +712,19 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		return preg_replace_callback('/'.$pattern.'/s', array( &$this, 'replace_save_pre_shortcode' ), $content);
 	}
 
 	/**
 	 * Store the inline location from a save location shortcode before it is removed.
-	 * 
+	 *
 	 * @since 1.3
 	 *
-	 * @param array $shortcode_match 
+	 * @param array $shortcode_match
 	 * @return The matched content, or an empty string if it was a save location shortcode.
 	 */
 	public function replace_save_pre_shortcode( $shortcode_match ) {
 		$content = $shortcode_match[0];
-		$tag_index = array_search( 'geo_mashup_save_location',  $shortcode_match ); 
+		$tag_index = array_search( 'geo_mashup_save_location',  $shortcode_match );
 		if ( $tag_index !== false ) {
 			// There is an inline location - save the attributes
 			$this->inline_location = shortcode_parse_atts( stripslashes( $shortcode_match[$tag_index+1] ) );
 
 			// If lat and lng are missing, try to geocode based on address
@@ -721,20 +749,20 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 			} else {
 				$message = ( is_wp_error( GeoMashupDB::$geocode_error ) ? GeoMashupDB::$geocode_error->get_error_message() : __( 'Address not found - try making it less detailed', 'GeoMashup' ) );
 				$content = str_replace( ']', ' geocoding_error="' . $message . '"]', $content );
 				$this->inline_location = null;
 			}
-		} 
+		}
 		return $content;
 	}
 
 	/**
 	 * Add AJAX uploaded KML to the location editor map.
 	 *
 	 * media_meta {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
 	 * called by WordPress.
-	 * 
+	 *
 	 * @since 1.3
 	 */
 	public function media_meta( $content, $post ) {
 		// Only chance to run some javascript after an ajax upload?
 		if ( 'attachment' == $post->post_type ) {
@@ -751,16 +779,16 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 	/**
 	 * Add Browser-uploaded KML to the location editor map.
 	 *
 	 * admin_print_scripts {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
-	 * 
+	 *
 	 * @since 1.3
 	 */
 	public function admin_print_scripts( $not_used ) {
 		// Load any uploaded KML into the search map - only works with browser uploader
-		
+
 		// See if wp_upload_handler found uploaded KML
 		$kml_url = get_transient( 'gm_uploaded_kml_url' );
 		if (strlen($kml_url) > 0) {
 			// Load the KML in the location editor
 			echo '
@@ -771,11 +799,11 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		}
 	}
 
 	/**
 	 * Add geo mime types to allowable uploads.
-	 * 
+	 *
 	 * upload_mimes {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
 	 * called by WordPress.
 	 *
 	 * @since 1.3
 	 */
@@ -785,12 +813,12 @@ class GeoMashupPostUIManager extends GeoMashupUIManager {
 		$mimes['gpx'] = 'application/octet-stream';
 		return $mimes;
 	}
 
 	/**
-	 * If an upload is KML, put the URL in an option to be loaded in the response 
-	 * 
+	 * If an upload is KML, put the URL in an option to be loaded in the response
+	 *
 	 * wp_handle_upload {@link http://codex.wordpress.org/Plugin_API/Filter_Reference filter}
 	 * called by WordPress.
 	 *
 	 * @since 1.3
 	 */
@@ -825,11 +853,11 @@ class GeoMashupCommentUIManager {
 	 */
 	private $add_form_script = false;
 
 	/**
 	 * Get the single instance of this class.
-	 * 
+	 *
 	 * @since 1.3
 	 *
 	 * @return GeoMashupPostUIManager The instance.
 	 */
 	public static function get_instance() {
@@ -854,19 +882,19 @@ class GeoMashupCommentUIManager {
 	/**
 	 * Initialize for use in relevant requests.
 	 *
 	 * init {@link http://codex.wordpress.org/Plugin_API/Action_Reference action}
 	 * called by WordPress.
-	 * 
+	 *
 	 * @since 1.3
-	 * @global array $geo_mashup_options 
+	 * @global array $geo_mashup_options
 	 * @uses apply_filters geo_mashup_load_comment_editor Returns a boolean that loads the editor when true.
 	 */
 	public function init() {
 		global $geo_mashup_options;
 
-		$load_comment_editor = ( !is_admin() && $geo_mashup_options->get( 'overall', 'located_object_name', 'comment' ) == 'true' ); 
+		$load_comment_editor = ( !is_admin() && $geo_mashup_options->get( 'overall', 'located_object_name', 'comment' ) == 'true' );
 		$load_comment_editor = apply_filters( 'geo_mashup_load_comment_editor', $load_comment_editor );
 
 		// If enabled, register all the interface elements
 		if ( $load_comment_editor ) {
 
@@ -893,11 +921,11 @@ class GeoMashupCommentUIManager {
 		$this->add_form_script = true;
 
 		// If there's a logged in user with a location, use that as a default.
 		// The client-side location will override it if available
 		$user = wp_get_current_user();
-		if ( $user ) 
+		if ( $user )
 			$default_location = GeoMashupDB::get_object_location( 'user', $user->ID );
 		if ( !$default_location )
 			$default_location = GeoMashupDB::blank_object_location();
 		$default_summary = ( empty( $default_location->locality_name ) ? '' : $default_location->locality_name . ', ' ) .
 				( empty( $default_location->admin_code ) ? '' : $default_location->admin_code );
@@ -905,29 +933,29 @@ class GeoMashupCommentUIManager {
 		// Print the form
 		printf( '<label id="geo-mashup-summary-label" for="geo-mashup-summary-input" style="display:none;">%s</label>', __( 'Written from (location)', 'GeoMashup' ) );
 		printf( '<input id="geo-mashup-summary-input" style="display:none;" type="text" size="25" value="%s" />', $default_summary );
 		printf( '<img id="geo-mashup-busy-icon" style="display:none;" src="%s" alt="%s" />', path_join( GEO_MASHUP_URL_PATH, 'images/busy_icon.gif' ), __( 'Loading...', 'GeoMashup' ) );
 		$input_format = '<input id="geo-mashup-%s-input" name="comment_location[%s]" type="hidden" value="%s" />';
-		printf( $input_format, 'lat', 'lat', $default_location->lat );
-		printf( $input_format, 'lng', 'lng', $default_location->lng );
+		printf( $input_format, 'lat', 'lat', esc_attr( $default_location->lat ) );
+		printf( $input_format, 'lng', 'lng', esc_attr( $default_location->lng ) );
 	}
 
 	/**
 	 * Print the form script in the footer if it's needed.
 	 *
 	 * @since 1.4
 	 */
 	public function wp_footer() {
 		global $geo_mashup_options;
 		if ( $this->add_form_script ) {
-			GeoMashup::register_script( 
-					'geo-mashup-comment-form', 
-					'js/comment-form.js', 
-					array( 'jquery' ), 
-					GEO_MASHUP_VERSION, 
+			GeoMashup::register_script(
+					'geo-mashup-comment-form',
+					'js/comment-form.js',
+					array( 'jquery' ),
+					GEO_MASHUP_VERSION,
 					true );
-					
+
 			wp_localize_script( 'geo-mashup-comment-form', 'geo_mashup_comment_form_settings', array( 'geonames_username' => $geo_mashup_options->get( 'overall', 'geonames_username' ) ) );
 			wp_print_scripts( 'geo-mashup-comment-form' );
 		}
 	}
 
@@ -943,11 +971,16 @@ class GeoMashupCommentUIManager {
 	public function save_comment( $comment_id = 0, $approval = '' ) {
 		if ( !$comment_id || 'spam' === $approval || empty( $_POST['comment_location'] ) || !is_array( $_POST['comment_location'] ) ) {
 			return false;
 		}
 
-		GeoMashupDB::set_object_location( 'comment', $comment_id, $_POST['comment_location'] );
+		$location = array(
+			'lat' => (float) $_POST['comment_location']['lat'],
+			'lng' => (float) $_POST['comment_location']['lng']
+		);
+
+		GeoMashupDB::set_object_location( 'comment', $comment_id, $location );
 	}
 }
 
 // Instantiate
 GeoMashupCommentUIManager::get_instance();
diff --git a/geo-mashup.php b/geo-mashup.php
index e3ede9b..d893eb7 100644
--- a/geo-mashup.php
+++ b/geo-mashup.php
@@ -713,12 +713,13 @@ class GeoMashup {
 			foreach( $post_ids as $post_id ) {
 				$urls = array_merge( $urls, self::get_kml_attachment_urls( $post_id ) );
 			}
 		}
 		$json = json_encode( $urls );
-		if ( isset( $_REQUEST['callback'] ) )
-			$json = $_REQUEST['callback'] . '(' . $json . ')';
+		if ( isset( $_REQUEST['callback'] ) ) {
+			$json = esc_js( $_REQUEST['callback'] ) . '(' . $json . ')';
+		}
 		echo $json;
 		exit();
 	}
 
 	/**
diff --git a/geo-query.php b/geo-query.php
index 1e68cf7..7d95d37 100644
--- a/geo-query.php
+++ b/geo-query.php
@@ -247,11 +247,11 @@ class GeoMashupQuery {
 		status_header(200);
 		header('Content-type: application/json; charset='.get_option('blog_charset'), true);
 		header('Cache-Control: no-cache;', true);
 		header('Expires: -1;', true);
 
-		$json = GeoMashup::get_locations_json($_REQUEST);
+		$json = GeoMashup::get_locations_json( GeoMashupDB::sanitize_query_args( $_REQUEST ) );
 		if ( isset( $_REQUEST['callback'] ) )
-			$json = $_REQUEST['callback'] . '(' . $json . ')';
+			$json = esc_js( $_REQUEST['callback'] ) . '(' . $json . ')';
 		echo $json;
 	}
 }

Vulnerable code: /** 
	 * 
		
		GeoMashup::register_script( 
				'mxn', 
				'js/mxn/mxn.js', 
				null, 
				GEO_MASHUP_VERSION , 
				
		GeoMashup::register_script( 
				'mxn-core', 
				'js/mxn/mxn.core.js', 
				array( 'mxn' ), 
				GEO_MASHUP_VERSION, 
					null, 
					'', 
					
			GeoMashup::register_script( 
					'mxn-google-3', 
					'js/mxn/mxn.googlev3.core.js', 
					array( 'mxn-core', 'google-maps-3' ), 
					GEO_MASHUP_VERSION, 
					
			GeoMashup::register_script( 
					'mxn-google-3-gm', 
					'js/mxn/mxn.googlev3.geo-mashup.js', 
					array( 'mxn-google-3' ), 
					GEO_MASHUP_VERSION, 
							
			wp_register_script( 
					'openlayers', 
					null, 
					'latest', 
					
			wp_register_script( 
					'openstreetmap', 
					 
			GeoMashup::register_script( 
					'js/mxn/mxn.openlayers.core.js', 
					array( 'mxn-core', 'openstreetmap' ), 
					GEO_MASHUP_VERSION, 
					
			GeoMashup::register_script( 
					'mxn-openlayers-gm', 
					'js/mxn/mxn.openlayers.geo-mashup.js', 
					array( 'mxn-openlayers' ), 
					GEO_MASHUP_VERSION, 
					
		GeoMashup::register_script( 
				'geo-mashup-location-editor', 
				'js/location-editor.js', 
				$required_scripts, 
				GEO_MASHUP_VERSION, 
				
		GeoMashup::register_script( 
				'jquery-ui-datepicker', 
				'js/jquery-ui.1.7.3.datepicker.js', 
				array( 'jquery', 'jquery-ui-core'), 
				'1.7.3', 
				
				wp_enqueue_script( 
					'geo-mashup-location-editor-custom', 
					$custom_url, 
	 * 
				 
	 * 
		
		if ( 'save' == $action or 'geocode' == $action ) {
			$date_string = $_POST['geo_mashup_date'] . ' ' . $_POST['geo_mashup_hour'] . ':' . 
				$_POST['geo_mashup_minute'] . ':00';
			$geo_date = date( 'Y-m-d H:i:s', strtotime( $date_string ) );
			// If PHP has added slashes, WP will do it again before saving
			$post_location['saved_name'] = stripslashes( $_POST['geo_mashup_location_name'] );
			if ( 'geocode' == $action ) {
				$status = GeoMashupDB::geocode( $_POST['geo_mashup_search'], $post_location );
				if ( $status != 200 ) {
					$post_location = array();
				}
			} else {
				if ( ! empty( $_POST['geo_mashup_select'] ) ) {
					$selected_items = explode( '|', $_POST['geo_mashup_select'] );
					$post_location = intval( $selected_items[0] );
				} else { 
					$post_location['id'] = $_POST['geo_mashup_location_id'];
					list( $lat, $lng ) = explode( ',', $_POST['geo_mashup_location'] );
					$post_location['lat'] = trim( $lat );
					$post_location['lng'] = trim( $lng );
					$post_location['geoname'] = $_POST['geo_mashup_geoname'];
					$post_location['address'] = stripslashes( $_POST['geo_mashup_address'] );
					$post_location['postal_code'] = $_POST['geo_mashup_postal_code'];
					$post_location['country_code'] = $_POST['geo_mashup_country_code'];
					$post_location['admin_code'] = $_POST['geo_mashup_admin_code'];
					$post_location['sub_admin_code'] = $_POST['geo_mashup_sub_admin_code'];
					$post_location['locality_name'] = $_POST['geo_mashup_locality_name'];
					if ( !empty( $_POST['geo_mashup_null_fields'] ) )
						$post_location['set_null'] = $_POST['geo_mashup_null_fields'];
				}
			}
			
			if ( ! empty( $post_location ) ) {
				$error = GeoMashupDB::set_object_location( $object_name, $object_id, $post_location, true, $geo_date );
				if ( is_wp_error( $error ) ) 
					return $error;
			}
		} else if ( 'delete' == $action ) {
			if ( is_wp_error( $error ) ) 
				return $error;
		// If geodata was manually updated but Geo Mashup location was not,
		// they may be out of sync now. Allowing that for now.
		return true;
	 * 
	 * @global array $geo_mashup_options 
		if ( $enabled ) { 
	 * 
			$object_id = $_GET['user_id'];
	 * 
	 * @return bool|WP_Error 
		$user_id = $_POST['user_id'];

		if ( !is_numeric( $user_id ) ) {
			return $user_id;
		}
	 * 
	 * 
	 * 
	 * @global array $geo_mashup_options 
		} 
	 * 
	 * 
	 * 
	 * @param id $post_id 
	 * @param object $post 
	 * 
	 * 
	 * @param array $shortcode_match 
		$tag_index = array_search( 'geo_mashup_save_location',  $shortcode_match ); 
		} 
	 * 
	 * 
		
	 * 
	 * If an upload is KML, put the URL in an option to be loaded in the response 
	 * 
	 * 
	 * 
	 * @global array $geo_mashup_options 
		$load_comment_editor = ( !is_admin() && $geo_mashup_options->get( 'overall', 'located_object_name', 'comment' ) == 'true' ); 
		if ( $user ) 
		printf( $input_format, 'lat', 'lat', $default_location->lat );
		printf( $input_format, 'lng', 'lng', $default_location->lng );
			GeoMashup::register_script( 
					'geo-mashup-comment-form', 
					'js/comment-form.js', 
					array( 'jquery' ), 
					GEO_MASHUP_VERSION, 
					
		GeoMashupDB::set_object_location( 'comment', $comment_id, $_POST['comment_location'] );
		if ( isset( $_REQUEST['callback'] ) )
			$json = $_REQUEST['callback'] . '(' . $json . ')';
		$json = GeoMashup::get_locations_json($_REQUEST);
			$json = $_REQUEST['callback'] . '(' . $json . ')';
Fixed code: 	/**
	 * Sanitize an array of query arguments.
	 *
	 * @param array $query_args
	 *
	 * @return array
	 */
	public static function sanitize_query_args( $query_args ) {
		array_walk_recursive($query_args, array( __CLASS__, 'sanitize_query_arg' ) );
		return $query_args;
	}

	/**
	 * Sanitize a single query argument.
	 *
	 * @param mixed $value May be modified.
	 * @param string $name
	 */
	public static function sanitize_query_arg( &$value, $name ) {
		switch ($name) {
			case 'minlat':
			case 'maxlat':
			case 'minlng':
			case 'maxlng':
			case 'near_lat':
			case 'near_lng':
			case 'radius_km':
			case 'radius_mi':
				$value = (float) $value;
				break;

			case 'map_cat':
			case 'object_ids':
				$value = preg_replace( '/[^0-9,]', '', $value );
				break;

			case 'map_post_type':
			case 'object_name':
				$value = sanitize_key( $value );
				break;

			case 'limit':
			case 'map_offset':
				$value = (int) $value;
				break;

			case 'suppress_filters':
				$value = (bool) $value;
				break;

			default:
				$value = sanitize_text_field( $value );
		}
	}

/**
	 *

		GeoMashup::register_script(
				'mxn',
				'js/mxn/mxn.js',
				null,
				GEO_MASHUP_VERSION ,

		GeoMashup::register_script(
				'mxn-core',
				'js/mxn/mxn.core.js',
				array( 'mxn' ),
				GEO_MASHUP_VERSION,
					null,
					'',

			GeoMashup::register_script(
					'mxn-google-3',
					'js/mxn/mxn.googlev3.core.js',
					array( 'mxn-core', 'google-maps-3' ),
					GEO_MASHUP_VERSION,

			GeoMashup::register_script(
					'mxn-google-3-gm',
					'js/mxn/mxn.googlev3.geo-mashup.js',
					array( 'mxn-google-3' ),
					GEO_MASHUP_VERSION,

			wp_register_script(
					'openlayers',
					null,
					'latest',

			wp_register_script(
					'openstreetmap',

			GeoMashup::register_script(
					'js/mxn/mxn.openlayers.core.js',
					array( 'mxn-core', 'openstreetmap' ),
					GEO_MASHUP_VERSION,

			GeoMashup::register_script(
					'mxn-openlayers-gm',
					'js/mxn/mxn.openlayers.geo-mashup.js',
					array( 'mxn-openlayers' ),
					GEO_MASHUP_VERSION,

		GeoMashup::register_script(
				'geo-mashup-location-editor',
				'js/location-editor.js',
				$required_scripts,
				GEO_MASHUP_VERSION,

		GeoMashup::register_script(
				'jquery-ui-datepicker',
				'js/jquery-ui.1.7.3.datepicker.js',
				array( 'jquery', 'jquery-ui-core'),
				'1.7.3',

				wp_enqueue_script(
					'geo-mashup-location-editor-custom',
					$custom_url,
	 *

	 *

		$post_location = array();
		$search_text = $geo_date = '';

		if ( 'save' === $action || 'geocode' === $action ) {

			$date_string = sanitize_text_field( $_POST['geo_mashup_date'] ) . ' ' .
			               (int) $_POST['geo_mashup_hour'] . ':' .
			               (int) $_POST['geo_mashup_minute'] . ':00';
			$geo_date    = date( 'Y-m-d H:i:s', strtotime( $date_string ) );

			$post_location['saved_name'] = sanitize_text_field( wp_unslash( $_POST['geo_mashup_location_name'] ) );

			$search_text = sanitize_text_field( $_POST['geo_mashup_search'] );
		}

		if ( 'geocode' === $action && ! GeoMashupDB::geocode( $search_text, $post_location ) ) {
		}
		if ( 'save' === $action && ! empty( $_POST['geo_mashup_select'] ) ) {
			$selected_items = explode( '|', $_POST['geo_mashup_select'] );
			$post_location  = empty( $selected_items ) ? 0 : (int) $selected_items[0];
		}

		if ( 'save' === $action && empty( $_POST['geo_mashup_select'] ) ) {
			$post_location['id'] = (int) $_POST['geo_mashup_location_id'];
			list( $lat, $lng ) = explode( ',', $_POST['geo_mashup_location'] );
			$post_location['lat']            = (float) $lat;
			$post_location['lng']            = (float) $lng;
			$post_location['geoname']        = sanitize_text_field( $_POST['geo_mashup_geoname'] );
			$post_location['address']        = sanitize_text_field( wp_unslash( $_POST['geo_mashup_address'] ) );
			$post_location['postal_code']    = sanitize_text_field( $_POST['geo_mashup_postal_code'] );
			$post_location['country_code']   = $this->sanitize_country_code( $_POST['geo_mashup_country_code'] );
			$post_location['admin_code']     = sanitize_text_field( $_POST['geo_mashup_admin_code'] );
			$post_location['sub_admin_code'] = sanitize_text_field( $_POST['geo_mashup_sub_admin_code'] );
			$post_location['locality_name']  = sanitize_text_field( $_POST['geo_mashup_locality_name'] );
			$post_location['set_null']       = $this->sanitize_null_fields( $_POST['geo_mashup_null_fields'] );
		}

		$error = null;
		if ( ! empty( $post_location ) ) {

			$error = GeoMashupDB::set_object_location(
				$object_name,
				$object_id,
				$post_location,
				true,
				$geo_date
			);

		}

		if ( 'delete' === $action ) {

		return is_wp_error( $error ) ? $error : true;
	}

	/**
	 * Sanitize country codes to two uppercase alpha characters.
	 *
	 * @param string $code
	 *
	 * @return string
	 */
	private function sanitize_country_code( $code ) {
		return substr( preg_replace( '/[^A-Z]/', '', $code), 0, 2 );
	}

	/**
	 * Sanitize null fields to null or a safe string.
	 *
	 * @param string $fields
	 *
	 * @return string
	 */
	private function sanitize_null_fields( $fields ) {
		return empty( $fields ) ? null : sanitize_text_field( $fields );
	 *
	 * @global array $geo_mashup_options
		if ( $enabled ) {
	 *
			$object_id = (int) $_GET['user_id'];
	 *
	 * @return bool|WP_Error
		$user_id = (int) $_POST['user_id'];
	 *
	 *
	 *
	 * @global array $geo_mashup_options
		}
	 *
	 *
	 *
	 * @param id $post_id
	 * @param object $post
	 *
	 *
	 * @param array $shortcode_match
		$tag_index = array_search( 'geo_mashup_save_location',  $shortcode_match );
		}
	 *
	 *

	 *
	 * If an upload is KML, put the URL in an option to be loaded in the response
	 *
	 *
	 *
	 * @global array $geo_mashup_options
		$load_comment_editor = ( !is_admin() && $geo_mashup_options->get( 'overall', 'located_object_name', 'comment' ) == 'true' );
		if ( $user )
		printf( $input_format, 'lat', 'lat', esc_attr( $default_location->lat ) );
		printf( $input_format, 'lng', 'lng', esc_attr( $default_location->lng ) );
			GeoMashup::register_script(
					'geo-mashup-comment-form',
					'js/comment-form.js',
					array( 'jquery' ),
					GEO_MASHUP_VERSION,

		$location = array(
			'lat' => (float) $_POST['comment_location']['lat'],
			'lng' => (float) $_POST['comment_location']['lng']
		);

		GeoMashupDB::set_object_location( 'comment', $comment_id, $location );
		if ( isset( $_REQUEST['callback'] ) ) {
			$json = esc_js( $_REQUEST['callback'] ) . '(' . $json . ')';
		}
		$json = GeoMashup::get_locations_json( GeoMashupDB::sanitize_query_args( $_REQUEST ) );
			$json = esc_js( $_REQUEST['callback'] ) . '(' . $json . ')';