Changeset 3440575
- Timestamp:
- 01/15/2026 06:30:29 PM (8 weeks ago)
- Location:
- yamaps
- Files:
-
- 6 edited
- 9 copied
-
tags/0.6.40 (copied) (copied from yamaps/trunk)
-
tags/0.6.40/includes/admin.php (copied) (copied from yamaps/trunk/includes/admin.php)
-
tags/0.6.40/includes/init.php (copied) (copied from yamaps/trunk/includes/init.php)
-
tags/0.6.40/includes/shortcodes.php (copied) (copied from yamaps/trunk/includes/shortcodes.php)
-
tags/0.6.40/js/btn.js (copied) (copied from yamaps/trunk/js/btn.js)
-
tags/0.6.40/js/shortcode_parser.js (copied) (copied from yamaps/trunk/js/shortcode_parser.js)
-
tags/0.6.40/options.php (copied) (copied from yamaps/trunk/options.php)
-
tags/0.6.40/readme.txt (copied) (copied from yamaps/trunk/readme.txt)
-
tags/0.6.40/yamap.php (copied) (copied from yamaps/trunk/yamap.php)
-
trunk/includes/api.php (modified) (1 diff)
-
trunk/includes/init.php (modified) (1 diff)
-
trunk/includes/shortcodes.php (modified) (6 diffs)
-
trunk/options.php (modified) (3 diffs)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/yamap.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
yamaps/trunk/includes/api.php
r3235800 r3440575 1 1 <?php 2 /** 3 * Sanitize API key - only allow alphanumeric, hyphens, underscores 4 * 5 * @param string $key API key 6 * @return string Sanitized API key 7 */ 8 function yamaps_sanitize_apikey( string $key ): string { 9 $key = sanitize_text_field( $key ); 10 // Yandex API keys are typically UUID-like strings 11 if ( preg_match( '/^[a-zA-Z0-9\-_]*$/', $key ) ) { 12 return $key; 13 } 14 return ''; 15 } 16 17 /** 18 * Sanitize locale string 19 * 20 * @param string $locale Locale string 21 * @return string Sanitized locale 22 */ 23 function yamaps_sanitize_locale( string $locale ): string { 24 // Locale should be like "en_US", "ru_RU", etc. 25 if ( preg_match( '/^[a-z]{2}_[A-Z]{2}$/', $locale ) ) { 26 return $locale; 27 } 28 return 'en_US'; 29 } 30 2 31 // New Yandex Map API call 3 32 function YandexMapAPI_script($noFooter = false) { 4 33 global $yamaps_defaults_front, $apikey, $post; 5 $maplocale = get_locale(); 6 if (strlen($maplocale)<5) $maplocale = "en_US"; 7 if (trim($yamaps_defaults_front['apikey_map_option'])<>"") { 8 $apikey='&apikey='.esc_html($yamaps_defaults_front['apikey_map_option']); 34 35 $maplocale = yamaps_sanitize_locale( get_locale() ); 36 37 $safe_apikey = ''; 38 if ( ! empty( $yamaps_defaults_front['apikey_map_option'] ) ) { 39 $safe_apikey = yamaps_sanitize_apikey( $yamaps_defaults_front['apikey_map_option'] ); 9 40 } 10 else {11 $apikey ='';12 }41 42 $apikey_param = $safe_apikey !== '' ? '&apikey=' . rawurlencode( $safe_apikey ) : ''; 43 13 44 if ($noFooter) { 14 $AltApiSrc = 'https://api-maps.yandex.ru/2.1/?lang='.esc_html($maplocale).esc_html($apikey).'&ver=2.1'; 15 $AltApiSrc = str_replace("&", "&", $AltApiSrc); 45 $AltApiSrc = 'https://api-maps.yandex.ru/2.1/?lang=' . rawurlencode( $maplocale ) . $apikey_param . '&ver=2.1'; 16 46 return $AltApiSrc; 17 47 } 18 48 else { 19 49 if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'yamap') ) { 20 wp_register_script( 'YandexMapAPI', 'https://api-maps.yandex.ru/2.1/?lang='.esc_html($maplocale).esc_html($apikey), [], 2.1, true ); 50 $api_url = 'https://api-maps.yandex.ru/2.1/?lang=' . rawurlencode( $maplocale ) . $apikey_param; 51 wp_register_script( 'YandexMapAPI', $api_url, [], '2.1', true ); 21 52 wp_enqueue_script( 'YandexMapAPI' ); 22 53 } -
yamaps/trunk/includes/init.php
r3412598 r3440575 11 11 $apikey = ''; 12 12 13 // Get API key from options 13 // Get API key from options (sanitized later in api.php) 14 14 $yamaps_options = get_option('yamaps_options'); 15 if (!empty($yamaps_options['apikey_map_option'])) { 16 $apikey = '&apikey=' . esc_attr($yamaps_options['apikey_map_option']); 15 if ( ! empty( $yamaps_options['apikey_map_option'] ) ) { 16 // Sanitize: only allow alphanumeric, hyphens, underscores 17 $raw_apikey = sanitize_text_field( $yamaps_options['apikey_map_option'] ); 18 if ( preg_match( '/^[a-zA-Z0-9\-_]*$/', $raw_apikey ) ) { 19 $apikey = '&apikey=' . rawurlencode( $raw_apikey ); 20 } else { 21 $apikey = ''; 22 } 17 23 } else { 18 24 $apikey = ''; -
yamaps/trunk/includes/shortcodes.php
r3412598 r3440575 1 1 <?php 2 3 /** 4 * Sanitize coordinates string (e.g., "55.7558, 37.6173") 5 * Only allows numbers, dots, commas, spaces, and minus signs 6 * 7 * @param string $coord Coordinate string 8 * @return string Sanitized coordinate string 9 */ 10 function yamaps_sanitize_coords( string $coord ): string { 11 // Remove all characters except numbers, dots, commas, spaces, and minus 12 $sanitized = preg_replace( '/[^0-9.,\s\-]/', '', $coord ); 13 14 // Additional validation: should match pattern like "55.7558, 37.6173" 15 if ( preg_match( '/^-?\d+\.?\d*\s*,\s*-?\d+\.?\d*$/', trim( $sanitized ) ) ) { 16 return trim( $sanitized ); 17 } 18 19 // Return default Moscow coordinates if invalid 20 return '55.7558, 37.6173'; 21 } 22 23 /** 24 * Sanitize zoom level (integer 0-21) 25 * 26 * @param mixed $zoom Zoom value 27 * @return int Sanitized zoom level 28 */ 29 function yamaps_sanitize_zoom( $zoom ): int { 30 $zoom_int = intval( $zoom ); 31 32 // Yandex Maps zoom range is 0-21 33 if ( $zoom_int < 0 ) { 34 return 0; 35 } 36 if ( $zoom_int > 21 ) { 37 return 21; 38 } 39 40 return $zoom_int; 41 } 42 43 /** 44 * Sanitize map type and return full Yandex type string 45 * 46 * @param string $type Map type 47 * @return string Sanitized map type (always with yandex# prefix) 48 */ 49 function yamaps_sanitize_map_type( string $type ): string { 50 // Map short names to full Yandex type names 51 $type_map = array( 52 'map' => 'yandex#map', 53 'satellite' => 'yandex#satellite', 54 'hybrid' => 'yandex#hybrid', 55 'yandex#map' => 'yandex#map', 56 'yandex#satellite' => 'yandex#satellite', 57 'yandex#hybrid' => 'yandex#hybrid', 58 ); 59 60 $type = sanitize_text_field( $type ); 61 62 return isset( $type_map[ $type ] ) ? $type_map[ $type ] : 'yandex#map'; 63 } 64 65 /** 66 * Sanitize controls string 67 * 68 * @param string $controls Controls string (semicolon-separated) 69 * @return string Sanitized controls for JS array 70 */ 71 function yamaps_sanitize_controls( string $controls ): string { 72 $allowed_controls = array( 73 'fullscreenControl', 74 'geolocationControl', 75 'routeEditor', 76 'rulerControl', 77 'searchControl', 78 'trafficControl', 79 'typeSelector', 80 'zoomControl', 81 'routeButtonControl', 82 'routePanelControl', 83 'smallMapDefaultSet', 84 'mediumMapDefaultSet', 85 'largeMapDefaultSet', 86 'default', 87 ); 88 89 $controls_array = array_map( 'trim', explode( ';', $controls ) ); 90 $sanitized = array(); 91 92 foreach ( $controls_array as $control ) { 93 $control = sanitize_text_field( $control ); 94 if ( in_array( $control, $allowed_controls, true ) ) { 95 $sanitized[] = '"' . esc_js( $control ) . '"'; 96 } 97 } 98 99 return implode( ', ', $sanitized ); 100 } 101 2 102 // Placemark shortcode function 3 103 function yaplacemark_func($atts) { … … 31 131 } 32 132 133 // Sanitize coordinates to prevent XSS 134 $safe_coord = yamaps_sanitize_coords( $atts["coord"] ); 135 33 136 $yaplacemark=' 34 YaMapsWP.myMap'.$maps_count.'.places.placemark'.$yaplacemark_count.' = {icon: "'.esc_js($atts["icon"]).'", name: "'.esc_js($atts["name"]).'", color: "'.esc_js($atts["color"]).'", coord: "'.esc_js($ atts["coord"]).'", url: "'.esc_url($atts["url"]).'",};35 myMap'.$maps_count.'placemark'.$yaplacemark_count.' = new ymaps.Placemark(['.$ atts["coord"].'], {137 YaMapsWP.myMap'.$maps_count.'.places.placemark'.$yaplacemark_count.' = {icon: "'.esc_js($atts["icon"]).'", name: "'.esc_js($atts["name"]).'", color: "'.esc_js($atts["color"]).'", coord: "'.esc_js($safe_coord).'", url: "'.esc_url($atts["url"]).'",}; 138 myMap'.$maps_count.'placemark'.$yaplacemark_count.' = new ymaps.Placemark(['.$safe_coord.'], { 36 139 hintContent: "'.esc_js($yahint).'", 37 140 iconContent: "'.esc_js($yacontent).'", … … 111 214 } 112 215 113 $yamactrl = str_replace(';', '", "', esc_js($atts["controls"])); 114 if (trim($yamactrl) != "") $yamactrl = '"'.$yamactrl.'"'; 216 // Sanitize all map parameters to prevent XSS 217 $safe_center = yamaps_sanitize_coords( $atts["center"] ); 218 $safe_zoom = yamaps_sanitize_zoom( $atts["zoom"] ); 219 $safe_type = yamaps_sanitize_map_type( $atts["type"] ); 220 $safe_controls = yamaps_sanitize_controls( $atts["controls"] ); 115 221 116 222 if (($yamap_load_api)) { … … 138 244 $placemarkscode = $safe_content; 139 245 140 $atts["container"]=trim($atts["container"]); 141 if ($atts["container"]<>"") { 142 $mapcontainter=esc_html($atts["container"]); 143 $mapcontainter=str_replace("#", "", $mapcontainter); 144 } 145 else { 146 $mapcontainter='yamap'.$current_map_index; 246 // Sanitize container ID - only allow alphanumeric, hyphens, underscores 247 $atts["container"] = trim( $atts["container"] ); 248 if ( $atts["container"] !== "" ) { 249 $mapcontainter = preg_replace( '/[^a-zA-Z0-9_\-]/', '', $atts["container"] ); 250 // Ensure ID doesn't start with a number (invalid HTML ID) 251 if ( preg_match( '/^[0-9]/', $mapcontainter ) ) { 252 $mapcontainter = 'yamap-' . $mapcontainter; 253 } 254 } else { 255 $mapcontainter = 'yamap' . $current_map_index; 147 256 } 148 257 … … 184 293 185 294 YMlisteners.myMap'.$current_map_index.' = {}; 186 YaMapsWP.myMap'.$current_map_index.' = {center: "'.esc_js($ atts["center"]).'", zoom: "'.esc_js($atts["zoom"]).'", type: "'.esc_js($atts["type"]).'", controls: "'.esc_js($atts["controls"]).'", places: {}};295 YaMapsWP.myMap'.$current_map_index.' = {center: "'.esc_js($safe_center).'", zoom: '.$safe_zoom.', type: "'.esc_js($safe_type).'", controls: "'.esc_js($atts["controls"]).'", places: {}}; 187 296 188 297 var yamapsonclick = function (url) { … … 192 301 function init () { 193 302 myMap'.$current_map_index.' = new ymaps.Map("'.$mapcontainter.'", { 194 center: ['. sanitize_text_field($atts["center"]).'],195 zoom: '. sanitize_text_field($atts["zoom"]).',196 type: "'. sanitize_text_field($atts["type"]).'",197 controls: ['. sanitize_text_field($yamactrl).'] ,303 center: ['.$safe_center.'], 304 zoom: '.$safe_zoom.', 305 type: "'.$safe_type.'", 306 controls: ['.$safe_controls.'] , 198 307 199 308 }, -
yamaps/trunk/options.php
r3412598 r3440575 65 65 function init () { 66 66 var testvar=document.getElementById('center_map_option').value; 67 var apikeyexist = false, apikey=<?php echo '"'.$apikey.'"'?>;67 var apikeyexist = false, apikey=<?php echo wp_json_encode( $apikey ); ?>; 68 68 if (apikey!=="") apikeyexist=true; 69 69 var controlsArr=["zoomControl", "typeSelector"]; 70 70 if (apikeyexist) controlsArr.push("searchControl"); // If the API key is defined, add search to the map. Without a key, it won't work anyway and will throw an error. 71 71 72 <?php 73 // Sanitize center coordinates for admin page 74 $admin_center = yamaps_sanitize_coords( $yamaps_defaults["center_map_option"] ); 75 $admin_zoom = yamaps_sanitize_zoom( $yamaps_defaults["zoom_map_option"] ); 76 $admin_type = yamaps_sanitize_map_type( $yamaps_defaults["type_map_option"] ); 77 ?> 72 78 var myMap0 = new ymaps.Map("yamap", { 73 center: [<?php echo $yamaps_defaults["center_map_option"]; ?>],74 zoom: <?php echo $yamaps_defaults["zoom_map_option"]; ?>,75 type: '<?php echo $yamaps_defaults["type_map_option"]; ?>',79 center: [<?php echo esc_js( $admin_center ); ?>], 80 zoom: <?php echo (int) $admin_zoom; ?>, 81 type: <?php echo wp_json_encode( $admin_type ); ?>, 76 82 controls: controlsArr 77 83 }); 78 84 79 85 // Add a sample placemark 80 placemark1 = new ymaps.Placemark([<?php echo $yamaps_defaults["center_map_option"]; ?>], {86 placemark1 = new ymaps.Placemark([<?php echo esc_js( $admin_center ); ?>], { 81 87 hintContent: "Placemark", 82 88 iconContent: "", … … 86 92 <?php 87 93 // Check if the icon field is a URL. If yes, set a custom image as the icon. 88 $iconurl = strripos($yamaps_defaults["type_icon_option"], 'http'); 89 if (is_int($iconurl)) { 90 echo ' 91 iconLayout: "default#image", 92 iconImageHref: "'.$yamaps_defaults["type_icon_option"].'" 93 94 '; 95 96 } 97 else { 98 echo ' 99 preset: "'.$yamaps_defaults["type_icon_option"].'", 100 iconColor: "'.$yamaps_defaults["color_icon_option"].'", 101 102 '; 103 } 94 // Sanitize icon option 95 $safe_icon = esc_js( $yamaps_defaults["type_icon_option"] ); 96 $safe_color = esc_js( $yamaps_defaults["color_icon_option"] ); 97 $iconurl = strripos( $yamaps_defaults["type_icon_option"], 'http' ); 98 if ( is_int( $iconurl ) ) { 99 echo ' 100 iconLayout: "default#image", 101 iconImageHref: "' . esc_url( $yamaps_defaults["type_icon_option"] ) . '" 102 103 '; 104 105 } 106 else { 107 echo ' 108 preset: "' . $safe_icon . '", 109 iconColor: "' . $safe_color . '", 110 111 '; 112 } 104 113 105 114 ?> … … 410 419 } 411 420 412 return $input; 421 $sanitized = array(); 422 423 // Sanitize center coordinates 424 if ( isset( $input['center_map_option'] ) ) { 425 $sanitized['center_map_option'] = yamaps_sanitize_coords( $input['center_map_option'] ); 426 } 427 428 // Sanitize zoom level (0-21) 429 if ( isset( $input['zoom_map_option'] ) ) { 430 $sanitized['zoom_map_option'] = (string) yamaps_sanitize_zoom( $input['zoom_map_option'] ); 431 } 432 433 // Sanitize map type 434 if ( isset( $input['type_map_option'] ) ) { 435 $sanitized['type_map_option'] = yamaps_sanitize_map_type( $input['type_map_option'] ); 436 } 437 438 // Sanitize height (allow only valid CSS units) 439 if ( isset( $input['height_map_option'] ) ) { 440 $height = sanitize_text_field( $input['height_map_option'] ); 441 // Validate CSS height format: number + unit (rem, em, px, %, vh) 442 if ( preg_match( '/^\d+(\.\d+)?(rem|em|px|%|vh)$/', $height ) ) { 443 $sanitized['height_map_option'] = $height; 444 } else { 445 $sanitized['height_map_option'] = '22rem'; // Default 446 } 447 } 448 449 // Sanitize controls (semicolon-separated list of allowed controls) 450 if ( isset( $input['controls_map_option'] ) ) { 451 $allowed_controls = array( 452 'fullscreenControl', 453 'geolocationControl', 454 'routeEditor', 455 'rulerControl', 456 'searchControl', 457 'trafficControl', 458 'typeSelector', 459 'zoomControl', 460 'routeButtonControl', 461 'routePanelControl', 462 'smallMapDefaultSet', 463 'mediumMapDefaultSet', 464 'largeMapDefaultSet', 465 'default', 466 ); 467 $controls_array = array_map( 'trim', explode( ';', $input['controls_map_option'] ) ); 468 $valid_controls = array(); 469 foreach ( $controls_array as $control ) { 470 $control = sanitize_text_field( $control ); 471 if ( in_array( $control, $allowed_controls, true ) ) { 472 $valid_controls[] = $control; 473 } 474 } 475 $sanitized['controls_map_option'] = implode( ';', $valid_controls ); 476 } 477 478 // Sanitize checkboxes (on/off) 479 $checkbox_fields = array( 480 'wheelzoom_map_option', 481 'mobiledrag_map_option', 482 'cluster_map_option', 483 'open_map_option', 484 'authorlink_map_option', 485 'reset_maps_option', 486 ); 487 foreach ( $checkbox_fields as $field ) { 488 $sanitized[ $field ] = isset( $input[ $field ] ) && $input[ $field ] === 'on' ? 'on' : 'off'; 489 } 490 491 // Sanitize cluster grid size (power of 2 or reasonable integer) 492 if ( isset( $input['cluster_grid_option'] ) ) { 493 $grid = intval( $input['cluster_grid_option'] ); 494 $sanitized['cluster_grid_option'] = ( $grid >= 2 && $grid <= 512 ) ? (string) $grid : '64'; 495 } 496 497 // Sanitize icon type (alphanumeric, #, URL allowed) 498 if ( isset( $input['type_icon_option'] ) ) { 499 $icon = sanitize_text_field( $input['type_icon_option'] ); 500 // Allow Yandex preset names or URLs 501 if ( preg_match( '/^[a-zA-Z0-9#_\-]+$/', $icon ) || filter_var( $icon, FILTER_VALIDATE_URL ) ) { 502 $sanitized['type_icon_option'] = $icon; 503 } else { 504 $sanitized['type_icon_option'] = 'islands#dotIcon'; 505 } 506 } 507 508 // Sanitize icon color (hex color) 509 if ( isset( $input['color_icon_option'] ) ) { 510 $color = sanitize_hex_color( $input['color_icon_option'] ); 511 $sanitized['color_icon_option'] = $color ? $color : '#1e98ff'; 512 } 513 514 // Sanitize API key (alphanumeric, hyphens, underscores) 515 if ( isset( $input['apikey_map_option'] ) ) { 516 $apikey = sanitize_text_field( $input['apikey_map_option'] ); 517 // Yandex API keys are typically UUID-like 518 if ( preg_match( '/^[a-zA-Z0-9\-_]*$/', $apikey ) ) { 519 $sanitized['apikey_map_option'] = $apikey; 520 } else { 521 $sanitized['apikey_map_option'] = ''; 522 } 523 } 524 525 return $sanitized; 413 526 } 414 527 -
yamaps/trunk/readme.txt
r3412601 r3440575 102 102 == Changelog == 103 103 104 = 0.6.41 = 105 * Code refactoring. 106 * Security improvements. 107 104 108 = 0.6.40 = 105 109 * Cluster support for placemarks -
yamaps/trunk/yamap.php
r3412598 r3440575 6 6 * Author URI: www.yhunter.ru 7 7 * Author: Yuri Baranov 8 * Version: 0.6.4 08 * Version: 0.6.41 9 9 * 10 10 *
Note: See TracChangeset
for help on using the changeset viewer.