PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.83
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / helpers / Grid / Grid_Ajax_Security.php

Grid_Ajax_Security.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.83, at includes/helpers/Grid/Grid_Ajax_Security.php

424 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace King_Addons;
4
5 use King_Addons\Animations\Animations;
6
7 if (!defined('ABSPATH')) {
8 exit;
9 }
10
11 /**
12 * Sanitizes untrusted grid settings received through AJAX handlers.
13 */
14 class Grid_Ajax_Security
15 {
16 /**
17 * Returns allowed animation size values.
18 *
19 * @return array<int, string> Allowed size slugs.
20 */
21 public static function get_allowed_animation_sizes(): array
22 {
23 return ['small', 'medium', 'large'];
24 }
25
26 /**
27 * Returns allowed image effect slugs.
28 *
29 * @return array<int, string> Allowed effect slugs.
30 */
31 public static function get_allowed_image_effects(): array
32 {
33 return ['none', 'pro-zi', 'pro-zo', 'grayscale-in', 'pro-go', 'blur-in', 'pro-bo', 'slide'];
34 }
35
36 /**
37 * Returns allowed image effect direction values.
38 *
39 * @return array<int, string> Allowed direction slugs.
40 */
41 public static function get_allowed_image_effect_directions(): array
42 {
43 return ['top', 'right', 'bottom', 'left'];
44 }
45
46 /**
47 * Sanitizes an animation slug against the widget allowlist.
48 *
49 * @param mixed $value Raw animation value.
50 * @return string Sanitized animation slug.
51 */
52 public static function sanitize_animation($value): string
53 {
54 $value = sanitize_key((string) $value);
55
56 return in_array($value, Animations::get_animation_slugs(), true) ? $value : 'none';
57 }
58
59 /**
60 * Sanitizes an animation size slug.
61 *
62 * @param mixed $value Raw animation size value.
63 * @return string Sanitized animation size slug.
64 */
65 public static function sanitize_animation_size($value): string
66 {
67 $value = sanitize_key((string) $value);
68
69 return in_array($value, self::get_allowed_animation_sizes(), true) ? $value : 'large';
70 }
71
72 /**
73 * Sanitizes an animation timing slug.
74 *
75 * @param mixed $value Raw animation timing value.
76 * @return string Sanitized animation timing slug.
77 */
78 public static function sanitize_animation_timing($value): string
79 {
80 $value = sanitize_key((string) $value);
81 $allowed = array_keys(Core::getAnimationTimings());
82
83 return in_array($value, $allowed, true) ? $value : 'ease-default';
84 }
85
86 /**
87 * Sanitizes an image effect slug.
88 *
89 * @param mixed $value Raw image effect value.
90 * @return string Sanitized image effect slug.
91 */
92 public static function sanitize_image_effect($value): string
93 {
94 $value = sanitize_key((string) $value);
95
96 if (!in_array($value, self::get_allowed_image_effects(), true)) {
97 return 'none';
98 }
99
100 if (
101 !king_addons_freemius()->can_use_premium_code__premium_only()
102 && in_array($value, ['pro-zi', 'pro-zo', 'pro-go', 'pro-bo'], true)
103 ) {
104 return 'none';
105 }
106
107 return $value;
108 }
109
110 /**
111 * Sanitizes an image effect size slug.
112 *
113 * @param mixed $value Raw image effect size value.
114 * @return string Sanitized image effect size slug.
115 */
116 public static function sanitize_image_effect_size($value): string
117 {
118 $value = sanitize_key((string) $value);
119
120 return in_array($value, self::get_allowed_animation_sizes(), true) ? $value : 'medium';
121 }
122
123 /**
124 * Sanitizes an image effect direction slug.
125 *
126 * @param mixed $value Raw image effect direction value.
127 * @return string Sanitized image effect direction slug.
128 */
129 public static function sanitize_image_effect_direction($value): string
130 {
131 $value = sanitize_key((string) $value);
132
133 return in_array($value, self::get_allowed_image_effect_directions(), true) ? $value : 'bottom';
134 }
135
136 /**
137 * Sanitizes a yes/no switcher value.
138 *
139 * @param mixed $value Raw switcher value.
140 * @return string Either "yes" or an empty string.
141 */
142 public static function sanitize_yes_no_switcher($value): string
143 {
144 return 'yes' === $value ? 'yes' : '';
145 }
146
147 /**
148 * Returns allowed HTML tags for grid titles.
149 *
150 * @return array<int, string> Allowed tag names.
151 */
152 public static function get_allowed_html_tags(): array
153 {
154 return ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'span', 'p'];
155 }
156
157 /**
158 * Sanitizes an HTML tag name against the grid allowlist.
159 *
160 * @param mixed $value Raw tag name.
161 * @return string Allowed tag name.
162 */
163 public static function sanitize_html_tag($value): string
164 {
165 $value = strtolower(sanitize_key((string) $value));
166
167 return in_array($value, self::get_allowed_html_tags(), true) ? $value : 'h2';
168 }
169
170 /**
171 * Sanitizes a CSS class token.
172 *
173 * @param mixed $value Raw class token.
174 * @return string Safe class token.
175 */
176 public static function sanitize_class_token($value): string
177 {
178 return sanitize_html_class((string) $value);
179 }
180
181 /**
182 * Sanitizes a query source / post type slug.
183 *
184 * @param mixed $value Raw query source.
185 * @return string Allowed query source.
186 */
187 public static function sanitize_query_source($value): string
188 {
189 $value = sanitize_key((string) $value);
190 $allowed = array_merge(
191 ['post', 'page', 'product', 'attachment', 'current', 'related', 'manual', 'dynamic'],
192 array_keys(get_post_types(['public' => true], 'names'))
193 );
194
195 return in_array($value, $allowed, true) ? $value : 'post';
196 }
197
198 /**
199 * Returns sanitized grid settings from the current AJAX request.
200 *
201 * @return array<string, mixed> Sanitized settings array.
202 */
203 public static function get_posted_grid_settings(): array
204 {
205 $settings = isset($_POST['grid_settings']) ? wp_unslash($_POST['grid_settings']) : [];
206
207 if (!is_array($settings)) {
208 return [];
209 }
210
211 return self::sanitize_grid_settings($settings);
212 }
213
214 /**
215 * Sanitizes grid settings that may be reflected into HTML.
216 *
217 * @param array<string, mixed> $settings Raw grid settings.
218 * @return array<string, mixed> Sanitized grid settings.
219 */
220 public static function sanitize_grid_settings(array $settings): array
221 {
222 $class_keys = [
223 'overlay_animation',
224 'overlay_animation_size',
225 'overlay_animation_timing',
226 'image_effects',
227 'image_effects_size',
228 'image_effects_direction',
229 'image_effects_animation_timing',
230 'title_pointer',
231 'title_pointer_animation',
232 'tax1_pointer',
233 'tax1_pointer_animation',
234 'tax2_pointer',
235 'tax2_pointer_animation',
236 'read_more_animation',
237 'layout_select',
238 'pagination_type',
239 'element_separator_style',
240 'element_tax_style',
241 'element_custom_field_style',
242 'overlay_post_link',
243 ];
244
245 foreach ($class_keys as $key) {
246 if (isset($settings[$key]) && is_scalar($settings[$key])) {
247 $settings[$key] = self::sanitize_class_token($settings[$key]);
248 }
249 }
250
251 if (isset($settings['overlay_animation'])) {
252 $settings['overlay_animation'] = self::sanitize_animation($settings['overlay_animation']);
253 }
254 if (isset($settings['overlay_animation_size'])) {
255 $settings['overlay_animation_size'] = self::sanitize_animation_size($settings['overlay_animation_size']);
256 }
257 if (isset($settings['overlay_animation_timing'])) {
258 $settings['overlay_animation_timing'] = self::sanitize_animation_timing($settings['overlay_animation_timing']);
259 }
260 if (isset($settings['overlay_animation_tr'])) {
261 $settings['overlay_animation_tr'] = self::sanitize_yes_no_switcher($settings['overlay_animation_tr']);
262 }
263 if (isset($settings['image_effects'])) {
264 $settings['image_effects'] = self::sanitize_image_effect($settings['image_effects']);
265 }
266 if (isset($settings['image_effects_size'])) {
267 $settings['image_effects_size'] = self::sanitize_image_effect_size($settings['image_effects_size']);
268 }
269 if (isset($settings['image_effects_direction'])) {
270 $settings['image_effects_direction'] = self::sanitize_image_effect_direction($settings['image_effects_direction']);
271 }
272 if (isset($settings['image_effects_animation_timing'])) {
273 $settings['image_effects_animation_timing'] = self::sanitize_animation_timing($settings['image_effects_animation_timing']);
274 }
275 if (isset($settings['element_title_tag'])) {
276 $settings['element_title_tag'] = self::sanitize_html_tag($settings['element_title_tag']);
277 }
278 if (isset($settings['query_source'])) {
279 $settings['query_source'] = self::sanitize_query_source($settings['query_source']);
280 }
281 if (isset($settings['query_tax_selection'])) {
282 $settings['query_tax_selection'] = sanitize_key((string) $settings['query_tax_selection']);
283 }
284 if (isset($settings['order_posts'])) {
285 $settings['order_posts'] = sanitize_key((string) $settings['order_posts']);
286 }
287 if (isset($settings['order_direction'])) {
288 $settings['order_direction'] = in_array(strtoupper((string) $settings['order_direction']), ['ASC', 'DESC'], true)
289 ? strtoupper((string) $settings['order_direction'])
290 : 'DESC';
291 }
292 if (isset($settings['query_randomize'])) {
293 $settings['query_randomize'] = sanitize_key((string) $settings['query_randomize']);
294 }
295 foreach (['query_offset', 'query_posts_per_page', 'query_slides_to_show'] as $int_key) {
296 if (isset($settings[$int_key])) {
297 $settings[$int_key] = absint($settings[$int_key]);
298 }
299 }
300 if (isset($settings['query_author']) && is_array($settings['query_author'])) {
301 $settings['query_author'] = array_map('absint', $settings['query_author']);
302 }
303 foreach ($settings as $key => $value) {
304 if (!is_string($key) || !is_array($value)) {
305 continue;
306 }
307 if (0 === strpos($key, 'query_taxonomy_') || 0 === strpos($key, 'query_exclude_')) {
308 $settings[$key] = array_map('absint', $value);
309 }
310 }
311 if (isset($settings['tax1_custom_color_switcher'])) {
312 $settings['tax1_custom_color_switcher'] = self::sanitize_yes_no_switcher($settings['tax1_custom_color_switcher']);
313 }
314 if (isset($settings['tax1_custom_color_field_text'])) {
315 $settings['tax1_custom_color_field_text'] = sanitize_key((string) $settings['tax1_custom_color_field_text']);
316 }
317 if (isset($settings['tax1_custom_color_field_bg'])) {
318 $settings['tax1_custom_color_field_bg'] = sanitize_key((string) $settings['tax1_custom_color_field_bg']);
319 }
320 if (isset($settings['open_links_in_new_tab'])) {
321 $settings['open_links_in_new_tab'] = self::sanitize_yes_no_switcher($settings['open_links_in_new_tab']);
322 }
323 if (isset($settings['secondary_img_on_hover'])) {
324 $settings['secondary_img_on_hover'] = self::sanitize_yes_no_switcher($settings['secondary_img_on_hover']);
325 }
326 if (isset($settings['grid_lazy_loading'])) {
327 $settings['grid_lazy_loading'] = self::sanitize_yes_no_switcher($settings['grid_lazy_loading']);
328 }
329
330 if (!empty($settings['overlay_image']) && is_array($settings['overlay_image'])) {
331 $settings['overlay_image']['url'] = esc_url_raw($settings['overlay_image']['url'] ?? '');
332 $settings['overlay_image']['alt'] = sanitize_text_field($settings['overlay_image']['alt'] ?? '');
333 }
334
335 if (!empty($settings['grid_elements']) && is_array($settings['grid_elements'])) {
336 foreach ($settings['grid_elements'] as $index => $element) {
337 if (!is_array($element)) {
338 unset($settings['grid_elements'][$index]);
339 continue;
340 }
341 $settings['grid_elements'][$index] = self::sanitize_grid_element($element);
342 }
343 }
344
345 return $settings;
346 }
347
348 /**
349 * Sanitizes one grid element repeater row.
350 *
351 * @param array<string, mixed> $element Raw element settings.
352 * @return array<string, mixed> Sanitized element settings.
353 */
354 public static function sanitize_grid_element(array $element): array
355 {
356 foreach (['element_select', '_id', 'element_display', 'element_align_hr', 'element_align_vr', 'element_location'] as $key) {
357 if (isset($element[$key]) && is_scalar($element[$key])) {
358 $element[$key] = self::sanitize_class_token($element[$key]);
359 }
360 }
361
362 if (isset($element['element_title_tag'])) {
363 $element['element_title_tag'] = self::sanitize_html_tag($element['element_title_tag']);
364 }
365 if (isset($element['element_animation'])) {
366 $element['element_animation'] = self::sanitize_animation($element['element_animation']);
367 }
368 if (isset($element['element_animation_size'])) {
369 $element['element_animation_size'] = self::sanitize_animation_size($element['element_animation_size']);
370 }
371 if (isset($element['element_animation_timing'])) {
372 $element['element_animation_timing'] = self::sanitize_animation_timing($element['element_animation_timing']);
373 }
374 if (isset($element['element_animation_tr'])) {
375 $element['element_animation_tr'] = self::sanitize_yes_no_switcher($element['element_animation_tr']);
376 }
377 if (isset($element['element_separator_style'])) {
378 $element['element_separator_style'] = self::sanitize_class_token($element['element_separator_style']);
379 }
380 if (isset($element['element_tax_style'])) {
381 $element['element_tax_style'] = self::sanitize_class_token($element['element_tax_style']);
382 }
383 if (isset($element['element_read_more_text'])) {
384 $element['element_read_more_text'] = sanitize_text_field((string) $element['element_read_more_text']);
385 }
386 if (isset($element['element_extra_text'])) {
387 $element['element_extra_text'] = sanitize_text_field((string) $element['element_extra_text']);
388 }
389 if (isset($element['element_tax_sep'])) {
390 $element['element_tax_sep'] = sanitize_text_field((string) $element['element_tax_sep']);
391 }
392
393 return $element;
394 }
395
396 /**
397 * Sanitizes a CSS color value used in generated style tags.
398 *
399 * @param mixed $value Raw color value.
400 * @return string Safe color string or an empty string.
401 */
402 public static function sanitize_css_color($value): string
403 {
404 $value = trim((string) $value);
405 if ('' === $value) {
406 return '';
407 }
408
409 if (preg_match('/^#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/', $value)) {
410 return $value;
411 }
412
413 if (preg_match('/^(rgb|rgba|hsl|hsla)\(\s*[0-9.%,\s\/]+\s*\)$/i', $value)) {
414 return $value;
415 }
416
417 if (preg_match('/^[a-zA-Z]+$/', $value)) {
418 return sanitize_key($value);
419 }
420
421 return '';
422 }
423 }
424