PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmHtmlSanitizer.php

FrmHtmlSanitizer.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More trunk, at classes/models/FrmHtmlSanitizer.php

94 lines 2.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 /**
7 * Sanitize HTML attribute values to prevent stored XSS.
8 *
9 * @since 6.34
10 */
11 class FrmHtmlSanitizer {
12
13 /**
14 * Sanitize href and src attribute values to valid URLs only.
15 *
16 * Decodes HTML entities in the attribute value before validating,
17 * so entity-encoded payloads are rejected.
18 *
19 * @since 6.34
20 *
21 * @param string $value HTML string to process.
22 *
23 * @return string
24 */
25 public static function sanitize_url_attributes( $value ) {
26 if ( '' === $value || ( ! str_contains( $value, 'href' ) && ! str_contains( $value, 'src' ) ) ) {
27 return $value;
28 }
29
30 $sanitized = preg_replace_callback(
31 '/\b(href|src)\s*=\s*"([^"]*)"/',
32 array( self::class, 'sanitize_url_attribute_value' ),
33 $value
34 );
35
36 return $sanitized ?? '';
37 }
38
39 /**
40 * Callback to sanitize a single URL attribute match.
41 *
42 * @since 6.34
43 *
44 * @param array $matches Regex matches with attribute name and value.
45 *
46 * @return string Rebuilt attribute with a safe URL value.
47 */
48 private static function sanitize_url_attribute_value( $matches ) {
49 $url = trim( html_entity_decode( $matches[2], ENT_QUOTES, 'UTF-8' ) );
50
51 if ( str_starts_with( $url, '#' ) ) {
52 return $matches[1] . '="' . esc_attr( $url ) . '"';
53 }
54
55 if ( 'src' === $matches[1] && self::is_png_data_uri( $url ) ) {
56 return $matches[1] . '="' . esc_attr( $url ) . '"';
57 }
58
59 if ( ! preg_match( '/^(https?:\/\/|mailto:|tel:)/i', $url ) ) {
60 return $matches[1] . '=""';
61 }
62
63 $safe = esc_url( $url, array( 'http', 'https', 'mailto', 'tel' ) );
64
65 if ( '' === $safe ) {
66 return $matches[1] . '=""';
67 }
68
69 $host = wp_parse_url( $safe, PHP_URL_HOST );
70
71 if ( $host && preg_match( '/%[0-9a-f]{2}/i', $host ) ) {
72 return $matches[1] . '=""';
73 }
74
75 return $matches[1] . '="' . esc_attr( $safe ) . '"';
76 }
77
78 /**
79 * Check for a PNG data URI that contains only base64 characters, like a drawn signature image src.
80 *
81 * The pattern allows only base64 characters after the prefix, so the value cannot carry a media
82 * type of its own or any markup into the attribute.
83 *
84 * @since 6.34
85 *
86 * @param string $url Decoded URL value to check.
87 *
88 * @return bool
89 */
90 private static function is_png_data_uri( $url ) {
91 return 1 === preg_match( '#^data:image/png;base64,[A-Za-z0-9+/]+={0,2}$#D', $url );
92 }
93 }
94