Plugin Directory

Changeset 3440575


Ignore:
Timestamp:
01/15/2026 06:30:29 PM (8 weeks ago)
Author:
yhunter
Message:

plugin update

Location:
yamaps
Files:
6 edited
9 copied

Legend:

Unmodified
Added
Removed
  • yamaps/trunk/includes/api.php

    r3235800 r3440575  
    11<?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 */
     8function 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 */
     23function 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
    231// New Yandex Map API call
    332function YandexMapAPI_script($noFooter = false) { 
    433    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'] );
    940    }
    10     else {
    11         $apikey = '';
    12     }
     41   
     42    $apikey_param = $safe_apikey !== '' ? '&apikey=' . rawurlencode( $safe_apikey ) : '';
     43   
    1344    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("&amp;", "&", $AltApiSrc);
     45        $AltApiSrc = 'https://api-maps.yandex.ru/2.1/?lang=' . rawurlencode( $maplocale ) . $apikey_param . '&ver=2.1';
    1646        return $AltApiSrc;
    1747    }
    1848    else {
    1949        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 ); 
    2152            wp_enqueue_script( 'YandexMapAPI' );
    2253        }   
  • yamaps/trunk/includes/init.php

    r3412598 r3440575  
    1111$apikey = '';
    1212
    13 // Get API key from options
     13// Get API key from options (sanitized later in api.php)
    1414$yamaps_options = get_option('yamaps_options');
    15 if (!empty($yamaps_options['apikey_map_option'])) {
    16     $apikey = '&apikey=' . esc_attr($yamaps_options['apikey_map_option']);
     15if ( ! 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    }
    1723} else {
    1824    $apikey = '';
  • yamaps/trunk/includes/shortcodes.php

    r3412598 r3440575  
    11<?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 */
     10function 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 */
     29function 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 */
     49function 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 */
     71function 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
    2102// Placemark shortcode function
    3103function yaplacemark_func($atts) {
     
    31131    }
    32132   
     133    // Sanitize coordinates to prevent XSS
     134    $safe_coord = yamaps_sanitize_coords( $atts["coord"] );
     135   
    33136    $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.'], {
    36139                                hintContent: "'.esc_js($yahint).'",
    37140                                iconContent: "'.esc_js($yacontent).'",
     
    111214    }
    112215
    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"] );
    115221
    116222    if (($yamap_load_api)) {
     
    138244    $placemarkscode = $safe_content;
    139245
    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;
    147256    }   
    148257   
     
    184293                   
    185294                    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: {}};
    187296
    188297                    var yamapsonclick = function (url) {
     
    192301                    function init () {
    193302                        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.'] ,
    198307                               
    199308                            },
  • yamaps/trunk/options.php

    r3412598 r3440575  
    6565                        function init () {
    6666                            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 ); ?>;
    6868                            if (apikey!=="") apikeyexist=true;
    6969                            var controlsArr=["zoomControl", "typeSelector"];
    7070                            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.
    7171
     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                            ?>
    7278                            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 ); ?>,
    7682                                    controls: controlsArr
    7783                                });   
    7884
    7985                            // 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 ); ?>], {
    8187                                hintContent: "Placemark",
    8288                                iconContent: "",
     
    8692                                <?php
    8793                                    // 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                                }
    104113
    105114                                ?>
     
    410419    }
    411420
    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;
    413526}
    414527
  • yamaps/trunk/readme.txt

    r3412601 r3440575  
    102102== Changelog ==
    103103
     104= 0.6.41 =
     105* Code refactoring.
     106* Security improvements.
     107
    104108= 0.6.40 =
    105109* Cluster support for placemarks
  • yamaps/trunk/yamap.php

    r3412598 r3440575  
    66 * Author URI:  www.yhunter.ru
    77 * Author:      Yuri Baranov
    8  * Version:     0.6.40
     8 * Version:     0.6.41
    99 *
    1010 *
Note: See TracChangeset for help on using the changeset viewer.