PluginProbe ʕ •ᴥ•ʔ
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More / 1.9.8.7
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More v1.9.8.7
1.10.1.1 1.10.1 1.10.0.5 trunk 1.1.4 1.1.4.2 1.1.5 1.1.5.1 1.1.6 1.1.6.1 1.1.7 1.1.7.1 1.1.7.2 1.1.8 1.1.8.1 1.1.8.2 1.1.8.3 1.1.8.4 1.10.0.1 1.10.0.2 1.10.0.3 1.10.0.4 1.2.0 1.2.0.1 1.2.1 1.2.2 1.2.2.1 1.2.2.2 1.2.3 1.2.3.1 1.2.3.2 1.2.4 1.2.4.1 1.2.5 1.2.5.1 1.2.6 1.2.7 1.2.8 1.2.8.1 1.2.9 1.3.0 1.3.1 1.3.1.1 1.3.1.2 1.3.2 1.3.3 1.3.5 1.3.6 1.3.6.1 1.3.6.2 1.3.7.2 1.3.7.3 1.3.7.4 1.3.8 1.3.9.1 1.4.0.1 1.4.1.1 1.4.2 1.4.2.1 1.4.2.2 1.4.3 1.4.4 1.4.4.1 1.4.5 1.4.5.1 1.4.5.2 1.4.5.3 1.4.6 1.4.7.1 1.4.7.2 1.4.8.1 1.4.9 1.5.0.1 1.5.0.3 1.5.0.4 1.5.1 1.5.1.1 1.5.1.3 1.5.2.1 1.5.2.2 1.5.2.3 1.5.3 1.5.3.1 1.5.4.1 1.5.4.2 1.5.5 1.5.5.1 1.5.6 1.5.6.2 1.5.7 1.5.8.2 1.5.9.1 1.5.9.4 1.5.9.5 1.6.0.1 1.6.0.2 1.6.1 1.6.2.2 1.6.2.3 1.6.3.1 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.7 1.6.7.1 1.6.7.2 1.6.7.3 1.6.8 1.6.8.1 1.6.9 1.7.0 1.7.1.1 1.7.1.2 1.7.2 1.7.2.1 1.7.3 1.7.4 1.7.4.1 1.7.4.2 1.7.5.1 1.7.5.2 1.7.5.3 1.7.5.5 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.7.9.1 1.8.0.1 1.8.0.2 1.8.1.1 1.8.1.2 1.8.1.3 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.3.1 1.8.4 1.8.4.1 1.8.5.2 1.8.5.3 1.8.5.4 1.8.6.2 1.8.6.3 1.8.6.4 1.8.7.2 1.8.8.2 1.8.8.3 1.8.9.1 1.8.9.2 1.8.9.4 1.8.9.5 1.8.9.6 1.9.0.1 1.9.0.2 1.9.0.3 1.9.0.4 1.9.1.1 1.9.1.2 1.9.1.3 1.9.1.4 1.9.1.5 1.9.1.6 1.9.2.1 1.9.2.2 1.9.2.3 1.9.3.1 1.9.3.2 1.9.4.1 1.9.4.2 1.9.5 1.9.5.1 1.9.5.2 1.9.6 1.9.6.1 1.9.6.2 1.9.7.1 1.9.7.2 1.9.7.3 1.9.8.1 1.9.8.2 1.9.8.4 1.9.8.7 1.9.9.2 1.9.9.3 1.9.9.4
wpforms-lite / includes / functions / escape-sanitize.php
wpforms-lite / includes / functions Last commit date
access.php 8 months ago builder.php 11 months ago checks.php 6 months ago colors.php 2 years ago data-presets.php 6 months ago date-time.php 1 year ago debug.php 9 months ago education.php 11 months ago escape-sanitize.php 6 months ago filesystem-media.php 1 year ago form-fields.php 8 months ago forms.php 10 months ago list.php 1 year ago payments.php 10 months ago plugins.php 1 year ago privacy.php 1 year ago providers.php 1 year ago utilities.php 5 months ago
escape-sanitize.php
634 lines
1 <?php
2 /**
3 * Helper functions to clean and sanitize data, escape it and prepare the output.
4 *
5 * @since 1.8.0
6 */
7
8 // phpcs:disable Generic.Commenting.DocComment.MissingShort
9 /** @noinspection PhpUndefinedNamespaceInspection */
10 /** @noinspection PhpUndefinedClassInspection */
11 // phpcs:enable Generic.Commenting.DocComment.MissingShort
12
13 use WPForms\Helpers\Templates;
14 use WPForms\Vendor\HTMLPurifier;
15 use WPForms\Vendor\HTMLPurifier_Config;
16 use WPForms\Helpers\File;
17
18 /**
19 * Decode special characters, both alpha- (<) and numeric-based (').
20 * Sanitize recursively, preserve new lines.
21 * Handle all the possible mixed variations of < and `&lt;` that can be processed into tags.
22 *
23 * @since 1.4.1
24 * @since 1.6.0 Sanitize recursively, preserve new lines.
25 *
26 * @param string $string Raw string to decode.
27 *
28 * @return string
29 */
30 function wpforms_decode_string( $string ) {
31
32 if ( ! is_string( $string ) ) {
33 return $string;
34 }
35
36 /*
37 * Sanitization should be done first, so tags are stripped and < is converted to &lt; etc.
38 * This iteration may do nothing when the string already comes with &lt; and &gt; only.
39 */
40 $string = wpforms_sanitize_text_deeply( $string, true );
41
42 // Now we need to convert the string without tags: &lt; back to < (same for quotes).
43 $string = wp_kses_decode_entities( html_entity_decode( $string, ENT_QUOTES ) );
44
45 // And now we need to sanitize AGAIN, to avoid unwanted tags that appeared after decoding.
46 return wpforms_sanitize_text_deeply( $string, true );
47 }
48
49 /**
50 * Sanitize key, primarily used for looking up options.
51 *
52 * @since 1.3.9
53 *
54 * @param string $key Key name.
55 *
56 * @return string
57 */
58 function wpforms_sanitize_key( $key = '' ) {
59
60 return preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
61 }
62
63 /**
64 * Sanitize hex color.
65 *
66 * @since 1.2.1
67 *
68 * @param string $color Color value.
69 *
70 * @return string
71 */
72 function wpforms_sanitize_hex_color( $color ) {
73
74 if ( empty( $color ) ) {
75 return '';
76 }
77
78 // 3 or 6 hex digits, or the empty string.
79 if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
80 return $color;
81 }
82
83 return '';
84 }
85
86 /**
87 * Sanitize error message, primarily used during form frontend output.
88 *
89 * @since 1.3.7
90 * @since 1.7.6 Expand list of allowed HTML tags and attributes.
91 *
92 * @param string $error Error message.
93 *
94 * @return string
95 */
96 function wpforms_sanitize_error( $error = '' ) {
97
98 $allow = [
99 'a' => [
100 'href' => [],
101 'title' => [],
102 'target' => [],
103 'rel' => [],
104 ],
105 'br' => [],
106 'em' => [],
107 'strong' => [],
108 'del' => [],
109 'p' => [
110 'style' => [],
111 ],
112 'blockquote' => [],
113 'ul' => [],
114 'ol' => [],
115 'li' => [],
116 'span' => [
117 'style' => [],
118 ],
119 ];
120
121 return wp_kses( $error, $allow );
122 }
123
124 /**
125 * Sanitize a string, that can be a multiline.
126 *
127 * @uses wpforms_sanitize_text_deeply()
128 *
129 * @since 1.4.1
130 *
131 * @param string $string String to deeply sanitize.
132 *
133 * @return string Sanitized string, or empty string if not a string provided.
134 */
135 function wpforms_sanitize_textarea_field( $string ) {
136
137 return wpforms_sanitize_text_deeply( $string, true );
138 }
139
140 /**
141 * Deeply sanitize the string, preserve newlines if needed.
142 * Prevent maliciously prepared strings from containing HTML tags.
143 *
144 * @since 1.6.0
145 *
146 * @param string $string String to deeply sanitize.
147 * @param bool $keep_newlines Whether to keep newlines. Default: false.
148 *
149 * @return string Sanitized string, or empty string if not a string provided.
150 */
151 function wpforms_sanitize_text_deeply( $string, $keep_newlines = false ) {
152
153 if ( is_object( $string ) || is_array( $string ) ) {
154 return '';
155 }
156
157 $string = (string) $string;
158 $keep_newlines = (bool) $keep_newlines;
159
160 $new_value = _sanitize_text_fields( $string, $keep_newlines );
161
162 if ( strlen( $new_value ) !== strlen( $string ) ) {
163 $new_value = wpforms_sanitize_text_deeply( $new_value, $keep_newlines );
164 }
165
166 return $new_value;
167 }
168
169 /**
170 * Sanitize an HTML string with a set of allowed HTML tags.
171 *
172 * @since 1.7.0
173 *
174 * @param string $value String to sanitize.
175 *
176 * @return string Sanitized string.
177 */
178 function wpforms_sanitize_richtext_field( $value ) {
179
180 $count = 1;
181 $value = convert_invalid_entities( $value );
182
183 // Remove 'script' and 'style' tags recursively.
184 while ( $count ) {
185 $value = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $value, - 1, $count );
186 }
187
188 // Make sure we have allowed tags only.
189 $value = wp_kses( $value, wpforms_get_allowed_html_tags_for_richtext_field() );
190
191 // Make sure that all tags are balanced.
192 return force_balance_tags( $value );
193 }
194
195 /**
196 * Escaping for Rich Text field values.
197 *
198 * @since 1.7.0
199 * @since 1.9.1 Removed new lines after adding paragraphs and breaks tags.
200 *
201 * @param string $value Text to escape.
202 *
203 * @return string Escaped text.
204 */
205 function wpforms_esc_richtext_field( $value ) {
206
207 $value = wpautop( wpforms_sanitize_richtext_field( $value ) );
208
209 return trim( str_replace( [ "\r\n", "\r", "\n" ], '', $value ) );
210 }
211
212 /**
213 * Retrieve allowed HTML tags for Rich Text field.
214 *
215 * @since 1.7.0
216 *
217 * @return array Array of allowed tags.
218 */
219 function wpforms_get_allowed_html_tags_for_richtext_field() {
220
221 $allowed_tags = array_fill_keys(
222 [
223 'img',
224 'h1',
225 'h2',
226 'h3',
227 'h4',
228 'h5',
229 'h6',
230 'p',
231 'a',
232 'ul',
233 'ol',
234 'li',
235 'dl',
236 'dt',
237 'dd',
238 'hr',
239 'br',
240 'code',
241 'pre',
242 'strong',
243 'b',
244 'em',
245 'i',
246 'blockquote',
247 'cite',
248 'q',
249 'del',
250 'span',
251 'small',
252 'table',
253 'thead',
254 'tbody',
255 'tfoot',
256 'th',
257 'tr',
258 'td',
259 'abbr',
260 'address',
261 'sub',
262 'sup',
263 'ins',
264 'figure',
265 'figcaption',
266 'caption',
267 'div',
268 ],
269 array_fill_keys(
270 [ 'align', 'class', 'id', 'style', 'src', 'rel', 'alt', 'href', 'target', 'width', 'height', 'title', 'cite', 'start', 'reversed', 'datetime', 'scope', 'colspan', 'rowspan' ],
271 []
272 )
273 );
274
275 /**
276 * Allowed HTML tags for Rich Text field.
277 *
278 * @since 1.7.0
279 *
280 * @param array $allowed_tags Allowed HTML tags.
281 */
282 $tags = (array) apply_filters( 'wpforms_get_allowed_html_tags_for_richtext_field', $allowed_tags );
283
284 // Force unset iframes, script and style no matter when we get back
285 // from apply_filters, as they are a huge security risk.
286 unset( $tags['iframe'], $tags['script'], $tags['style'] );
287
288 return $tags;
289 }
290
291 /**
292 * Sanitize an array, that consists of values as strings.
293 * After that - merge all array values into multiline string.
294 *
295 * @since 1.4.1
296 *
297 * @param array $array Data to sanitize.
298 *
299 * @return mixed If not an array is passed (or empty var) - return unmodified var.
300 * Otherwise - a merged array into multiline string.
301 */
302 function wpforms_sanitize_array_combine( $array ) {
303
304 if ( empty( $array ) || ! is_array( $array ) ) {
305 return $array;
306 }
307
308 return implode( "\n", array_map( 'sanitize_text_field', $array ) );
309 }
310
311 /**
312 * Format, sanitize, and return/echo HTML element ID, classes, attributes,
313 * and data attributes.
314 *
315 * @since 1.3.7
316 *
317 * @param string $id HTML id attribute value.
318 * @param array $class A list of classnames for the class attribute.
319 * @param array $datas Data attributes.
320 * @param array $atts Any additional HTML attributes and their values.
321 * @param bool $echo Whether to echo the output or just return it. Defaults to return.
322 *
323 * @return string|void
324 */
325 function wpforms_html_attributes( $id = '', $class = [], $datas = [], $atts = [], $echo = false ) {
326
327 $id = trim( $id );
328 $parts = [];
329
330 if ( ! empty( $id ) ) {
331 $id = sanitize_html_class( $id );
332
333 if ( ! empty( $id ) ) {
334 $parts[] = 'id="' . $id . '"';
335 }
336 }
337
338 if ( ! empty( $class ) ) {
339 $class = wpforms_sanitize_classes( $class, true );
340
341 if ( ! empty( $class ) ) {
342 $parts[] = 'class="' . $class . '"';
343 }
344 }
345
346 if ( ! empty( $datas ) ) {
347 foreach ( $datas as $data => $val ) {
348 $parts[] = 'data-' . sanitize_html_class( $data ) . '="' . esc_attr( $val ) . '"';
349 }
350 }
351
352 if ( ! empty( $atts ) ) {
353 foreach ( $atts as $att => $val ) {
354 if ( '0' === (string) $val || ! empty( $val ) ) {
355 if ( $att[0] === '[' ) {
356 // Handle special case for bound attributes in AMP.
357 $escaped_att = '[' . sanitize_html_class( trim( $att, '[]' ) ) . ']';
358 } else {
359 $escaped_att = sanitize_html_class( $att );
360 }
361 $parts[] = $escaped_att . '="' . esc_attr( $val ) . '"';
362 }
363 }
364 }
365
366 $output = implode( ' ', $parts );
367
368 if ( $echo ) {
369 echo trim( $output ); // phpcs:ignore
370 } else {
371 return trim( $output );
372 }
373 }
374
375 /**
376 * Sanitize string of CSS classes.
377 *
378 * @since 1.2.1
379 *
380 * @param array|string $classes CSS classes.
381 * @param bool $convert True will convert strings to array and vice versa.
382 *
383 * @return string|array
384 */
385 function wpforms_sanitize_classes( $classes, $convert = false ) {
386
387 $array = is_array( $classes );
388 $css = [];
389
390 if ( ! empty( $classes ) ) {
391 if ( ! $array ) {
392 $classes = explode( ' ', trim( $classes ) );
393 }
394 foreach ( array_unique( $classes ) as $class ) {
395 if ( ! empty( $class ) ) {
396 $css[] = sanitize_html_class( $class );
397 }
398 }
399 }
400
401 if ( $array ) {
402 return $convert ? implode( ' ', $css ) : $css;
403 }
404
405 return $convert ? $css : implode( ' ', $css );
406 }
407
408 /**
409 * Include a template - alias to \WPForms\Helpers\Template::get_html.
410 * Use 'require' if $args are passed or 'load_template' if not.
411 *
412 * @since 1.5.6
413 *
414 * @param string $template_name Template name.
415 * @param array $args Arguments.
416 * @param bool $extract Extract arguments.
417 *
418 * @throws RuntimeException If extract() tries to modify the scope.
419 *
420 * @return string Compiled HTML.
421 */
422 function wpforms_render( $template_name, $args = [], $extract = false ) {
423
424 return Templates::get_html( $template_name, $args, $extract );
425 }
426
427 /**
428 * Alias for default readonly function.
429 *
430 * @since 1.6.9
431 *
432 * @param mixed $readonly One of the values to compare.
433 * @param mixed $current The other value to compare if not just true.
434 * @param bool $echo Whether to echo or just return the string.
435 *
436 * @return string HTML attribute or empty string.
437 */
438 function wpforms_readonly( $readonly, $current = true, $echo = true ) {
439
440 if ( function_exists( 'wp_readonly' ) ) {
441 return wp_readonly( $readonly, $current, $echo );
442 }
443
444 return __checked_selected_helper( $readonly, $current, $echo, 'readonly' );
445 }
446
447 /**
448 * Get the required label text, with a filter.
449 *
450 * @since 1.4.4
451 *
452 * @return string
453 */
454 function wpforms_get_required_label() {
455
456 return apply_filters( 'wpforms_required_label', esc_html__( 'This field is required.', 'wpforms-lite' ) );
457 }
458
459 /**
460 * Get the required field label HTML, with a filter.
461 *
462 * @since 1.4.8
463 *
464 * @return string
465 */
466 function wpforms_get_field_required_label() {
467
468 $label_html = apply_filters_deprecated(
469 'wpforms_field_required_label',
470 [ ' <span class="wpforms-required-label">*</span>' ],
471 '1.4.8 of the WPForms plugin',
472 'wpforms_get_field_required_label'
473 );
474
475 return apply_filters( 'wpforms_get_field_required_label', $label_html );
476 }
477
478 /**
479 * Escape unselected choices for radio/checkbox fields.
480 *
481 * @since 1.8.3
482 *
483 * @param string $formatted_field HTML field.
484 *
485 * @return string
486 */
487 function wpforms_esc_unselected_choices( $formatted_field ) {
488
489 $allowed_html = wp_kses_allowed_html( 'post' );
490
491 $allowed_html['input'] = [
492 'type' => [],
493 'disabled' => [],
494 'checked' => [],
495 'class' => [],
496 'value' => [],
497 ];
498 $allowed_html['label'] = [];
499
500 return wp_kses( $formatted_field, $allowed_html );
501 }
502
503 /**
504 * Decode HTML entities in a string.
505 * Do it cycle to decode all possible entities, including cases like `&amp;lt;`.
506 *
507 * @since 1.9.2.3
508 *
509 * @param string $html HTML.
510 * @param int $flags Flags.
511 * @param string|null $encoding Encoding.
512 *
513 * @return string
514 * @noinspection PhpMissingParamTypeInspection
515 */
516 function wpforms_html_entity_decode_deep( string $html, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, $encoding = null ): string {
517
518 do {
519 $previous_html = $html;
520 $html = html_entity_decode( $html, $flags, $encoding );
521 } while ( $html !== $previous_html );
522
523 return $html;
524 }
525
526 /**
527 * Sanitize form data.
528 *
529 * @since 1.9.3
530 *
531 * @param array $data Form data.
532 *
533 * @return array
534 */
535 function wpforms_sanitize_form_data( array $data ): array {
536
537 foreach ( $data['fields'] as & $field ) {
538 $field = wpforms_sanitize_field( $field );
539 }
540
541 unset( $field );
542
543 return $data;
544 }
545
546 /**
547 * Sanitize form field.
548 *
549 * @since 1.9.3
550 *
551 * @param array $field Field.
552 *
553 * @return array
554 */
555 function wpforms_sanitize_field( array $field ): array {
556
557 $raw_field_options = [
558 'html' => [ 'code' ],
559 ];
560
561 $field_type = $field['type'] ?? '';
562 $raw_options = $raw_field_options[ $field_type ] ?? [];
563
564 /**
565 * Filter raw options for a field type.
566 * Allows modifying options that should not be sanitized.
567 *
568 * @since 1.9.3
569 *
570 * @param array $raw_options Raw options.
571 * @param string $field_type Field type.
572 */
573 $raw_options = (array) apply_filters( 'wpforms_raw_options', $raw_options, $field_type );
574
575 $purifier = wpforms_get_html_purifier();
576 $decode_and_purify = static function ( $item ) use ( $purifier ) {
577 return $purifier->purify( wpforms_html_entity_decode_deep( $item ) );
578 };
579
580 foreach ( $field as $option => & $value ) {
581 if ( in_array( $option, $raw_options, true ) ) {
582 continue;
583 }
584
585 $value = wp_unslash( $value );
586
587 if ( is_array( $value ) ) {
588 array_walk_recursive( $value, $decode_and_purify );
589 } else {
590 $value = $decode_and_purify( $value );
591 }
592
593 $value = wp_slash( $value );
594 }
595
596 unset( $value );
597
598 return $field;
599 }
600
601 /**
602 * Get allowed HTML purifier object.
603 *
604 * @since 1.9.3
605 *
606 * @return HTMLPurifier
607 */
608 function wpforms_get_html_purifier(): HTMLPurifier {
609
610 static $purifier;
611
612 if ( $purifier ) {
613 return $purifier;
614 }
615
616 require_once WPFORMS_PLUGIN_DIR . '/vendor_prefixed/ezyang/htmlpurifier/library/HTMLPurifier.auto.php';
617
618 $config = HTMLPurifier_Config::createDefault();
619 $cache_dir = trailingslashit( File::get_cache_dir() ) . 'htmlpurifier';
620
621 // Make sure the cache directory exists.
622 File::mkdir( $cache_dir );
623
624 $config->set( 'Cache.SerializerPath', $cache_dir );
625 $config->set( 'Attr.AllowedRel', 'noopener,noreferrer,external,follow,nofollow,ugc,sponsored,tag' );
626 $config->set( 'Attr.AllowedFrameTargets', [ '_blank', '_self', '_parent', '_top' ] );
627 $config->set( 'HTML.TargetNoopener', false );
628 $config->set( 'HTML.TargetNoreferrer', false );
629
630 $purifier = new HTMLPurifier( $config );
631
632 return $purifier;
633 }
634