Plugin Directory

Changeset 3480942


Ignore:
Timestamp:
03/12/2026 08:42:24 AM (3 weeks ago)
Author:
prasunsen
Message:

security updates

Location:
hostel/trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • hostel/trunk/controllers/bookings.php

    r3478265 r3480942  
    77        switch($_GET['do'] ?? null) {
    88            case 'add':
    9                 if(!empty($_POST['ok'])) {
     9                if(!empty($_POST['ok']) and check_admin_referer('wphostel_booking')) {
    1010                    $_POST['from_date'] = $_POST['fromyear'].'-'.$_POST['frommonth'].'-'.$_POST['fromday'];
    1111                    $_POST['to_date'] = $_POST['toyear'].'-'.$_POST['tomonth'].'-'.$_POST['today'];
     
    6161                // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.NoCaching
    6262                $booking = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".WPHOSTEL_BOOKINGS." WHERE id=%d", (int)$_GET['id']));
    63                 // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.DirectQuery
    64                 // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.NoCaching
    65                 $room = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".WPHOSTEL_ROOMS." WHERE id=%d", $booking['room_id']));
     63               
     64                if (empty($booking)) {
     65                    wp_die('Booking not found');
     66                }
     67
     68                // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.DirectQuery
     69                // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.NoCaching
     70                $room = $wpdb->get_row($wpdb->prepare("SELECT * FROM ".WPHOSTEL_ROOMS." WHERE id=%d", (int)$booking->room_id));
    6671
    6772                if(@file_exists(get_stylesheet_directory().'/wphostel/view-booking.html.php')) include get_stylesheet_directory().'/wphostel/view-booking.html.php';
     
    7580                $type = empty($_GET['type']) ? 'upcoming' : sanitize_text_field($_GET['type']);
    7681                $offset = empty($_GET['offset']) ? 0 : intval($_GET['offset']);
    77                 $dir = empty($_GET['dir']) ? 'ASC' : $_GET['dir'];
    78                 if($dir != 'ASC' and $dir != 'DESC') $dir = 'ASC';
     82                $dir = empty($_GET['dir']) ? 'ASC' : strtoupper(sanitize_text_field($_GET['dir']));
     83                if($dir != 'ASC' && $dir != 'DESC') $dir = 'ASC';
    7984                $odir = ($dir == 'ASC') ? 'DESC' : 'ASC';
    8085               
    8186                // mark booking as fully paid   
    8287                if(!empty($_GET['mark_paid'])) {
     88                    if (!isset($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'mark_paid')) {
     89                        wp_die('Security check failed');
     90                    }
    8391                    $_booking->mark_paid($_GET['id']);
    8492
     
    9098                }
    9199               
    92                 // define $where_sql and orderby depending on the $type     
    93                 $curdate = date("Y-m-d", current_time('timestamp'));       
     100                // define $where_sql and orderby depending on the $type
     101                $curdate = date("Y-m-d", current_time('timestamp'));
    94102                if($type == 'upcoming') {
    95103                    $where_sql = "AND from_date >=  '$curdate' ";
    96104                    $orderby = "ORDER BY from_date";
    97                    
     105
    98106                }
    99107                else {
     
    101109                    $orderby = "ORDER BY from_date DESC";
    102110                }
    103                
     111
    104112                // define limit (as it's paginated)             
    105113                $page_limit = 20;
     
    107115               
    108116                // search filter
     117                $filters = array();
    109118                if(!empty($_GET['contact_email'])) {
    110                     $_GET['contact_email'] = sanitize_text_field($_GET['contact_email']);
    111                     $where_sql .= " AND contact_email LIKE '%".$_GET['contact_email']."%' ";
     119                    $contact_email = sanitize_text_field($_GET['contact_email']);
     120                    $where_sql .= $wpdb->prepare(" AND contact_email LIKE %s ", '%' . $wpdb->esc_like($contact_email) . '%');
     121                    $filters[] = 'contact_email=' . urlencode($contact_email);
    112122                }
    113123                if(!empty($_GET['contact_name'])) {
    114                     $_GET['contact_name'] = sanitize_text_field($_GET['contact_name']);
    115                     $where_sql .= " AND contact_name LIKE '%".$_GET['contact_name']."%' ";
     124                    $contact_name = sanitize_text_field($_GET['contact_name']);
     125                    $where_sql .= $wpdb->prepare(" AND contact_name LIKE %s ", '%' . $wpdb->esc_like($contact_name) . '%');
     126                    $filters[] = 'contact_name=' . urlencode($contact_name);
    116127                }
    117128                if(!empty($_GET['room_id'])) {
    118129                    $_GET['room_id'] = intval($_GET['room_id']);
    119130                    $where_sql .= $wpdb->prepare(" AND room_id = %d ", $_GET['room_id']);
     131                    $filters[] = 'room_id=' . $_GET['room_id'];
    120132                }
    121133                if(!empty($_GET['status'])) {
    122134                    $_GET['status'] = sanitize_text_field($_GET['status']);
    123135                    $where_sql .= $wpdb->prepare(" AND status = %s ", $_GET['status']);
    124                 }               
     136                    $filters[] = 'status=' . urlencode($_GET['status']);
     137                }
    125138                if(!empty($_GET['booking_id'])) {
    126139                    $_GET['booking_id'] = intval($_GET['booking_id']);
    127140                    $where_sql .= $wpdb->prepare(" AND tB.id = %d ", $_GET['booking_id']);
    128                 }
    129                 if(!empty($_GET['contact_email']) or !empty($_GET['contact_name'])
    130                     or !empty($_GET['room_id']) or !empty($_GET['status']) or !empty($_GET['booking_id'])) $filters_apply = true;   
    131                    
     141                    $filters[] = 'booking_id=' . $_GET['booking_id'];
     142                }
     143                if(!empty($_GET['contact_email']) or !empty($_GET['contact_name'])
     144                    or !empty($_GET['room_id']) or !empty($_GET['status']) or !empty($_GET['booking_id'])) $filters_apply = true;
     145
    132146                if(!empty($_GET['ob'])) {
    133147                    $ob = sanitize_text_field($_GET['ob']);
     
    136150                    }
    137151                    $orderby = "ORDER BY $ob $dir";
    138                 }
     152                    $filters[] = 'ob=' . urlencode($ob);
     153                }
     154
     155                // Build filters string for pagination links
     156                $filters_str = !empty($filters) ? '&' . implode('&', $filters) : '';
    139157
    140158                // select bookings - direct query necessary for custom plugin table
     
    267285    static function book() {
    268286        global $wpdb, $post;
     287
     288        // Verify nonce for CSRF protection
     289        if (!isset($_POST['wphostel_nonce']) || !wp_verify_nonce($_POST['wphostel_nonce'], 'wphostel_booking')) {
     290            return '<!--BOOKERROR-->' . __('Security check failed. Please refresh the page and try again.', 'wphostel');
     291        }
    269292
    270293        // insert booking details
  • hostel/trunk/controllers/help.php

    r3478265 r3480942  
    88    static function email_log() {
    99        global $wpdb;
    10         $date = empty($_POST['date']) ? date('Y-m-d') : $_POST['date'];
    11         if(!empty($_POST['cleanup'])) update_option('hostelpro_cleanup_email_log', $_POST['cleanup_days']);
     10        $date = empty($_POST['date']) ? date('Y-m-d') : sanitize_text_field($_POST['date']);
     11        if(!empty($_POST['cleanup']) and check_admin_referer('wphostel_email_log')) update_option('hostelpro_cleanup_email_log', intval($_POST['cleanup_days']));
    1212
    1313        // select emails from log - direct query necessary for custom plugin table
  • hostel/trunk/controllers/rooms.php

    r3478265 r3480942  
    8888        if(empty($booking_start)) $booking_start = 'tomorrow';
    8989        $book_to_date = ($booking_start == 'tomorrow') ? '+2 days' : 'tomorrow';
    90         $show_titles = empty($atts['show_titles']) ? 0 : $atts['show_titles'];
     90        $show_titles = empty($atts['show_titles']) ? 0 : intval($atts['show_titles']);
    9191
    9292        // the dropdown defaults to "from tomorrow to 1 day after"
  • hostel/trunk/controllers/shortcodes.php

    r3478265 r3480942  
    5353        $booking_start = get_option('wphostel_booking_start');
    5454        $min_date = ($booking_start == 'tomorrow') ? 1 : 0;
    55         $show_titles = empty($atts['show_titles']) ? 0 : $atts['show_titles'];
    56         $show_table = isset($atts['show_table']) ?  $atts['show_table'] : 1;
    57                
     55        $show_titles = empty($atts['show_titles']) ? 0 : intval($atts['show_titles']);
     56        $show_table = isset($atts['show_table']) ?  intval($atts['show_table']) : 1;
     57
    5858        // the dropdown defaults to "from tomorrow to 1 day after"
    59         $datefrom = empty($_POST['wphostel_from']) ? date("Y-m-d", strtotime($booking_start)) : $_POST['wphostel_from'];
    60         $dateto = empty($_POST['wphostel_to']) ? date("Y-m-d", strtotime($default_dateto_diff)) : $_POST['wphostel_to'];
     59        $datefrom = empty($_POST['wphostel_from']) ? date("Y-m-d", strtotime($booking_start)) : sanitize_text_field($_POST['wphostel_from']);
     60        $dateto = empty($_POST['wphostel_to']) ? date("Y-m-d", strtotime($default_dateto_diff)) : sanitize_text_field($_POST['wphostel_to']);
    6161       
    6262        wphostel_enqueue_datepicker();
     
    6969   
    7070    // displays a Book! button
    71     static function book($atts) {       
     71    static function book($atts) {
    7272        global $post;
    7373        $room_id = intval($atts[0]);
    7474        $shortcode_id = self :: get_id();
    75        
    76         // this if will be removed when bookiing by ajax is done 
    77         if(!empty($_GET['in_booking_mode']) and $_GET['room_id']==$room_id) {
     75
     76        // this if will be removed when bookiing by ajax is done
     77        if(!empty($_GET['in_booking_mode']) and intval($_GET['room_id'])==$room_id) {
    7878            return self :: booking();
    7979        }
    8080       
    81         $text = empty($atts[1]) ? __('Book', 'wphostel') : $atts[1];
    82        
     81        $text = empty($atts[1]) ? __('Book', 'wphostel') : esc_attr($atts[1]);
     82
    8383        wphostel_enqueue_datepicker();
    84        
     84
    8585        return '<div id="wphostelBookForm'.$shortcode_id.'">
    8686        <form method="post">
    8787        <input type="hidden" name="from_date" value="'.date("Y-m-d", strtotime('tomorrow')).'">
    8888                <input type="hidden" name="to_date" value="'.date("Y-m-d", strtotime('+2 days')).'">
    89                 <input type="hidden" name="room_id" value="'.$room_id.'">               
     89                <input type="hidden" name="room_id" value="'.esc_attr($room_id).'">
    9090                <input type="hidden" name="action" value="wphostel_ajax">
    9191                <input type="hidden" name="type" value="load_booking_form">
    92                 <input type="hidden" name="in_booking_mode" value="1">     
    93         <input type="button"  onclick="WPHostelLoadBooking(this.form, '."'wphostelBookForm".$shortcode_id."'".');" value="'.$text.'">
     92                <input type="hidden" name="in_booking_mode" value="1">
     93        <input type="button"  onclick="WPHostelLoadBooking(this.form, '."'wphostelBookForm".esc_attr($shortcode_id)."'".');" value="'.esc_attr($text).'">
    9494        </form></div>';
    9595    }
  • hostel/trunk/controllers/sync.php

    r3478265 r3480942  
    1212        // @codingStandardsIgnoreLine WordPress.DB.DirectDatabaseQuery.NoCaching
    1313        $bookings = $wpdb->get_results($wpdb->prepare("SELECT * FROM ".WPHOSTEL_BOOKINGS."
    14             WHERE room_id=%d AND to_date >= ".date('Y-m-d', current_time('timestamp'))." ORDER BY id", intval($_GET['room_id'])));
     14            WHERE room_id=%d AND to_date >= %s ORDER BY id", intval($_GET['room_id']), date('Y-m-d', current_time('timestamp'))));
    1515
    1616        if(!empty($_GET['download'])) {
     
    9898    static function icsToArray($paramUrl) {
    9999         $paramUrl = trim($paramUrl);
    100          ob_start();
    101         $icsFile = file_get_contents($paramUrl);
    102        
    103         $error = ob_get_clean();
    104         if(!empty($error)) {
    105             // log error
    106             $msg = "Importing iCal events failed at ".date(get_option('date_format'), current_time('timestamp'))." with message: $error";
    107             update_option('wphostel_ical_import_error', $msg);
    108         }
    109         if(empty($icsFile)) {
    110            // try curl
     100
     101         // Validate URL - must be http or https only to prevent SSRF
     102         if (!preg_match('#^https?://#i', $paramUrl)) {
     103            return false;
     104         }
     105
     106         // Parse URL and block private/local IP addresses to prevent SSRF
     107         $parsed_url = parse_url($paramUrl);
     108         if (isset($parsed_url['host'])) {
     109            $host = $parsed_url['host'];
     110            // Check if host resolves to a private IP
     111            $ip = gethostbyname($host);
     112            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
     113                return false;
     114            }
     115         }
     116
     117         // Use wp_remote_get instead of file_get_contents for better security and WordPress standards
     118         $response = wp_remote_get($paramUrl, array(
     119            'timeout' => 15,
     120            'sslverify' => true,
     121            'user-agent' => 'Mozilla/5.0 (compatible; Hostel Plugin/' . WPHOSTEL_VERSION . ')'
     122         ));
     123
     124         if (is_wp_error($response)) {
     125            $msg = "Importing iCal events failed at ".date(get_option('date_format'), current_time('timestamp'))." with message: " . $response->get_error_message();
     126            update_option('wphostel_ical_import_error', $msg);
     127            return false;
     128         }
     129
     130         $icsFile = wp_remote_retrieve_body($response);
     131
     132         if (empty($icsFile)) {
     133            // try curl as fallback
    111134             $curl = curl_init();
    112135            curl_setopt($curl, CURLOPT_URL, $paramUrl);
    113136            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    114             curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
    115             //curl_setopt($curl, CURLOPT_HEADER, false);
    116             curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
     137            curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Hostel Plugin/" . WPHOSTEL_VERSION . ")");
     138            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, TRUE);
    117139            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
    118             curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
     140            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); // Disable follow location for security
    119141            $icsFile = curl_exec($curl);
    120142            curl_close($curl);
    121             //echo $icsFile;
    122         }
     143         }
    123144
    124145        if(empty($icsFile)) return false;
  • hostel/trunk/hostel.php

    r3478265 r3480942  
    55Description: Hostel / BnB management plugin
    66Author: Kiboko Labs
    7 Version: 1.1.7
     7Version: 1.1.8
    88Author URI: http://kibokolabs.com
    99License: GPLv2 or later
  • hostel/trunk/models/booking.php

    r3478265 r3480942  
    160160        $wpdb->query($wpdb->prepare("INSERT INTO ".WPHOSTEL_EMAILLOG." SET
    161161            sender=%s, receiver=%s, subject=%s, date=CURDATE(), status=%s",
    162             $admin_email, $email_options['admin_email'], $subject, $status));
     162            $email_options['admin_email'], $email_options['admin_email'], $subject, $status));
    163163        } // end do email admin
    164164
     
    191191            $wpdb->query($wpdb->prepare("INSERT INTO ".WPHOSTEL_EMAILLOG." SET
    192192            sender=%s, receiver=%s, subject=%s, date=CURDATE(), status=%s",
    193             $admin_email, $booking->contact_email, $subject, $status));
     193            $email_options['admin_email'], $booking->contact_email, $subject, $status));
    194194        } // end do email user
    195195    } // end email
  • hostel/trunk/models/room.php

    r3478265 r3480942  
    6363    }
    6464   
    65     // list all rooms, paginated. 
     65    // list all rooms, paginated.
    6666    // allow filters
    6767    function find($filters = null) {
    6868        global $wpdb;
    69        
     69
    7070        $ob = "id";
    7171        $dir = "DESC";
    72         $offset = empty($_GET['offset']) ? 0 : $_GET['offset'];
     72        $offset = empty($_GET['offset']) ? 0 : intval($_GET['offset']);
    7373        $limit = 20;
    74        
     74
     75        // ORDER BY cannot be parameterized with $wpdb->prepare(), use whitelist validation
     76        $allowed_orderby = array('id', 'title', 'price', 'rtype', 'beds');
     77        if (!empty($_GET['ob']) && in_array($_GET['ob'], $allowed_orderby)) {
     78            $ob = $_GET['ob'];
     79        }
     80        $ob = esc_sql($ob);
     81
     82        // ORDER direction whitelist
     83        if (!empty($_GET['dir']) && strtoupper($_GET['dir']) === 'ASC') {
     84            $dir = 'ASC';
     85        }
     86        $dir = esc_sql($dir);
     87
    7588        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    76         $rooms = $wpdb->get_results($wpdb->prepare("SELECT * FROM ".WPHOSTEL_ROOMS." ORDER BY %s %s LIMIT %d, %d",
    77             $ob, $dir, $offset, $limit));
    78            
    79         return $rooms; 
     89        $rooms = $wpdb->get_results($wpdb->prepare("SELECT * FROM ".WPHOSTEL_ROOMS." ORDER BY $ob $dir LIMIT %d, %d", $offset, $limit));
     90
     91        return $rooms;
    8092    }
    8193   
  • hostel/trunk/views/booking-form.html.php

    r3478265 r3480942  
    3535            <input type="hidden" name="type" value="book">
    3636            <input type="hidden" name="shortcode_id" value="<?php echo esc_attr($shortcode_id);?>">
     37            <?php wp_nonce_field('wphostel_booking', 'wphostel_nonce'); ?>
    3738        </form>
    3839    </div>
  • hostel/trunk/views/booking.html.php

    r3478265 r3480942  
    3636                    <input type="button" value="<?php esc_attr_e('Delete booking', 'wphostel');?>" onclick="wpHostelConfirmDelete(this.form);" class="button">
    3737                <?php endif;?>
    38                 <input type="button" value="<?php esc_attr_e('Go Back', 'wphostel');?>" onclick="window.location='admin.php?page=wphostel_bookings&type=<?php echo esc_attr($_GET['type']);?>&offset=<?php echo (int)$_GET['offset'];?>';" class="button">
     38                <input type="button" value="<?php esc_attr_e('Go Back', 'wphostel');?>" onclick="window.location='admin.php?page=wphostel_bookings&type=<?php echo esc_js($_GET['type'] ?? '');?>&offset=<?php echo (int)($_GET['offset'] ?? 0);?>';" class="button">
    3939            </div>
    4040            <input type="hidden" name="ok" value="1">
  • hostel/trunk/views/bookings.html.php

    r3478265 r3480942  
    7171            notice_str = "&send_emails=1";
    7272        }
    73         window.location = 'admin.php?page=wphostel_bookings&type=<?php echo esc_js($type);?>&offset=<?php echo (int)$offset;?>&mark_paid=1&id='+id + notice_str;
     73        var nonce = '<?php echo esc_js(wp_create_nonce('mark_paid'));?>';
     74        window.location = 'admin.php?page=wphostel_bookings&type=<?php echo esc_js($type);?>&offset=<?php echo (int)$offset;?>&mark_paid=1&id='+id + notice_str + '&nonce=' + nonce;
    7475    }
    7576}
  • hostel/trunk/views/email-log.html.php

    r3478265 r3480942  
    77        <div class="postbox wp-admin" style="padding:20px;">
    88            <form method="post">
     9                <?php wp_nonce_field('wphostel_email_log');?>
    910                <p><label><?php esc_html_e('Log date:', 'wphostel');?></label> <input type="text" name="date" class="wphostelDatePicker" value="<?php echo esc_attr($date);?>">
    1011                <input type="submit" value="<?php esc_attr_e('Show log', 'wphostel');?>" class="button button-primary">
  • hostel/trunk/views/options.php

    r3478265 r3480942  
    3131            <?php if(!empty($payment_errors)):?>
    3232                <p><a href="#" onclick="jQuery('#hostelErrorlog').toggle();return false;"><?php esc_html_e('View payments errorlog', 'wphostel');?></a></p>
    33                 <div id="hostelErrorlog" style="display:none;"><?php echo esc_html(nl2br($payment_errors));?></div>
     33                <div id="hostelErrorlog" style="display:none;"><?php echo nl2br(esc_html($payment_errors));?></div>
    3434            <?php endif;?>
    3535
Note: See TracChangeset for help on using the changeset viewer.