PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 All 197 releases
← All changes | app/Services/FormBuilder/EditorShortcodeParser.php +89 -55 6.2.9 → 6.2.15 View file →
@@ -8,14 +8,16 @@
8 8 class EditorShortcodeParser
9 9 {
10 10 /**
11 11 * Available dynamic short codes
12 + *
12 13 * @var null
13 14 */
14 15 private static $dynamicShortcodes = null;
15 -
16 +
16 17 /**
17 18 * mappings of methods to parse the shortcode
19 + *
18 20 * @var array
19 21 */
20 22 private static $handlers = [
21 23 'ip' => 'parseIp',
@@ -20,18 +22,18 @@
20 22 private static $handlers = [
21 23 'ip' => 'parseIp',
22 24 'date.m/d/Y' => 'parseDate',
23 25 'date.d/m/Y' => 'parseDate',
24 -
26 +
25 27 'embed_post.ID' => 'parsePostProperties',
26 28 'embed_post.post_title' => 'parsePostProperties',
27 29 'embed_post.permalink' => 'parsePostProperties',
28 30 'http_referer' => 'parseWPProperties',
29 -
31 +
30 32 'wp.admin_email' => 'parseWPProperties',
31 33 'wp.site_url' => 'parseWPProperties',
32 34 'wp.site_title' => 'parseWPProperties',
33 -
35 +
34 36 'user.ID' => 'parseUserProperties',
35 37 'user.display_name' => 'parseUserProperties',
36 38 'user.first_name' => 'parseUserProperties',
37 39 'user.last_name' => 'parseUserProperties',
@@ -36,16 +38,16 @@
36 38 'user.first_name' => 'parseUserProperties',
37 39 'user.last_name' => 'parseUserProperties',
38 40 'user.user_email' => 'parseUserProperties',
39 41 'user.user_login' => 'parseUserProperties',
40 -
42 +
41 43 'browser.name' => 'parseBrowserProperties',
42 44 'browser.platform' => 'parseBrowserProperties',
43 -
45 +
44 46 'get.param_name' => 'parseRequestParam',
45 47 'random_string.param_name' => 'parseRandomString',
46 48 ];
47 -
49 +
48 50 /**
49 51 * Filter dynamic shortcodes in input value
50 52 *
51 53 * @param string $value
@@ -57,15 +59,15 @@
57 59 if (0 === strpos($value, '{ ')) {
58 60 // it's the css
59 61 return $value;
60 62 }
61 -
63 +
62 64 if (is_null(static::$dynamicShortcodes)) {
63 65 static::$dynamicShortcodes = fluentFormEditorShortCodes();
64 66 }
65 -
67 +
66 68 $filteredValue = '';
67 -
69 +
68 70 foreach (static::parseValue($value) as $handler) {
69 71 if (isset(static::$handlers[$handler])) {
70 72 return call_user_func_array(
71 73 [__CLASS__, static::$handlers[$handler]],
@@ -71,9 +73,9 @@
71 73 [__CLASS__, static::$handlers[$handler]],
72 74 ['{' . $handler . '}', $form]
73 75 );
74 76 }
75 -
77 +
76 78 if (false !== strpos($handler, 'get.')) {
77 79 return static::parseRequestParam($handler);
78 80 }
79 81 if (false !== strpos($handler, 'random_string.')) {
@@ -78,9 +80,9 @@
78 80 }
79 81 if (false !== strpos($handler, 'random_string.')) {
80 82 return static::parseRandomString($handler);
81 83 }
82 -
84 +
83 85 if (false !== strpos($handler, 'user.')) {
84 86 $parsedValue = self::parseUserProperties($handler);
85 87 if (is_array($parsedValue) || is_object($parsedValue)) {
86 88 return '';
@@ -86,13 +88,13 @@
86 88 return '';
87 89 }
88 90 return esc_html($parsedValue);
89 91 }
90 -
92 +
91 93 if (false !== strpos($handler, 'date.')) {
92 94 return esc_html(self::parseDate($handler));
93 95 }
94 -
96 +
95 97 if (false !== strpos($handler, 'embed_post.meta.')) {
96 98 $key = substr(str_replace(['{', '}'], '', $value), 16);
97 99 global $post;
98 100 if ($post) {
@@ -102,19 +104,20 @@
102 104 }
103 105 }
104 106 return '';
105 107 }
106 -
108 +
107 109 if (false !== strpos($handler, 'embed_post.')) {
108 110 return self::parsePostProperties($handler, $form);
109 111 }
110 -
112 +
111 113 if (false !== strpos($handler, 'cookie.')) {
112 114 $scookieProperty = substr($handler, strlen('cookie.'));
113 -
114 - return array_key_exists($scookieProperty, $_COOKIE) ? wp_unslash($_COOKIE[$scookieProperty]) : '';
115 + $cookieValue = array_key_exists($scookieProperty, $_COOKIE) ? sanitize_text_field(wp_unslash($_COOKIE[$scookieProperty])) : '';
116 +
117 + return static::escapeReflectedValue($cookieValue);
115 118 }
116 -
119 +
117 120 if (false !== strpos($handler, 'dynamic.')) {
118 121 $dynamicKey = substr($handler, strlen('dynamic.'));
119 122 // maybe has fallback value
120 123 $dynamicKey = explode('|', $dynamicKey);
@@ -125,23 +128,23 @@
125 128 }
126 129 if (isset($dynamicKey[0])) {
127 130 $ref = $dynamicKey[0];
128 131 }
129 -
132 +
130 133 if ('payment_summary' == $ref) {
131 134 return fluentform_sanitize_html('<div class="ff_dynamic_value ff_dynamic_payment_summary" data-ref="payment_summary"><div class="ff_payment_summary"></div><div class="ff_payment_summary_fallback">' . $fallBack . '</div></div>');
132 135 }
133 -
136 +
134 137 return fluentform_sanitize_html('<span class="ff_dynamic_value" data-ref="' . $ref . '" data-fallback="' . $fallBack . '">' . $fallBack . '</span>');
135 138 }
136 -
139 +
137 140 // if it's multi line then just return
138 141 if (false !== strpos($handler, PHP_EOL)) { // most probably it's a css
139 142 return '{' . $handler . '}';
140 143 }
141 -
144 +
142 145 $handlerArray = explode('.', $handler);
143 -
146 +
144 147 if (count($handlerArray) > 1) {
145 148 // it's a grouped handler
146 149 $group = array_shift($handlerArray);
147 150 $parsedValue = apply_filters('fluentform_editor_shortcode_callback_group_' . $group, '{' . $handler . '}', $form, $handlerArray);
@@ -146,16 +149,16 @@
146 149 $group = array_shift($handlerArray);
147 150 $parsedValue = apply_filters('fluentform_editor_shortcode_callback_group_' . $group, '{' . $handler . '}', $form, $handlerArray);
148 151 return apply_filters('fluentform/editor_shortcode_callback_group_' . $group, $parsedValue, $form, $handlerArray);
149 152 }
150 -
153 +
151 154 $parsedValue = apply_filters('fluentform_editor_shortcode_callback_' . $handler, '{' . $handler . '}', $form);
152 155 return apply_filters('fluentform/editor_shortcode_callback_' . $handler, $parsedValue, $form);
153 156 }
154 -
157 +
155 158 return $filteredValue;
156 159 }
157 -
160 +
158 161 /**
159 162 * Parse request query param.
160 163 *
161 164 * @param string $value
@@ -172,12 +175,39 @@
172 175 if (null === $value || '' === $value) {
173 176 return '';
174 177 }
175 178
176 - return esc_attr(Helper::flattenRequestValue($value));
179 + return static::escapeReflectedValue(Helper::flattenRequestValue($value));
177 180 }
178 -
181 +
179 182 /**
183 + * Escape a visitor-supplied value ({get.x}, {cookie.x}) for the assembled form HTML.
184 + *
185 + * Smartcodes are substituted after Custom HTML was sanitized, so the value can land in
186 + * any attribute, including an iframe src or anchor href. esc_attr() leaves a javascript:
187 + * scheme intact and keeps existing entities (?p=java&#9;script:...), so encode every
188 + * ampersand and drop values that would resolve to a script-capable URL.
189 + *
190 + * @param string $value
191 + *
192 + * @return string
193 + */
194 + public static function escapeReflectedValue($value)
195 + {
196 + $value = wp_check_invalid_utf8((string) $value);
197 +
198 + // Browsers strip control chars and whitespace from URLs before reading the scheme.
199 + $scheme = strtolower(preg_replace('/[\x00-\x20]+/', '', $value));
200 +
201 + if (preg_match('/^(javascript|vbscript|data):/', $scheme)) {
202 + return '';
203 + }
204 +
205 + // Encode braces too, so the value cannot plant a smartcode for a later replacement pass.
206 + return str_replace(['{', '}'], ['&#123;', '&#125;'], htmlspecialchars($value, ENT_QUOTES, 'UTF-8', true));
207 + }
208 +
209 + /**
180 210 * Parse the curly braced shortcode into array
181 211 *
182 212 * @param string $value
183 213 *
@@ -192,16 +222,16 @@
192 222 -1,
193 223 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
194 224 );
195 225 }
196 -
226 +
197 227 return $value;
198 228 }
199 -
229 +
200 230 /**
201 231 * Declare all parsers and must be [private] static methods
202 232 */
203 -
233 +
204 234 /**
205 235 * Parse loggedin user properties
206 236 *
207 237 * @param string $value
@@ -211,13 +241,13 @@
211 241 private static function parseUserProperties($value, $form = null)
212 242 {
213 243 if ($user = wp_get_current_user()) {
214 244 $prop = substr(str_replace(['{', '}'], '', $value), 5);
215 -
245 +
216 246 if (false !== strpos($prop, 'meta.')) {
217 247 $metaKey = substr($prop, strlen('meta.'));
218 248 $metaKey = sanitize_text_field($metaKey);
219 - if (empty($metaKey)) {
249 + if (empty($metaKey) || ShortCodeParser::isDeniedUserProperty($metaKey)) {
220 250 return '';
221 251 }
222 252 $userId = $user->ID;
223 253 $data = get_user_meta($userId, $metaKey, true);
@@ -226,15 +256,19 @@
226 256 return esc_html($data);
227 257 }
228 258 return esc_html(implode(',', $data));
229 259 }
230 -
260 +
261 + if (ShortCodeParser::isDeniedUserProperty($prop)) {
262 + return '';
263 + }
264 +
231 265 return esc_html($user->{$prop});
232 266 }
233 -
267 +
234 268 return '';
235 269 }
236 -
270 +
237 271 /**
238 272 * Parse embedded post properties
239 273 *
240 274 * @param string $value
@@ -246,15 +280,15 @@
246 280 global $post;
247 281 if (!$post) {
248 282 return '';
249 283 }
250 -
284 +
251 285 $key = $prop = substr(str_replace(['{', '}'], '', $value), 11);
252 -
286 +
253 287 if (false !== strpos($key, 'author.')) {
254 288 $authorProperty = substr($key, strlen('author.'));
255 289 $authorId = $post->post_author;
256 - if ($authorId) {
290 + if ($authorId && !ShortCodeParser::isDeniedUserProperty($authorProperty)) {
257 291 $data = get_the_author_meta($authorProperty, $authorId);
258 292 if (!is_array($data)) {
259 293 return esc_html($data);
260 294 }
@@ -278,19 +312,19 @@
278 312 }
279 313 return '';
280 314 }
281 315 }
282 -
316 +
283 317 if ('permalink' == $prop) {
284 318 return site_url(esc_attr(urldecode(wpFluentForm('request')->server('REQUEST_URI'))));
285 319 }
286 -
287 - if (property_exists($post, $prop)) {
320 +
321 + if ('post_password' !== $prop && property_exists($post, $prop)) {
288 322 return esc_html($post->{$prop});
289 323 }
290 324 return '';
291 325 }
292 -
326 +
293 327 /**
294 328 * Parse WP Properties
295 329 *
296 330 * @param string $value
@@ -310,12 +344,12 @@
310 344 }
311 345 if ('{http_referer}' == $value) {
312 346 return esc_url(wp_get_referer());
313 347 }
314 -
348 +
315 349 return '';
316 350 }
317 -
351 +
318 352 /**
319 353 * Parse browser/user-agent properties
320 354 *
321 355 * @param string $value
@@ -329,12 +363,12 @@
329 363 return esc_html($browser->getBrowser());
330 364 } elseif ('{browser.platform}' == $value) {
331 365 return esc_html($browser->getPlatform());
332 366 }
333 -
367 +
334 368 return '';
335 369 }
336 -
370 +
337 371 /**
338 372 * Parse ip shortcode
339 373 *
340 374 * @param string $value
@@ -346,9 +380,9 @@
346 380 $rawIp = wpFluentForm('request')->getIp();
347 381 $ip = sanitize_text_field($rawIp);
348 382 return $ip ? esc_html($ip) : $value;
349 383 }
350 -
384 +
351 385 /**
352 386 * Parse date shortcode
353 387 *
354 388 * @param string $value
@@ -360,9 +394,9 @@
360 394 $format = substr(str_replace(['}', '{'], '', $value), 5);
361 395 $date = date($format, strtotime(current_time('mysql')));
362 396 return $date ? esc_html($date) : '';
363 397 }
364 -
398 +
365 399 /**
366 400 * Parse request query param.
367 401 *
368 402 * @param string $value
@@ -374,20 +408,20 @@
374 408 {
375 409 $exploded = explode('.', $value);
376 410 $param = array_pop($exploded);
377 411 $value = wpFluentForm('request')->get($param);
378 -
412 +
379 413 if (!$value) {
380 414 return '';
381 415 }
382 -
416 +
383 417 if (is_array($value)) {
384 418 return sanitize_textarea_field(implode(', ', $value));
385 419 }
386 -
420 +
387 421 return sanitize_textarea_field($value);
388 422 }
389 -
423 +
390 424 /**
391 425 * Generate random a string with prefix
392 426 *
393 427 * @param $value
@@ -398,8 +432,8 @@
398 432 {
399 433 $exploded = explode('.', $value);
400 434 $prefix = array_pop($exploded);
401 435 $value = $prefix . uniqid();
402 -
436 +
403 437 return esc_html(apply_filters('fluentform/shortcode_parser_callback_random_string', $value, $prefix, new static()));
404 438 }
405 439 }