PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
← All changes | includes/controls/select-input/dynamic-select-input-module.php +53 -12 4.1.1 → 4.5.6 View file →
@@ -38,9 +38,9 @@
38 38 /**
39 39 * get Ajax Data
40 40 */
41 41 public function getSelectInputData() {
42 - $nonce = isset($_POST['security']) ? sanitize_text_field($_POST['security']) : '';
42 + $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
43 43
44 44 try {
45 45 if (!wp_verify_nonce($nonce, 'upk_dynamic_select')) {
46 46 throw new \Exception('Invalid request');
@@ -49,9 +49,9 @@
49 49 if (!current_user_can('edit_posts')) {
50 50 throw new \Exception('Unauthorized request');
51 51 }
52 52
53 - $query = isset($_POST['query']) ? sanitize_text_field($_POST['query']) : '';
53 + $query = isset($_POST['query']) ? sanitize_text_field(wp_unslash($_POST['query'])) : '';
54 54
55 55 if ($query == 'terms') {
56 56 $data = $this->getTerms();
57 57 } else if ($query == 'authors') {
@@ -72,9 +72,10 @@
72 72 * Get Post Type
73 73 * @return string
74 74 */
75 75 protected function getPostType() {
76 - return isset($_POST['post_type']) ? sanitize_text_field($_POST['post_type']) : '';
76 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs.
77 + return isset($_POST['post_type']) ? sanitize_text_field(wp_unslash($_POST['post_type'])) : '';
77 78 }
78 79
79 80 /**
80 81 * @return string[]|\WP_Post_Type[]
@@ -86,9 +87,10 @@
86 87 /**
87 88 * @return string
88 89 */
89 90 protected function getSearchQuery() {
90 - return isset($_POST['search_text']) ? sanitize_text_field($_POST['search_text']) : '';
91 + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs.
92 + return isset($_POST['search_text']) ? sanitize_text_field(wp_unslash($_POST['search_text'])) : '';
91 93 }
92 94
93 95 /**
94 96 * @return array|mixed
@@ -93,9 +95,17 @@
93 95 /**
94 96 * @return array|mixed
95 97 */
96 98 protected function getselecedIds() {
97 - return isset($_POST['ids']) ? sanitize_text_field($_POST['ids']) : [];
99 + if ( ! check_ajax_referer( 'upk_dynamic_select', 'security', false ) ) {
100 + return [];
101 + }
102 +
103 + if ( ! isset( $_POST['ids'] ) ) {
104 + return [];
105 + }
106 +
107 + return array_values( array_filter( wp_parse_id_list( (array) wp_unslash( $_POST['ids'] ) ) ) );
98 108 }
99 109
100 110
101 111 /**
@@ -129,18 +139,26 @@
129 139 $args = [];
130 140
131 141 $args['post_status'] = 'publish';
132 142
133 - if ($this->getPostType()) {
134 - $args['post_type'] = $this->getPostType();
143 + $public_post_types = $this->getAllPublicPostTypes();
144 + $requested_post_type = $this->getPostType();
145 +
146 + // post_type comes straight from $_POST. Restrict it to the public post types this
147 + // control is meant to browse so it cannot be pointed at a private post type.
148 + if ($requested_post_type && in_array($requested_post_type, $public_post_types, true)) {
149 + $args['post_type'] = $requested_post_type;
135 150 } else {
136 - $args['post_type'] = $this->getAllPublicPostTypes();
151 + $args['post_type'] = $public_post_types;
137 152 }
153 +
138 154 if (!empty($include)) {
139 155 $args['post__in'] = $include;
140 - $args['posts_per_page'] = count($include);
156 + $args['posts_per_page'] = min(100, count($include));
141 157 } else {
142 - $args['posts_per_page'] = -1;
158 + // Never run this unbounded: it is reachable by any 'edit_posts' user and
159 + // -1 returns every published post of every public post type.
160 + $args['posts_per_page'] = 50;
143 161 }
144 162 if ($searchText) {
145 163 $args['s'] = $searchText;
146 164 }
@@ -200,8 +218,10 @@
200 218 $search_text = $this->getSearchQuery();
201 219 $taxonomies = $this->getAllPublicTaxonomies();
202 220 $include = $this->getselecedIds();
203 221
222 + $post_type = '';
223 +
204 224 if ($this->getPostType() == '_ultimate_post_kit_pro_related_post_type') {
205 225 $post_type = $this->getAllPublicPostTypes();
206 226 } elseif ($this->getPostType()) {
207 227 $post_type = $this->getPostType();
@@ -206,8 +226,12 @@
206 226 } elseif ($this->getPostType()) {
207 227 $post_type = $this->getPostType();
208 228 }
209 229
230 + if (empty($post_type)) {
231 + return [];
232 + }
233 +
210 234 $post_taxonomies = get_object_taxonomies($post_type);
211 235 $taxonomies = array_intersect($post_taxonomies, $taxonomies);
212 236 $data = [];
213 237
@@ -216,9 +240,9 @@
216 240 }
217 241
218 242 $args = [
219 243 'taxonomy' => $taxonomies,
220 - 'hide_empty' => false,
244 + 'hide_empty' => true,
221 245 ];
222 246
223 247 if (!empty($include)) {
224 248 $args['include'] = $include;
@@ -263,17 +287,34 @@
263 287
264 288 $args = [
265 289 'fields' => ['ID', 'display_name'],
266 290 'orderby' => 'display_name',
291 + // Always bound the result set. Without this an empty search returns every
292 + // user on the site, unpaged.
293 + 'number' => 20,
267 294 ];
268 295
296 + // This endpoint is only capability-gated on 'edit_posts', so a Contributor can
297 + // reach it. WordPress core restricts callers without 'list_users' to users who
298 + // have published something (see WP_REST_Users_Controller::get_items), so match
299 + // that restriction rather than exposing the full user table.
300 + if (!current_user_can('list_users')) {
301 + $args['has_published_posts'] = true;
302 + }
303 +
269 304 if (!empty($include)) {
270 305 $args['include'] = $include;
306 + // Resolving already-selected values needs room for all of them, but still
307 + // bounded so a long id list cannot be used to dump the table.
308 + $args['number'] = min(100, max(20, count($include)));
271 309 }
272 310
273 311 if ($search_text) {
274 - $args['number'] = 20;
275 312 $args['search'] = "*$search_text*";
313 + // WP_User_Query searches user_email when the term contains "@", which turns
314 + // this into an address oracle. Core strips user_email from the searchable
315 + // columns for callers without 'list_users'; do the same here.
316 + $args['search_columns'] = ['ID', 'user_login', 'user_nicename', 'display_name'];
276 317 }
277 318
278 319 $users = get_users($args);
279 320