| @@ -9,9 +9,9 @@ | ||
| 9 | 9 | * Version of the database we are moving to. |
| 10 | 10 | * |
| 11 | 11 | * @var int |
| 12 | 12 | */ |
| 13 | - public static $db_version = 105; | |
| 13 | + public static $db_version = 106; | |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | 16 | * Used by the API add-on. |
| 17 | 17 | * |
| @@ -28,9 +28,9 @@ | ||
| 28 | 28 | * @since 2.0 |
| 29 | 29 | * |
| 30 | 30 | * @var string |
| 31 | 31 | */ |
| 32 | - public static $plug_version = '6.32.1'; | |
| 32 | + public static $plug_version = '6.35'; | |
| 33 | 33 | |
| 34 | 34 | /** |
| 35 | 35 | * @var bool |
| 36 | 36 | */ |
| @@ -937,8 +937,9 @@ | ||
| 937 | 937 | } else { |
| 938 | 938 | self::sanitize_value( self::class . '::strip_most_html', $value ); |
| 939 | 939 | } |
| 940 | 940 | self::decode_specialchars( $value ); |
| 941 | + self::sanitize_value( 'FrmHtmlSanitizer::sanitize_url_attributes', $value ); | |
| 941 | 942 | } |
| 942 | 943 | |
| 943 | 944 | /** |
| 944 | 945 | * Allow only a small set of very basic HTML for unprivileged users. |
| @@ -1368,10 +1369,9 @@ | ||
| 1368 | 1369 | if ( isset( $atts['echo'] ) ) { |
| 1369 | 1370 | unset( $atts['echo'] ); |
| 1370 | 1371 | } |
| 1371 | 1372 | |
| 1372 | - $html_atts = self::array_to_html_params( $atts ); | |
| 1373 | - $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) ); | |
| 1373 | + $icon = trim( str_replace( array( 'frm_icon_font', 'frmfont ' ), '', $class ) ); | |
| 1374 | 1374 | |
| 1375 | 1375 | // Replace icons that have been removed or renamed. |
| 1376 | 1376 | $deprecated = array( |
| 1377 | 1377 | 'frm_clone_solid_icon' => 'frm_clone_icon', |
| @@ -1383,11 +1383,11 @@ | ||
| 1383 | 1383 | $icon = $deprecated[ $icon ]; |
| 1384 | 1384 | $class = str_replace( $icon, $deprecated[ $icon ], $class ); |
| 1385 | 1385 | } |
| 1386 | 1386 | |
| 1387 | - if ( $icon === $class ) { | |
| 1388 | - $icon = '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>'; | |
| 1389 | - } else { | |
| 1387 | + $is_font_icon = $icon === $class; | |
| 1388 | + | |
| 1389 | + if ( ! $is_font_icon ) { | |
| 1390 | 1390 | $class = str_contains( $icon, ' ' ) ? ' ' . $icon : ''; |
| 1391 | 1391 | |
| 1392 | 1392 | if ( str_contains( $icon, ' ' ) ) { |
| 1393 | 1393 | $icon = explode( ' ', $icon ); |
| @@ -1392,18 +1392,46 @@ | ||
| 1392 | 1392 | if ( str_contains( $icon, ' ' ) ) { |
| 1393 | 1393 | $icon = explode( ' ', $icon ); |
| 1394 | 1394 | $icon = reset( $icon ); |
| 1395 | 1395 | } |
| 1396 | + } | |
| 1396 | 1397 | |
| 1397 | - $icon = '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>'; | |
| 1398 | - } | |
| 1398 | + if ( $atts ) { | |
| 1399 | + // A caller passed attributes, so kses still has to decide which of them survive. Its | |
| 1400 | + // allowlist comes from safe_html() through the frm_striphtml_allowed_tags filter, and | |
| 1401 | + // add-ons widen it around their own icons, so there is no fixed list to check against. | |
| 1402 | + $html_atts = self::array_to_html_params( $atts ); | |
| 1403 | + $markup = $is_font_icon | |
| 1404 | + ? '<i class="' . esc_attr( $class ) . '"' . $html_atts . '></i>' | |
| 1405 | + : '<svg class="frmsvg' . esc_attr( $class ) . '"' . $html_atts . '><use href="#' . esc_attr( $icon ) . '" /></svg>'; | |
| 1399 | 1406 | |
| 1400 | - if ( $echo ) { | |
| 1401 | - echo self::kses_icon( $icon ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 1407 | + if ( ! $echo ) { | |
| 1408 | + return $markup; | |
| 1409 | + } | |
| 1410 | + | |
| 1411 | + echo self::kses_icon( $markup ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 1402 | 1412 | return null; |
| 1403 | 1413 | } |
| 1404 | 1414 | |
| 1405 | - return $icon; | |
| 1415 | + /** | |
| 1416 | + * With no attributes from the caller, the tag is nothing but this method's own markup | |
| 1417 | + * around an escaped class and icon id, so there is nothing left for kses to decide and it | |
| 1418 | + * can be skipped. Echoing the pieces rather than a finished string keeps that safe to the | |
| 1419 | + * escaping sniff without an annotation. | |
| 1420 | + * | |
| 1421 | + * This is the path the form builder takes for most of the tens of thousands of icons it | |
| 1422 | + * renders on a large form, and the kses pass was most of what each one cost. | |
| 1423 | + */ | |
| 1424 | + $callback = function () use ( $is_font_icon, $class, $icon ) { | |
| 1425 | + if ( $is_font_icon ) { | |
| 1426 | + echo '<i class="' . esc_attr( $class ) . '"></i>'; | |
| 1427 | + return; | |
| 1428 | + } | |
| 1429 | + | |
| 1430 | + echo '<svg class="frmsvg' . esc_attr( $class ) . '"><use href="#' . esc_attr( $icon ) . '" /></svg>'; | |
| 1431 | + }; | |
| 1432 | + | |
| 1433 | + return self::clip( $callback, $echo ); | |
| 1406 | 1434 | } |
| 1407 | 1435 | |
| 1408 | 1436 | /** |
| 1409 | 1437 | * Run kses for icons. It needs to add a few filters first in order to preserve some custom style values. |
| @@ -1630,9 +1658,9 @@ | ||
| 1630 | 1658 | |
| 1631 | 1659 | printf( |
| 1632 | 1660 | /* translators: %1$s: Start link HTML, %2$s: CTA text ("upgrading to PRO" by default), %3$s: End link HTML */ |
| 1633 | 1661 | esc_html__( 'You\'re using Formidable Forms Lite. To unlock more features consider %1$s%2$s%3$s.', 'formidable' ), |
| 1634 | - '<a href="' . esc_url( $upgrade_link ) . '">', | |
| 1662 | + '<a href="' . esc_url( $upgrade_link ) . '" target="_blank" rel="noopener">', | |
| 1635 | 1663 | esc_html( $cta_text ), |
| 1636 | 1664 | '</a>' |
| 1637 | 1665 | ); |
| 1638 | 1666 | ?> |