| @@ -12,8 +12,9 @@ | ||
| 12 | 12 | * @package PropertyHive/Classes |
| 13 | 13 | * @category Class |
| 14 | 14 | * @author PropertyHive |
| 15 | 15 | */ |
| 16 | +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Property; preserving the existing PH_* class name is required for plugin and extension compatibility. | |
| 16 | 17 | class PH_Property { |
| 17 | 18 | |
| 18 | 19 | /** @public int Property (post) ID */ |
| 19 | 20 | public $id; |
| @@ -103,8 +104,14 @@ | ||
| 103 | 104 | if ($value == '') |
| 104 | 105 | { |
| 105 | 106 | $value = get_post_meta( $this->id, '_' . $key, true ); |
| 106 | 107 | } |
| 108 | + // Sanitize stored description HTML before trusted extension callbacks run. | |
| 109 | + // This also protects descriptions saved before the write-boundary checks. | |
| 110 | + if ( preg_match( '/^_?(?:room_(?:name|dimensions|description)|description(?:_name)?)_[0-9]+$/', $key ) ) { | |
| 111 | + $value = is_scalar( $value ) ? (string) $value : ''; | |
| 112 | + $value = preg_match( '/^_?(?:room_description|description)_[0-9]+$/', $key ) ? propertyhive_sanitize_description( $value ) : wp_kses_post( $value ); | |
| 113 | + } | |
| 107 | 114 | } |
| 108 | 115 | |
| 109 | 116 | $value = apply_filters( 'propertyhive_get_detail', $value, $key, $this ); |
| 110 | 117 | |
| @@ -314,9 +321,9 @@ | ||
| 314 | 321 | $this->_for_sale == 'yes' && $this->_price_poa == 'yes' && |
| 315 | 322 | $this->_to_rent == 'yes' && $this->_rent_poa == 'yes' |
| 316 | 323 | ) |
| 317 | 324 | { |
| 318 | - $return = __( 'POA', 'propertyhive' ); | |
| 325 | + $return = esc_html__( 'POA', 'propertyhive' ); | |
| 319 | 326 | } |
| 320 | 327 | else |
| 321 | 328 | { |
| 322 | 329 | // Price Details |
| @@ -344,9 +351,9 @@ | ||
| 344 | 351 | else |
| 345 | 352 | { |
| 346 | 353 | if ( !$is_admin && $this->_poa == 'yes') |
| 347 | 354 | { |
| 348 | - $return = __( 'POA', 'propertyhive' ); | |
| 355 | + $return = esc_html__( 'POA', 'propertyhive' ); | |
| 349 | 356 | } |
| 350 | 357 | else |
| 351 | 358 | { |
| 352 | 359 | $ph_countries = new PH_Countries(); |
| @@ -352,21 +359,24 @@ | ||
| 352 | 359 | $ph_countries = new PH_Countries(); |
| 353 | 360 | |
| 354 | 361 | if ( !$is_admin ) |
| 355 | 362 | { |
| 356 | - if ( isset($_GET['currency']) ) | |
| 363 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public currency choice only affects displayed prices. | |
| 364 | + if ( isset($_GET['currency']) && is_string( $_GET['currency'] ) ) | |
| 357 | 365 | { |
| 358 | - if ( $_GET['currency'] != '' ) | |
| 366 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public currency choice only affects displayed prices. | |
| 367 | + $currency_code = sanitize_text_field( wp_unslash( $_GET['currency'] ) ); | |
| 368 | + if ( $currency_code != '' ) | |
| 359 | 369 | { |
| 360 | - $requested_currency = $ph_countries->get_currency( sanitize_text_field($_GET['currency']) ); | |
| 370 | + $requested_currency = $ph_countries->get_currency( $currency_code ); | |
| 361 | 371 | if ( $requested_currency !== FALSE ) |
| 362 | 372 | { |
| 363 | 373 | $currency = $requested_currency; |
| 364 | 374 | $currency['exchange_rate'] = 1; |
| 365 | 375 | $exchange_rates = get_option( 'propertyhive_currency_exchange_rates', array() ); |
| 366 | - if ( isset($exchange_rates[$_GET['currency']]) ) | |
| 376 | + if ( isset($exchange_rates[$currency_code]) ) | |
| 367 | 377 | { |
| 368 | - $currency['exchange_rate'] = $exchange_rates[sanitize_text_field($_GET['currency'])]; | |
| 378 | + $currency['exchange_rate'] = $exchange_rates[$currency_code]; | |
| 369 | 379 | } |
| 370 | 380 | } |
| 371 | 381 | } |
| 372 | 382 | else |
| @@ -387,11 +397,11 @@ | ||
| 387 | 397 | } |
| 388 | 398 | } |
| 389 | 399 | } |
| 390 | 400 | } |
| 391 | - elseif ( isset($_COOKIE['propertyhive_currency']) && $_COOKIE['propertyhive_currency'] != '' ) | |
| 401 | + elseif ( false !== ( $cookie_currency = $ph_countries->get_currency_from_cookie() ) ) | |
| 392 | 402 | { |
| 393 | - $currency = @json_decode(html_entity_decode($_COOKIE['propertyhive_currency']), true); | |
| 403 | + $currency = $cookie_currency; | |
| 394 | 404 | } |
| 395 | 405 | else |
| 396 | 406 | { |
| 397 | 407 | $default_currency = apply_filters( 'propertyhive_default_display_currency', '' ); |
| @@ -442,9 +452,9 @@ | ||
| 442 | 452 | // Round this after calculation |
| 443 | 453 | $price = round($this->_price_actual * $currency['exchange_rate'], 0); |
| 444 | 454 | } |
| 445 | 455 | |
| 446 | - $return = ( ( $price != '' ) ? $prefix . ph_display_price_field($price, !$is_admin) . $suffix : '-' ); | |
| 456 | + $return = ( ( $price != '' ) ? $prefix . esc_html( ph_display_price_field($price, !$is_admin) ) . $suffix : '-' ); | |
| 447 | 457 | break; |
| 448 | 458 | } |
| 449 | 459 | case "residential-lettings": |
| 450 | 460 | { |
| @@ -461,15 +471,17 @@ | ||
| 461 | 471 | case "pa": { $price = ($price * 12); break; } |
| 462 | 472 | } |
| 463 | 473 | } |
| 464 | 474 | |
| 465 | - $return = ( ( $price != '' ) ? $prefix . ph_display_price_field($price, !$is_admin) . $suffix . ' ' . __( $this->_rent_frequency, 'propertyhive' ) : '-' ); | |
| 475 | + $return = ( ( $price != '' ) ? $prefix . esc_html( ph_display_price_field($price, !$is_admin) ) . $suffix . ' ' . esc_html( propertyhive_get_rent_frequency_label( $this->_rent_frequency ) ) : '-' ); | |
| 466 | 476 | break; |
| 467 | 477 | } |
| 468 | 478 | } |
| 469 | 479 | } |
| 480 | + | |
| 470 | 481 | } |
| 471 | 482 | |
| 483 | + // Stored price/frequency text is escaped above; currency and commercial HTML filters remain trusted. | |
| 472 | 484 | return apply_filters( 'propertyhive_price_output', $return, $this, $currency, $prefix, $suffix ); |
| 473 | 485 | } |
| 474 | 486 | |
| 475 | 487 | /** |
| @@ -487,9 +499,9 @@ | ||
| 487 | 499 | $is_admin = ( !is_admin() || ( is_admin() && defined('DOING_AJAX') && DOING_AJAX ) ) ? false : true; |
| 488 | 500 | |
| 489 | 501 | if ( !$is_admin && $this->_price_poa == 'yes' ) |
| 490 | 502 | { |
| 491 | - $price .= __( 'POA', 'propertyhive' ); | |
| 503 | + $price .= esc_html__( 'POA', 'propertyhive' ); | |
| 492 | 504 | } |
| 493 | 505 | else |
| 494 | 506 | { |
| 495 | 507 | $ph_countries = new PH_Countries(); |
| @@ -505,9 +517,9 @@ | ||
| 505 | 517 | $suffix = ( (!$currency['currency_prefix']) ? $currency['currency_symbol'] : '' ); |
| 506 | 518 | |
| 507 | 519 | if ( $this->_price_from != '' ) |
| 508 | 520 | { |
| 509 | - $price .= $prefix . ph_display_price_field($this->_price_from, !$is_admin) . $suffix; | |
| 521 | + $price .= $prefix . esc_html( ph_display_price_field($this->_price_from, !$is_admin) ) . $suffix; | |
| 510 | 522 | } |
| 511 | 523 | if ( $this->_price_to != '' && $this->_price_to != $this->_price_from ) |
| 512 | 524 | { |
| 513 | 525 | if ( $price != '' ) |
| @@ -513,18 +525,19 @@ | ||
| 513 | 525 | if ( $price != '' ) |
| 514 | 526 | { |
| 515 | 527 | $price .= ' - '; |
| 516 | 528 | } |
| 517 | - $price .= $prefix . ph_display_price_field($this->_price_to, !$is_admin) . $suffix; | |
| 529 | + $price .= $prefix . esc_html( ph_display_price_field($this->_price_to, !$is_admin) ) . $suffix; | |
| 518 | 530 | } |
| 519 | 531 | if ( $price != '' ) |
| 520 | 532 | { |
| 521 | 533 | $price_units = get_commercial_price_units( ); |
| 522 | - $price .= ( isset($price_units[$this->_price_units]) ) ? ' ' . $price_units[$this->_price_units] : ''; | |
| 534 | + $price .= ( isset($price_units[$this->_price_units]) ) ? ' ' . esc_html( $price_units[$this->_price_units] ) : ''; | |
| 523 | 535 | } |
| 524 | 536 | } |
| 525 | 537 | } |
| 526 | 538 | |
| 539 | + // Stored price/unit text is escaped above; preserve trusted currency and price filter HTML. | |
| 527 | 540 | return apply_filters( 'propertyhive_commercial_price_output', $price, $this ); |
| 528 | 541 | } |
| 529 | 542 | |
| 530 | 543 | /** |
| @@ -542,9 +555,9 @@ | ||
| 542 | 555 | $is_admin = ( !is_admin() || ( is_admin() && defined('DOING_AJAX') && DOING_AJAX ) ) ? false : true; |
| 543 | 556 | |
| 544 | 557 | if ( !$is_admin && $this->_rent_poa == 'yes' ) |
| 545 | 558 | { |
| 546 | - $rent .= __( 'POA', 'propertyhive' ); | |
| 559 | + $rent .= esc_html__( 'POA', 'propertyhive' ); | |
| 547 | 560 | } |
| 548 | 561 | else |
| 549 | 562 | { |
| 550 | 563 | $ph_countries = new PH_Countries(); |
| @@ -560,9 +573,9 @@ | ||
| 560 | 573 | $suffix = ( (!$currency['currency_prefix']) ? $currency['currency_symbol'] : '' ); |
| 561 | 574 | |
| 562 | 575 | if ( $this->_rent_from != '' ) |
| 563 | 576 | { |
| 564 | - $rent .= $prefix . ph_display_price_field($this->_rent_from, !$is_admin) . $suffix; | |
| 577 | + $rent .= $prefix . esc_html( ph_display_price_field($this->_rent_from, !$is_admin) ) . $suffix; | |
| 565 | 578 | } |
| 566 | 579 | if ( $this->_rent_to != '' && $this->_rent_to != $this->_rent_from ) |
| 567 | 580 | { |
| 568 | 581 | if ( $rent != '' ) |
| @@ -568,18 +581,19 @@ | ||
| 568 | 581 | if ( $rent != '' ) |
| 569 | 582 | { |
| 570 | 583 | $rent .= ' - '; |
| 571 | 584 | } |
| 572 | - $rent .= $prefix . ph_display_price_field($this->_rent_to, !$is_admin) . $suffix; | |
| 585 | + $rent .= $prefix . esc_html( ph_display_price_field($this->_rent_to, !$is_admin) ) . $suffix; | |
| 573 | 586 | } |
| 574 | 587 | if ( $rent != '' ) |
| 575 | 588 | { |
| 576 | 589 | $price_units = get_commercial_price_units( ); |
| 577 | - $rent .= ' ' . __( ( isset($price_units[$this->_rent_units]) ? $price_units[$this->_rent_units] : $this->_rent_units ), 'propertyhive' ); | |
| 590 | + $rent .= ' ' . esc_html( isset( $price_units[$this->_rent_units] ) ? $price_units[$this->_rent_units] : $this->_rent_units ); | |
| 578 | 591 | } |
| 579 | 592 | } |
| 580 | 593 | } |
| 581 | 594 | |
| 595 | + // Stored rent/unit text is escaped above; preserve trusted currency and rent filter HTML. | |
| 582 | 596 | return apply_filters( 'propertyhive_commercial_rent_output', $rent, $this ); |
| 583 | 597 | } |
| 584 | 598 | |
| 585 | 599 | public function get_formatted_floor_area( ) { |
| @@ -620,9 +634,9 @@ | ||
| 620 | 634 | $area_units = get_area_units( ); |
| 621 | 635 | $area .= ( isset($area_units[$this->_floor_area_units]) ) ? ' ' . $area_units[$this->_floor_area_units] : ''; |
| 622 | 636 | } |
| 623 | 637 | |
| 624 | - return apply_filters( 'propertyhive_floor_area_output', $area, $this ); | |
| 638 | + return apply_filters( 'propertyhive_floor_area_output', esc_html( $area ), $this ); | |
| 625 | 639 | |
| 626 | 640 | } |
| 627 | 641 | |
| 628 | 642 | public function get_formatted_site_area( ) { |
| @@ -663,9 +677,9 @@ | ||
| 663 | 677 | $area_units = get_area_units( ); |
| 664 | 678 | $area .= ( isset($area_units[$this->_site_area_units]) ) ? ' ' . $area_units[$this->_site_area_units] : ''; |
| 665 | 679 | } |
| 666 | 680 | |
| 667 | - return apply_filters( 'propertyhive_site_area_output', $area, $this ); | |
| 681 | + return apply_filters( 'propertyhive_site_area_output', esc_html( $area ), $this ); | |
| 668 | 682 | |
| 669 | 683 | } |
| 670 | 684 | |
| 671 | 685 | /** |
| @@ -712,9 +726,9 @@ | ||
| 712 | 726 | } |
| 713 | 727 | |
| 714 | 728 | if (strtotime($this->_available_date) > time()) |
| 715 | 729 | { |
| 716 | - return date( get_option( 'date_format' ), strtotime($this->_available_date) ); | |
| 730 | + return gmdate( get_option( 'date_format' ), strtotime($this->_available_date) ); | |
| 717 | 731 | } |
| 718 | 732 | else |
| 719 | 733 | { |
| 720 | 734 | return __( 'Now', 'propertyhive' ); |
| @@ -792,9 +806,9 @@ | ||
| 792 | 806 | if ($this->{'_room_name_' . $i} != '' || $this->{'_room_dimensions_' . $i} != '') |
| 793 | 807 | { |
| 794 | 808 | $return .= "\n"; |
| 795 | 809 | } |
| 796 | - $return .= strip_tags($this->{'_room_description_' . $i}) . "\n\n"; | |
| 810 | + $return .= wp_strip_all_tags($this->{'_room_description_' . $i}) . "\n\n"; | |
| 797 | 811 | } |
| 798 | 812 | } |
| 799 | 813 | } |
| 800 | 814 | |
| @@ -836,9 +850,9 @@ | ||
| 836 | 850 | if ($this->{'_description_name_' . $i} != '') |
| 837 | 851 | { |
| 838 | 852 | $return .= $this->{'_description_name_' . $i} . "\n"; |
| 839 | 853 | } |
| 840 | - $return .= strip_tags($this->{'_description_' . $i}) . "\n\n"; | |
| 854 | + $return .= wp_strip_all_tags($this->{'_description_' . $i}) . "\n\n"; | |
| 841 | 855 | } |
| 842 | 856 | } |
| 843 | 857 | } |
| 844 | 858 | |
| @@ -1032,31 +1046,14 @@ | ||
| 1032 | 1046 | * @return string |
| 1033 | 1047 | */ |
| 1034 | 1048 | public function get_imported_id() |
| 1035 | 1049 | { |
| 1036 | - global $wpdb; | |
| 1037 | - | |
| 1038 | - $row = $wpdb->get_row( | |
| 1039 | - $wpdb->prepare( | |
| 1040 | - " | |
| 1041 | - SELECT meta_value | |
| 1042 | - FROM {$wpdb->prefix}postmeta | |
| 1043 | - WHERE | |
| 1044 | - meta_key LIKE %s | |
| 1045 | - AND post_id = %d | |
| 1046 | - LIMIT 1 | |
| 1047 | - ", | |
| 1048 | - '_imported_ref_%', | |
| 1049 | - $this->id | |
| 1050 | - ), | |
| 1051 | - ARRAY_A | |
| 1052 | - ); | |
| 1053 | - | |
| 1054 | - if ( null !== $row ) | |
| 1055 | - { | |
| 1056 | - return $row['meta_value']; | |
| 1050 | + // Use WordPress's metadata cache and match the literal importer-key prefix. | |
| 1051 | + foreach ( get_post_meta( $this->id ) as $key => $values ) { | |
| 1052 | + if ( 0 === strpos( $key, '_imported_ref_' ) && isset( $values[0] ) ) { | |
| 1053 | + return $values[0]; | |
| 1054 | + } | |
| 1057 | 1055 | } |
| 1058 | - | |
| 1059 | 1056 | return ''; |
| 1060 | 1057 | } |
| 1061 | 1058 | |
| 1062 | 1059 | /** |