Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/pagelayer/init.php
+++ b/pagelayer/init.php
@@ -5,7 +5,7 @@
define('PAGELAYER_BASE', plugin_basename(PAGELAYER_FILE));
define('PAGELAYER_PREMIUM_BASE', 'pagelayer-pro/pagelayer-pro.php');
-define('PAGELAYER_VERSION', '2.0.8');
+define('PAGELAYER_VERSION', '2.0.9');
define('PAGELAYER_DIR', dirname(PAGELAYER_FILE));
define('PAGELAYER_SLUG', 'pagelayer');
define('PAGELAYER_URL', plugins_url('', PAGELAYER_FILE));
--- a/pagelayer/main/ajax.php
+++ b/pagelayer/main/ajax.php
@@ -630,7 +630,7 @@
$data = '';
if(isset($_REQUEST['pagelayer_section_id'])){
- $get_url = PAGELAYER_API.'/library.php?give_id='.$_REQUEST['pagelayer_section_id'].(!empty($pagelayer->license['license']) ? '&license='.$pagelayer->license['license'] : '').'&url='.rawurlencode(site_url());
+ $get_url = PAGELAYER_API.'/library.php?give_id='.sanitize_text_field($_REQUEST['pagelayer_section_id']).(!empty($pagelayer->license['license']) ? '&license='.$pagelayer->license['license'] : '').'&url='.rawurlencode(site_url());
// For SitePad users
if(function_exists('get_softaculous_file')){
@@ -717,7 +717,7 @@
$data = '';
if(isset($_REQUEST['pagelayer_section_id'])){
- $get_url = PAGELAYER_API.'/library.php?give_id='.$_REQUEST['pagelayer_section_id'].(!empty($pagelayer->license['license']) ? '&license='.$pagelayer->license['license'] : '').'&url='.rawurlencode(site_url());
+ $get_url = PAGELAYER_API.'/library.php?give_id='.sanitize_text_field($_REQUEST['pagelayer_section_id']).(!empty($pagelayer->license['license']) ? '&license='.$pagelayer->license['license'] : '').'&url='.rawurlencode(site_url());
// For SitePad users
if(function_exists('get_softaculous_file')){
@@ -1171,8 +1171,15 @@
// Some AJAX security
check_ajax_referer('pagelayer_ajax', 'pagelayer_nonce');
- // TODO : Allowed
- echo pagelayer_widget_posts($_POST);
+
+ // This ajax call is only used during post/page editing
+ if(!current_user_can('edit_posts')){
+ echo __pl('no_permission');
+ wp_die();
+ }
+
+ $sanitized_post = pagelayer_sanitize_posts_data($_POST);
+ echo pagelayer_widget_posts($sanitized_post);
wp_die();
}
@@ -1184,10 +1191,17 @@
// Some AJAX security
check_ajax_referer('pagelayer_ajax', 'pagelayer_nonce');
+ // This ajax call is only used during post/page editing
+ if(!current_user_can('edit_posts')){
+ echo __pl('no_permission');
+ wp_die();
+ }
+
// Load shortcodes
pagelayer_load_shortcodes();
- // TODO : Allowed
- echo pagelayer_posts($_POST);
+
+ $sanitized_post = pagelayer_sanitize_posts_data($_POST, false);
+ echo pagelayer_posts($sanitized_post);
wp_die();
}
@@ -1221,7 +1235,6 @@
$sc = '[pl_archive_posts '.$string.'][/pl_archive_posts]';
- // TODO : Allowed
echo pagelayer_the_content($sc);
wp_die();
}
@@ -1327,7 +1340,7 @@
continue;
}
- $body .= $k."t : t $".$k."n";
+ $body .= sanitize_text_field($k)."t : t $".$k."n";
}
@@ -1431,7 +1444,7 @@
// If After logout URL, then save
if(!empty($_REQUEST['logout_url'])){
- update_user_option($user->ID, 'pagelayer_logout_url', $_REQUEST['logout_url']);
+ update_user_option($user->ID, 'pagelayer_logout_url', sanitize_url($_REQUEST['logout_url']));
}
$data['redirect'] = (empty($_REQUEST['login_url']) ? '' : sanitize_url($_REQUEST['login_url']));
@@ -1449,12 +1462,17 @@
// Some AJAX security
check_ajax_referer('pagelayer_ajax', 'pagelayer_nonce');
+ if(!current_user_can('edit_posts')){
+ echo __pl('no_permission');
+ wp_die();
+ }
+
$args = array(
- 'post_type' => $_POST['type'],
- 'orderby' => $_POST['post_order'],
- 'order' => $_POST['order'],
- 'hierarchical' => (empty($_POST['hier']) || $_POST['hier'] == null ? '' : $_POST['hier']),
- 'number' => (empty($_POST['depth']) || $_POST['depth'] == null ? '' : $_POST['depth']),
+ 'post_type' => sanitize_text_field($_POST['type']),
+ 'orderby' => sanitize_text_field($_POST['post_order']),
+ 'order' => sanitize_text_field($_POST['order']),
+ 'hierarchical' => (empty($_POST['hier']) || $_POST['hier'] == null ? '' : sanitize_text_field($_POST['hier'])),
+ 'number' => (empty($_POST['depth']) || $_POST['depth'] == null ? '' : sanitize_text_field($_POST['depth'])),
'posts_per_page' => -1,
);
@@ -1778,6 +1796,11 @@
// Some AJAX security
check_ajax_referer('pagelayer_ajax', 'pagelayer_nonce');
+ if(!current_user_can('edit_posts')){
+ echo __pl('no_permission');
+ wp_die();
+ }
+
$args = array(
'title_li' => 0,
'orderby' => $_POST['post_order'],
--- a/pagelayer/main/class.php
+++ b/pagelayer/main/class.php
@@ -42,6 +42,7 @@
var $templates;
var $template_header;
var $template_post;
+ var $template_editor;
var $template_footer;
var $template_popup_ids;
var $load_live_errors;
--- a/pagelayer/main/functions.php
+++ b/pagelayer/main/functions.php
@@ -1290,7 +1290,7 @@
}
// These events not start with on
- $not_allowed = array('click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'load', 'unload', 'change', 'submit', 'reset', 'select', 'blur', 'focus', 'keydown', 'keypress', 'keyup', 'afterprint', 'beforeprint', 'beforeunload', 'error', 'hashchange', 'message', 'offline', 'online', 'pagehide', 'pageshow', 'popstate', 'resize', 'storage', 'contextmenu', 'input', 'invalid', 'search', 'mousewheel', 'wheel', 'drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop', 'scroll', 'copy', 'cut', 'paste', 'abort', 'canplay', 'canplaythrough', 'cuechange', 'durationchange', 'emptied', 'ended', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting', 'toggle', 'animationstart', 'animationcancel', 'animationend', 'animationiteration', 'auxclick', 'beforeinput', 'beforematch', 'beforexrselect', 'compositionend', 'compositionstart', 'compositionupdate', 'contentvisibilityautostatechange', 'focusout', 'focusin', 'fullscreenchange', 'fullscreenerror', 'gotpointercapture', 'lostpointercapture', 'mouseenter', 'mouseleave', 'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerrawupdate', 'pointerup', 'scrollend', 'securitypolicyviolation', 'touchcancel', 'touchend', 'touchmove', 'touchstart', 'transitioncancel', 'transitionend', 'transitionrun', 'transitionstart', 'MozMousePixelScroll', 'DOMActivate', 'afterscriptexecute', 'beforescriptexecute', 'DOMMouseScroll', 'willreveal', 'gesturechange', 'gestureend', 'gesturestart', 'mouseforcechanged', 'mouseforcedown', 'mouseforceup', 'mouseforceup', 'beforetoggle');
+ $not_allowed = array('click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'load', 'unload', 'change', 'submit', 'reset', 'select', 'blur', 'focus', 'keydown', 'keypress', 'keyup', 'afterprint', 'beforeprint', 'beforeunload', 'error', 'hashchange', 'message', 'offline', 'online', 'pagehide', 'pageshow', 'popstate', 'resize', 'storage', 'contextmenu', 'input', 'invalid', 'search', 'mousewheel', 'wheel', 'drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop', 'scroll', 'copy', 'cut', 'paste', 'abort', 'canplay', 'canplaythrough', 'cuechange', 'durationchange', 'emptied', 'ended', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting', 'toggle', 'animationstart', 'animationcancel', 'animationend', 'animationiteration', 'auxclick', 'beforeinput', 'beforematch', 'beforexrselect', 'compositionend', 'compositionstart', 'compositionupdate', 'contentvisibilityautostatechange', 'focusout', 'focusin', 'fullscreenchange', 'fullscreenerror', 'gotpointercapture', 'lostpointercapture', 'mouseenter', 'mouseleave', 'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerrawupdate', 'pointerup', 'scrollend', 'securitypolicyviolation', 'touchcancel', 'touchend', 'touchmove', 'touchstart', 'transitioncancel', 'transitionend', 'transitionrun', 'transitionstart', 'MozMousePixelScroll', 'DOMActivate', 'afterscriptexecute', 'beforescriptexecute', 'DOMMouseScroll', 'willreveal', 'gesturechange', 'gestureend', 'gesturestart', 'mouseforcechanged', 'mouseforcedown', 'mouseforceup', 'mouseforceup', 'beforetoggle', 'selectstart', 'selectionchange');
$not_allowed = implode('|', $not_allowed);
@@ -3454,6 +3454,84 @@
return $str;
}
+// Sanitize posts data for WP_Query
+function pagelayer_sanitize_posts_data($data, $only_allowed = true) {
+
+ $allowed_keys = [
+ 'post_type', 'posts_per_page', 'order', 'orderby', 'paged',
+ 'filter_by', 'term', 'exc_term', 'cat', 'category_name',
+ 'tag', 'author', 'author_name', 'post__in', 'post__not_in',
+ 'include', 'exclude', 'search', 's', 'exact', 'sentence',
+ 'post_status', 'post_parent', 'offset',
+ 'posts_per_archive_page', 'page', 'ignore_sticky_posts'
+ ];
+
+ $sanitized = [];
+
+ foreach($data as $key => $value){
+ if($only_allowed && !in_array($key, $allowed_keys)) {
+ continue;
+ }
+
+ $sanitized[$key] = pagelayer_sanitize_text_field($value);
+ }
+
+ // Security: Restrict post_status to prevent information disclosure
+ // Only users who can read private posts or edit others' posts should be able to query non-public statuses
+ if(isset($sanitized['post_status'])){
+ $requested_status = $sanitized['post_status'];
+
+ // If requesting something other than publish, verify permissions
+ if ($requested_status !== 'publish') {
+ // Check if the user has permission to read private posts or edit others' posts
+ // This prevents contributors from seeing titles of private posts they don't own.
+ if (!current_user_can('read_private_posts') && !current_user_can('edit_others_posts')) {
+ $sanitized['post_status'] = 'publish';
+ }
+ }
+ }else{
+ // Default to publish for safety if not specified
+ $sanitized['post_status'] = 'publish';
+ }
+
+ if(isset($sanitized['posts_per_page'])){
+ $sanitized['posts_per_page'] = (int) $sanitized['posts_per_page'];
+ if ($sanitized['posts_per_page'] > 100) {
+ $sanitized['posts_per_page'] = 100;
+ }
+ }
+
+ if(isset($sanitized['paged'])){
+ $sanitized['paged'] = (int) $sanitized['paged'];
+ }
+
+ if(isset($sanitized['offset'])){
+ $sanitized['offset'] = (int) $sanitized['offset'];
+ }
+
+ if(isset($sanitized['post__in']) && is_string($sanitized['post__in'])){
+ $sanitized['post__in'] = array_map('intval', explode(',', $sanitized['post__in']));
+ }
+
+ if(isset($sanitized['post__not_in']) && is_string($sanitized['post__not_in'])){
+ $sanitized['post__not_in'] = array_map('intval', explode(',', $sanitized['post__not_in']));
+ }
+
+ if(isset($sanitized['post_parent'])){
+ $sanitized['post_parent'] = (int) $sanitized['post_parent'];
+ }
+
+ if(isset($sanitized['cat'])){
+ $sanitized['cat'] = (int) $sanitized['cat'];
+ }
+
+ if(isset($sanitized['author'])){
+ $sanitized['author'] = (int) $sanitized['author'];
+ }
+
+ return $sanitized;
+}
+
// Update nav menu item
function pagelayer_save_nav_menu_items($items){
--- a/pagelayer/main/shortcode_functions.php
+++ b/pagelayer/main/shortcode_functions.php
@@ -1664,6 +1664,11 @@
}
+// Anchor Handler
+function pagelayer_sc_anchor(&$el){
+ $el['atts']['title'] = empty($el['atts']['title']) ? '' : esc_attr(sanitize_html_class( $el['atts']['title']));
+}
+
function pagelayer_sc_google_maps(&$el){
$el['atts']['show_v2'] = true;
--- a/pagelayer/pagelayer.php
+++ b/pagelayer/pagelayer.php
@@ -3,7 +3,7 @@
Plugin Name: Pagelayer
Plugin URI: http://wordpress.org/plugins/pagelayer/
Description: Pagelayer is a WordPress page builder plugin. Its very easy to use and very light on the browser.
-Version: 2.0.8
+Version: 2.0.9
Author: Pagelayer Team
Author URI: https://pagelayer.com/
License: LGPL v2.1