← All changes
|
includes/controls/select-input/dynamic-select-input-module.php
+46
-8
4.5.0
→
4.5.6
View file →
| @@ -95,10 +95,17 @@ | ||
| 95 | 95 | /** |
| 96 | 96 | * @return array|mixed |
| 97 | 97 | */ |
| 98 | 98 | protected function getselecedIds() { |
| 99 | - // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs. | |
| 100 | - return isset($_POST['ids']) ? sanitize_text_field(wp_unslash($_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'] ) ) ) ); | |
| 101 | 108 | } |
| 102 | 109 | |
| 103 | 110 | |
| 104 | 111 | /** |
| @@ -132,18 +139,26 @@ | ||
| 132 | 139 | $args = []; |
| 133 | 140 | |
| 134 | 141 | $args['post_status'] = 'publish'; |
| 135 | 142 | |
| 136 | - if ($this->getPostType()) { | |
| 137 | - $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; | |
| 138 | 150 | } else { |
| 139 | - $args['post_type'] = $this->getAllPublicPostTypes(); | |
| 151 | + $args['post_type'] = $public_post_types; | |
| 140 | 152 | } |
| 153 | + | |
| 141 | 154 | if (!empty($include)) { |
| 142 | 155 | $args['post__in'] = $include; |
| 143 | - $args['posts_per_page'] = count($include); | |
| 156 | + $args['posts_per_page'] = min(100, count($include)); | |
| 144 | 157 | } else { |
| 145 | - $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; | |
| 146 | 161 | } |
| 147 | 162 | if ($searchText) { |
| 148 | 163 | $args['s'] = $searchText; |
| 149 | 164 | } |
| @@ -203,8 +218,10 @@ | ||
| 203 | 218 | $search_text = $this->getSearchQuery(); |
| 204 | 219 | $taxonomies = $this->getAllPublicTaxonomies(); |
| 205 | 220 | $include = $this->getselecedIds(); |
| 206 | 221 | |
| 222 | + $post_type = ''; | |
| 223 | + | |
| 207 | 224 | if ($this->getPostType() == '_ultimate_post_kit_pro_related_post_type') { |
| 208 | 225 | $post_type = $this->getAllPublicPostTypes(); |
| 209 | 226 | } elseif ($this->getPostType()) { |
| 210 | 227 | $post_type = $this->getPostType(); |
| @@ -209,8 +226,12 @@ | ||
| 209 | 226 | } elseif ($this->getPostType()) { |
| 210 | 227 | $post_type = $this->getPostType(); |
| 211 | 228 | } |
| 212 | 229 | |
| 230 | + if (empty($post_type)) { | |
| 231 | + return []; | |
| 232 | + } | |
| 233 | + | |
| 213 | 234 | $post_taxonomies = get_object_taxonomies($post_type); |
| 214 | 235 | $taxonomies = array_intersect($post_taxonomies, $taxonomies); |
| 215 | 236 | $data = []; |
| 216 | 237 | |
| @@ -266,17 +287,34 @@ | ||
| 266 | 287 | |
| 267 | 288 | $args = [ |
| 268 | 289 | 'fields' => ['ID', 'display_name'], |
| 269 | 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, | |
| 270 | 294 | ]; |
| 271 | 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 | + | |
| 272 | 304 | if (!empty($include)) { |
| 273 | 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))); | |
| 274 | 309 | } |
| 275 | 310 | |
| 276 | 311 | if ($search_text) { |
| 277 | - $args['number'] = 20; | |
| 278 | 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']; | |
| 279 | 317 | } |
| 280 | 318 | |
| 281 | 319 | $users = get_users($args); |
| 282 | 320 | |