PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.14
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.14
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 3.6.65 All 196 releases
← All changes | app/Services/FormBuilder/ShortCodeParser.php +66 -23 6.2.106.2.14 View file →
@@ -10,8 +10,10 @@
10 10 use FluentForm\App\Helpers\Helper;
11 11
12 12 class ShortCodeParser
13 13 {
14 + const USER_SECRET_PROPERTIES = ['user_pass', 'user_activation_key', 'session_tokens', 'data'];
15 +
14 16 protected static $form = null;
15 17
16 18 protected static $entry = null;
17 19
@@ -95,9 +97,9 @@
95 97 'ff_will_return_html',
96 98 [
97 99 false,
98 100 $provider,
99 - $key
101 + $key,
100 102 ],
101 103 FLUENTFORM_FRAMEWORK_UPGRADE,
102 104 'fluentform/will_return_html',
103 105 'Use fluentform/will_return_html instead of ff_will_return_html.'
@@ -106,9 +108,9 @@
106 108 }
107 109 $parsable[$key] = static::parseShortCodeFromString($value, $isUrl, $isHtml, $htmlSanitized);
108 110 }
109 111 }
110 -
112 +
111 113 return $parsable;
112 114 }
113 115
114 116 protected static function parseShortCodeFromString($parsable, $isUrl = false, $isHtml = false, $htmlSanitized = false)
@@ -124,9 +126,9 @@
124 126 $value = '';
125 127 if (false !== strpos($matches[1], 'inputs.')) {
126 128 $formProperty = substr($matches[1], strlen('inputs.'));
127 129 $value = static::getFormData($formProperty, $isHtml);
128 - } else if (false !== strpos($matches[1], 'labels.')) {
130 + } elseif (false !== strpos($matches[1], 'labels.')) {
129 131 $formLabelProperty = substr($matches[1], strlen('labels.'));
130 132 $value = static::getFormLabelData($formLabelProperty);
131 133 } elseif (false !== strpos($matches[1], 'user.')) {
132 134 $userProperty = substr($matches[1], strlen('user.'));
@@ -141,17 +143,17 @@
141 143 $submissionProperty = substr($matches[1], strlen('submission.'));
142 144 $value = static::getSubmissionData($submissionProperty);
143 145 } elseif (false !== strpos($matches[1], 'cookie.')) {
144 146 $scookieProperty = substr($matches[1], strlen('cookie.'));
145 - $value = array_key_exists($scookieProperty, $_COOKIE) ? wp_unslash($_COOKIE[$scookieProperty]) : '';
147 + $value = array_key_exists($scookieProperty, $_COOKIE) ? sanitize_text_field(wp_unslash($_COOKIE[$scookieProperty])) : '';
146 148 } elseif (false !== strpos($matches[1], 'payment.')) {
147 149 $property = substr($matches[1], strlen('payment.'));
148 150 $deprecatedValue = apply_filters_deprecated(
149 151 'fluentform_payment_smartcode', [
150 - '',
151 - $property,
152 - self::getInstance()
153 - ],
152 + '',
153 + $property,
154 + self::getInstance(),
155 + ],
154 156 FLUENTFORM_FRAMEWORK_UPGRADE,
155 157 'fluentform/payment_smartcode',
156 158 'Use fluentform/payment_smartcode instead of fluentform_payment_smartcode.'
157 159 );
@@ -169,9 +171,9 @@
169 171 // Don't encode values that are already complete URLs like {wp.site_url}
170 172 if (!preg_match('#^https?://#i', (string) $value)) {
171 173 $value = rawurlencode($value);
172 174 }
173 - } else if ($htmlSanitized) {
175 + } elseif ($htmlSanitized) {
174 176 $value = fluentform_sanitize_html($value);
175 177 }
176 178
177 179 return $value;
@@ -226,9 +228,9 @@
226 228 [
227 229 $originalInput,
228 230 $field,
229 231 static::getForm()->id,
230 - $isHtml
232 + $isHtml,
231 233 ],
232 234 FLUENTFORM_FRAMEWORK_UPGRADE,
233 235 'fluentform/response_render_' . $field['element'],
234 236 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
@@ -247,9 +249,9 @@
247 249 [
248 250 static::$store['inputs'][$key],
249 251 $field,
250 252 static::getForm()->id,
251 - $isHtml
253 + $isHtml,
252 254 ],
253 255 FLUENTFORM_FRAMEWORK_UPGRADE,
254 256 'fluentform/response_render_' . $field['element'],
255 257 'Use fluentform/response_render_' . $field['element'] . ' instead of fluentform_response_render_' . $field['element']
@@ -280,9 +282,9 @@
280 282 $key = $currentFieldName;
281 283 }
282 284 $inputLabel = ArrayHelper::get(ArrayHelper::get(static::$formFields, $key, []), 'label', '');
283 285 $inputLabel = str_replace(['[', ']'], '', $inputLabel);
284 - $keys = explode(".", $key);
286 + $keys = explode('.', $key);
285 287 if (count($keys) > 1) {
286 288 $parentKey = array_shift($keys);
287 289 $inputLabel = str_replace($parentKey, '', $inputLabel);
288 290 }
@@ -301,9 +303,34 @@
301 303 {
302 304 if (is_null(static::$store['user'])) {
303 305 static::$store['user'] = wp_get_current_user();
304 306 }
305 - return static::$store['user']->{$key};
307 +
308 + $user = static::$store['user'];
309 +
310 + // SECURITY (FINDING-11): `$user->{$key}` reads straight from the wp_users row via
311 + // WP_User::__get, so an author-controlled {user.user_pass} (or {user.user_activation_key})
312 + // would exfiltrate the *submitting* user's password hash / reset token in a notification.
313 + // Allow only a fixed set of safe profile fields; resolve anything else from user meta,
314 + // which never contains the sensitive wp_users columns.
315 + $allowed = [
316 + 'ID', 'id', 'display_name', 'first_name', 'last_name', 'user_email',
317 + 'user_login', 'user_nicename', 'nickname', 'user_url', 'description', 'roles',
318 + 'user_registered', // non-sensitive wp_users column; keep {user.user_registered} working
319 + ];
320 + if (static::isDeniedUserProperty($key)) {
321 + return '';
322 + }
323 + if (in_array($key, $allowed, true)) {
324 + return $user->{$key};
325 + }
326 +
327 + $key = (string) $key;
328 + if ($user->ID && '' !== $key) {
329 + return get_user_meta($user->ID, $key, true);
330 + }
331 +
332 + return '';
306 333 }
307 334
308 335 protected static function getPostData($key)
309 336 {
@@ -318,9 +345,9 @@
318 345
319 346 if (false !== strpos($key, 'author.')) {
320 347 $authorProperty = substr($key, strlen('author.'));
321 348 $authorId = static::$store['post']->post_author;
322 - if ($authorId) {
349 + if ($authorId && !static::isDeniedUserProperty($authorProperty)) {
323 350 $data = get_the_author_meta($authorProperty, $authorId);
324 351 if (!is_array($data)) {
325 352 return $data;
326 353 }
@@ -345,11 +372,30 @@
345 372 return '';
346 373 }
347 374 }
348 375
376 + if ('post_password' === $key) {
377 + return '';
378 + }
379 +
349 380 return static::$store['post']->{$key};
350 381 }
351 382
383 + // Shared by {user.*} and {embed_post.author.*} in both parsers. get_the_author_meta() and
384 + // WP_User fall through to any user meta, where plugins keep 2FA secrets and tokens under
385 + // protected (underscore) keys, so the secret columns alone are not enough to deny.
386 + public static function isDeniedUserProperty($property)
387 + {
388 + // Same aliases get_the_author_meta() accepts: 'pass' means user_pass
389 + if (in_array($property, ['login', 'pass', 'nicename', 'email', 'url', 'registered', 'activation_key', 'status'], true)) {
390 + $property = 'user_' . $property;
391 + }
392 +
393 + $denied = (array) apply_filters('fluentform/smartcode_user_denied_properties', self::USER_SECRET_PROPERTIES);
394 +
395 + return in_array($property, $denied, true) || is_protected_meta($property, 'user');
396 + }
397 +
352 398 protected static function getWPData($key)
353 399 {
354 400 if ('admin_email' == $key) {
355 401 return get_option('admin_email');
@@ -420,9 +466,9 @@
420 466
421 467 $status = apply_filters_deprecated(
422 468 'fluentform_all_data_skip_password_field',
423 469 [
424 - __return_true()
470 + __return_true(),
425 471 ],
426 472 FLUENTFORM_FRAMEWORK_UPGRADE,
427 473 'fluentform/all_data_skip_password_field',
428 474 'Use fluentform/all_data_skip_password_field instead of fluentform_all_data_skip_password_field.'
@@ -440,9 +486,9 @@
440 486 $hideHiddenField = true;
441 487 $hideHiddenField = apply_filters_deprecated(
442 488 'fluentform_all_data_without_hidden_fields',
443 489 [
444 - $hideHiddenField
490 + $hideHiddenField,
445 491 ],
446 492 FLUENTFORM_FRAMEWORK_UPGRADE,
447 493 'fluentform/all_data_without_hidden_fields',
448 494 'Use fluentform/all_data_without_hidden_fields instead of fluentform_all_data_without_hidden_fields.'
@@ -475,9 +521,9 @@
475 521 [
476 522 $html,
477 523 $formFields,
478 524 $inputLabels,
479 - $response
525 + $response,
480 526 ],
481 527 FLUENTFORM_FRAMEWORK_UPGRADE,
482 528 'fluentform/all_data_shortcode_html',
483 529 'Use fluentform/all_data_shortcode_html instead of fluentform_all_data_shortcode_html.'
@@ -488,9 +534,9 @@
488 534 } elseif (0 === strpos($key, 'pdf.download_link.')) {
489 535 $key = apply_filters_deprecated(
490 536 'fluentform_shortcode_parser_callback_pdf.download_link.public',
491 537 [
492 - $key, static::getInstance()
538 + $key, static::getInstance(),
493 539 ],
494 540 FLUENTFORM_FRAMEWORK_UPGRADE,
495 541 'fluentform/shortcode_parser_callback_pdf.download_link.public',
496 542 'Use fluentform/shortcode_parser_callback_pdf.download_link.public instead of fluentform_shortcode_parser_callback_pdf.download_link.public.'
@@ -528,15 +574,13 @@
528 574 }
529 575 return '';
530 576 }
531 577
532 -
533 578 // if it's multi line then just return
534 579 if (false !== strpos($key, PHP_EOL)) { // most probably it's a css
535 580 return '{' . $key . '}';
536 581 }
537 582
538 -
539 583 $groups = explode('.', $key);
540 584 if (count($groups) > 1) {
541 585 $group = array_shift($groups);
542 586 $property = implode('.', $groups);
@@ -543,9 +587,9 @@
543 587 $handlerValue = apply_filters_deprecated(
544 588 'fluentform_smartcode_group_' . $group,
545 589 [
546 590 $property,
547 - static::getInstance()
591 + static::getInstance(),
548 592 ],
549 593 FLUENTFORM_FRAMEWORK_UPGRADE,
550 594 'fluentform/smartcode_group_' . $group,
551 595 'Use fluentform/smartcode_group_' . $group . ' instead of fluentform_smartcode_group_' . $group
@@ -561,15 +605,14 @@
561 605 $handlerValue = apply_filters_deprecated(
562 606 'fluentform_shortcode_parser_callback_' . $key,
563 607 [
564 608 '{' . $key . '}',
565 - static::getInstance()
609 + static::getInstance(),
566 610 ],
567 611 FLUENTFORM_FRAMEWORK_UPGRADE,
568 612 'fluentform/shortcode_parser_callback_' . $key,
569 613 'Use fluentform/shortcode_parser_callback_' . $key . ' instead of fluentform_shortcode_parser_callback_' . $key
570 614 );
571 -
572 615
573 616 $handlerValue = apply_filters('fluentform/shortcode_parser_callback_' . $key, $handlerValue, static::getInstance());
574 617
575 618 if ($handlerValue) {