Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 3.3.0

* FIX: Escape API error messages to prevent reflected cross-site scripting on listing pages

## 3.2.9

* FIX: Improve santization of unescaped attributes
Expand Down
6 changes: 5 additions & 1 deletion src/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Contributors: SimplyRETS
Tags: real estate, idx, mls, rets, reso web api
Requires at least: 3.0.1
Tested up to: 7.0
Stable tag: 3.2.9
Stable tag: 3.3.0
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.html

Expand Down Expand Up @@ -235,6 +235,10 @@ listing sidebar widget.

== Changelog ==

= 3.3.0 =

* FIX: Escape API error messages to prevent reflected cross-site scripting on listing pages

= 3.2.9 =

* FIX: Improve santization of unescaped attributes
Expand Down
2 changes: 1 addition & 1 deletion src/simply-rets-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ public static function noResultsMsg($response) {
if (array_key_exists("message", $response)) {
return (
'<br><p><strong>'
. $response['message']
. esc_html($response['message'])
. '</br></p></strong>'
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/simply-rets.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Plugin URI: https://simplyrets.com/wordpress-idx-plugin
Description: Show your Real Estate listings on your Wordpress site. SimplyRETS provides a very simple set up and full control over your listings.
Author: SimplyRETS
Version: 3.2.9
Version: 3.3.0
License: GNU General Public License v3 or later

Copyright (c) SimplyRETS 2014 - 2026
Expand All @@ -15,7 +15,7 @@
}

/* Code starts here */
const SIMPLYRETSWP_VERSION = "v3.2.9";
const SIMPLYRETSWP_VERSION = "v3.3.0";

$plugin = plugin_basename(__FILE__);
$php_version = phpversion();
Expand Down
80 changes: 80 additions & 0 deletions src/tests/test-api-error-messages.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
if (! defined('ABSPATH')) {
exit;
}

class ApiErrorMessagesTest extends WP_UnitTestCase {

/**
* @dataProvider apiMessageProvider
*/
public function testApiMessagesAreEscaped($message, $escaped_message) {
$expected = '<br><p><strong>' . $escaped_message . '</br></p></strong>';

$this->assertSame($expected, SrMessages::noResultsMsg(array('message' => $message)));

// Exercise the single-listing error path used by the reported exploit.
$response = array(
'lastUpdate' => null,
'response' => (object)array('message' => $message)
);
$this->assertSame($expected, SimplyRetsRenderer::srResidentialDetailsGenerator($response));
}

public static function apiMessageProvider() {
return array(
'reported SVG payload' => array(
'No property found wih MLS ID "<svg onload=alert(document.domain)>"',
'No property found wih MLS ID &quot;&lt;svg onload=alert(document.domain)&gt;&quot;'
),
'script element' => array(
'<script>alert(document.domain)</script>',
'&lt;script&gt;alert(document.domain)&lt;/script&gt;'
),
'wrapper breakout' => array(
'</strong></p><img src=x onerror="alert(1)">',
'&lt;/strong&gt;&lt;/p&gt;&lt;img src=x onerror=&quot;alert(1)&quot;&gt;'
),
'encoded HTML entities' => array(
'&lt;svg onload=alert(1)&gt;',
'&lt;svg onload=alert(1)&gt;'
),
'ordinary numeric ID' => array(
'No property found wih MLS ID "99999999"',
'No property found wih MLS ID &quot;99999999&quot;'
),
'text with ampersands and apostrophes' => array(
"Listing isn't available & cannot be displayed.",
'Listing isn&#039;t available &amp; cannot be displayed.'
)
);
}

public function testObjectApiResponseIsEscaped() {
$this->assertSame(
'<br><p><strong>&lt;svg onload=alert(1)&gt;</br></p></strong>',
SrMessages::noResultsMsg((object)array('message' => '<svg onload=alert(1)>'))
);
}

public function testDefaultNoResultsMessageIsPreserved() {
delete_option('sr_custom_no_results_message');

$this->assertSame(
'<p><strong>0 listings matched your search. '
. 'Please try to broaden your search criteria or try again later.</strong></p>',
SrMessages::noResultsMsg(array())
);
}

public function testCustomNoResultsMarkupIsPreserved() {
$custom_message = '<p>No listings. <a href="/contact/">Contact us</a>.</p>';
update_option('sr_custom_no_results_message', $custom_message);

$this->assertSame($custom_message, SrMessages::noResultsMsg(array()));
$this->assertSame(
'<br><p><strong>&lt;svg onload=alert(1)&gt;</br></p></strong>',
SrMessages::noResultsMsg(array('message' => '<svg onload=alert(1)>'))
);
}
}
Loading