Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/beaver-builder-lite-version/classes/class-fl-builder-compatibility.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-compatibility.php
@@ -626,6 +626,10 @@
* @since 1.10.7
*/
public static function render_module_content_filter( $contents, $module ) {
+ if ( 'box' === $module->slug ) {
+ return $contents;
+ }
+
$postdata = FLBuilderModel::get_post_data();
if ( isset( $_GET['safemode'] ) && FLBuilderModel::is_builder_active() || ( isset( $postdata['safemode'] ) && 'true' === $postdata['safemode'] ) ) {
return sprintf( '<h3>[%1$s] %2$s %3$s</h3>', __( 'SAFEMODE', 'fl-builder' ), $module->name, __( 'module', 'fl-builder' ) );
--- a/beaver-builder-lite-version/classes/class-fl-builder-debug.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-debug.php
@@ -457,7 +457,7 @@
);
self::register( 'bb', $args );
- $info = get_option( '_fl_builder_update_info', array() );
+ $info = get_site_option( '_fl_builder_update_info', array() );
$from = '';
if ( isset( $info['from'] ) && ! empty( $info['from'] ) ) {
$from = ' - Previous ' . $info['from'];
--- a/beaver-builder-lite-version/classes/class-fl-builder-loader.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-loader.php
@@ -48,7 +48,7 @@
* @return void
*/
static private function define_constants() {
- define( 'FL_BUILDER_VERSION', '2.10.2.2' );
+ define( 'FL_BUILDER_VERSION', '2.10.3.2' );
define( 'FL_BUILDER_FILE', trailingslashit( dirname( __DIR__, 1 ) ) . 'fl-builder.php' );
define( 'FL_BUILDER_DIR', plugin_dir_path( FL_BUILDER_FILE ) );
define( 'FL_BUILDER_URL', esc_url( plugins_url( '/', FL_BUILDER_FILE ) ) );
--- a/beaver-builder-lite-version/classes/class-fl-builder-loop.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-loop.php
@@ -636,8 +636,14 @@
$has_archive = is_string( $args->has_archive ) ? $args->has_archive : false;
$is_single = false;
+ // Build the full page path for multi-segment archive slug comparison
+ $full_page_path = $custom_paged['current_page'];
+ if ( ! empty( $custom_paged['parent_page'] ) ) {
+ $full_page_path = $custom_paged['parent_page'] . '/' . $custom_paged['current_page'];
+ }
+
// Check if it's a CPT archive or CPT single.
- if ( $custom_paged['current_page'] != $post_type && $has_archive != $custom_paged['current_page'] ) {
+ if ( $custom_paged['current_page'] != $post_type && $has_archive != $custom_paged['current_page'] && $has_archive != $full_page_path ) {
// Is a child post of the current post type?
$post_object = get_page_by_path( $custom_paged['current_page'], OBJECT, $post_type );
@@ -651,7 +657,7 @@
$slug = $args->rewrite['slug'];
- if ( is_string( $args->has_archive ) ) {
+ if ( is_string( $args->has_archive ) && ! $is_single ) {
$slug = $args->has_archive;
}
@@ -828,8 +834,13 @@
if ( is_array( $wp_the_query->query ) ) {
foreach ( $wp_the_query->query as $key => $value ) {
- if ( strpos( $key, 'flpaged' ) === 0 && is_page() && get_option( 'page_on_front' ) ) {
- return false;
+ if ( strpos( $key, 'flpaged' ) === 0 ) {
+ if ( is_page() && get_option( 'page_on_front' ) ) {
+ return false;
+ }
+ if ( is_post_type_archive() || is_archive() || is_home() ) {
+ return false;
+ }
}
}
--- a/beaver-builder-lite-version/classes/class-fl-builder-model.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-model.php
@@ -1628,12 +1628,29 @@
}
}
+ $is_preview = $node_preview_id && $node_preview_id == $node->node;
+
+ // Node preview is only ever sent by the builder's own live preview
+ // request (action `render_layout`), which already requires a
+ // logged in user with edit_post capability on the target post
+ // before reaching this point (see FLBuilderAJAX::call_action()).
+ // Re-check the same guarantee here so any other caller that
+ // resolves node settings (e.g. a module's own front-end ajax
+ // action) can't honor an attacker-supplied node_preview override.
+ if ( $is_preview && ( ! is_user_logged_in() || ! current_user_can( 'edit_post', self::get_post_id() ) ) ) {
+ $is_preview = false;
+ }
+
// Get either the preview settings or saved node settings merged with the defaults.
- if ( $node_preview_id && $node_preview_id == $node->node ) {
+ if ( $is_preview ) {
if ( ! isset( $post_data['node_preview_processed_settings'] ) ) {
$settings = $post_data['node_preview'];
+ // Preview settings are client-supplied and rendered as-is, so
+ // gate JS code fields the same way the save path does.
+ $settings = (array) self::strip_client_js_code_overrides( (object) $settings, isset( $node->settings->type ) ? $node->settings->type : '', $node->settings );
+
if ( isset( $settings['dynamic_node_settings'] ) ) {
$settings = FLBuilderDynamicGlobal::merge_settings_for_save( $node, (object) $settings );
}
@@ -2232,8 +2249,10 @@
// Apply settings that were passed if we have them.
if ( $settings && $settings_id ) {
if ( $settings_id === $row->node ) {
+ $settings = self::strip_client_js_code_overrides( (object) $settings, isset( $row->settings->type ) ? $row->settings->type : '', $row->settings );
$layout_data[ $new_row_id ]->settings = (object) array_merge( (array) $row->settings, (array) $settings );
} elseif ( isset( $new_nodes[ $settings_id ] ) ) {
+ $settings = self::strip_client_js_code_overrides( (object) $settings, isset( $new_nodes[ $settings_id ]->settings->type ) ? $new_nodes[ $settings_id ]->settings->type : '', $new_nodes[ $settings_id ]->settings );
$new_nodes[ $settings_id ]->settings = (object) array_merge( (array) $new_nodes[ $settings_id ]->settings, (array) $settings );
}
}
@@ -3132,8 +3151,10 @@
// Apply settings that were passed if we have them.
if ( $settings && $settings_id ) {
if ( $settings_id === $col->node ) {
+ $settings = self::strip_client_js_code_overrides( (object) $settings, isset( $col->settings->type ) ? $col->settings->type : '', $col->settings );
$layout_data[ $new_col_id ]->settings = (object) array_merge( (array) $col->settings, (array) $settings );
} elseif ( isset( $new_nodes[ $settings_id ] ) ) {
+ $settings = self::strip_client_js_code_overrides( (object) $settings, isset( $new_nodes[ $settings_id ]->settings->type ) ? $new_nodes[ $settings_id ]->settings->type : '', $new_nodes[ $settings_id ]->settings );
$new_nodes[ $settings_id ]->settings = (object) array_merge( (array) $new_nodes[ $settings_id ]->settings, (array) $settings );
}
}
@@ -3414,7 +3435,8 @@
// Merge form settings passed from the frontend
if ( $form_settings ) {
- $settings = (object) array_merge( (array) $settings, $form_settings );
+ $form_settings = (array) self::strip_client_js_code_overrides( (object) $form_settings, isset( $settings->type ) ? $settings->type : '', $settings );
+ $settings = (object) array_merge( (array) $settings, $form_settings );
}
// Merge alias settings
@@ -4129,6 +4151,7 @@
if ( ! $module ) {
return false;
} elseif ( $settings ) {
+ $settings = self::strip_client_js_code_overrides( (object) $settings, isset( $module->settings->type ) ? $module->settings->type : '', $module->settings );
$module->settings = (object) array_merge( (array) $module->settings, (array) $settings );
}
@@ -4649,6 +4672,229 @@
}
/**
+ * JS code field plan per module type: which top-level fields and which
+ * repeater sub-fields carry a JavaScript code editor. Memoized so the copy
+ * hot path does not re-walk settings forms for every node.
+ *
+ * @var array
+ */
+ static private $js_code_field_plan = array();
+
+ /**
+ * Node-level JavaScript code fields that apply to any node type.
+ *
+ * These are only added to the settings form for privileged users
+ * (FLBuilderNodeCodeSettings::filter_settings_fields), so the form walk
+ * below cannot discover them for the users who need gating. They are
+ * stripped by name instead.
+ *
+ * @var array
+ */
+ static private $node_js_code_fields = array( 'bb_js_code' );
+
+ /**
+ * Removes JavaScript code field values from a client-supplied settings
+ * object for users who lack the unfiltered_html capability.
+ *
+ * Code fields with a JavaScript editor are echoed raw into the generated
+ * layout JS, so an unprivileged user who can set one can inject arbitrary
+ * script. This strips those keys from the incoming client settings before
+ * they are merged, at every entry point that accepts node settings (save,
+ * copy, alias, preview), so the stored/source value is kept instead.
+ *
+ * Node-level code fields are stripped for every node type, rows and
+ * columns included. Module form fields are stripped from the module's
+ * registered form, one level into form repeaters.
+ *
+ * Top-level keys are removed outright so the merge that follows keeps the
+ * stored value. Repeater sub-fields cannot rely on that: the merge replaces
+ * the whole repeater key with the client array, so the stored per-item
+ * value is written back onto each item instead, matched by item key. Items
+ * with no stored counterpart (newly added ones) have the field removed.
+ *
+ * Privileged users are unaffected. Forms without JS code fields are
+ * returned unchanged. The incoming object is never mutated.
+ *
+ * @since 2.10.2.4
+ * @param object $settings The incoming client settings object.
+ * @param string $node_type The type of the node being written (module type).
+ * @param object $stored The settings the client settings will be merged
+ * into, used to restore repeater sub-field values.
+ * @return object The settings object with disallowed JS code fields removed.
+ */
+ static public function strip_client_js_code_overrides( $settings, $node_type, $stored = null ) {
+ if ( FLBuilderModel::user_has_unfiltered_html() ) {
+ return $settings;
+ }
+ if ( ! is_object( $settings ) ) {
+ return $settings;
+ }
+
+ $settings = clone $settings;
+
+ foreach ( self::$node_js_code_fields as $name ) {
+ unset( $settings->$name );
+ }
+
+ if ( empty( $node_type ) || ! isset( self::$modules[ $node_type ] ) ) {
+ return $settings;
+ }
+
+ $plan = self::get_js_code_field_plan( $node_type );
+
+ foreach ( $plan['top'] as $name ) {
+ unset( $settings->$name );
+ }
+ foreach ( $plan['repeaters'] as $name => $sub_names ) {
+ if ( isset( $settings->$name ) ) {
+ $stored_items = self::get_container_value( $stored, $name );
+ $settings->$name = self::strip_js_fields_from_items( $settings->$name, $sub_names, $stored_items );
+ }
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Builds (and memoizes) the JS code field plan for a module type.
+ *
+ * @param string $node_type The module type.
+ * @return array The plan: 'top' field names and 'repeaters' sub-field names.
+ */
+ static private function get_js_code_field_plan( $node_type ) {
+ if ( isset( self::$js_code_field_plan[ $node_type ] ) ) {
+ return self::$js_code_field_plan[ $node_type ];
+ }
+
+ $plan = array(
+ 'top' => array(),
+ 'repeaters' => array(),
+ );
+
+ foreach ( self::get_settings_form_fields( $node_type, 'module' ) as $name => $field ) {
+ if ( self::is_js_code_field( $field ) ) {
+ $plan['top'][] = $name;
+ } elseif ( isset( $field['type'], $field['form'] ) && 'form' === $field['type'] ) {
+ $sub_names = self::get_js_code_field_names( $field['form'] );
+ if ( ! empty( $sub_names ) ) {
+ $plan['repeaters'][ $name ] = $sub_names;
+ }
+ }
+ }
+
+ self::$js_code_field_plan[ $node_type ] = $plan;
+ return $plan;
+ }
+
+ /**
+ * Returns the names of JavaScript code fields in a registered settings form.
+ *
+ * @param string $form The registered form id.
+ * @return array The JS code field names.
+ */
+ static private function get_js_code_field_names( $form ) {
+ $names = array();
+ foreach ( self::get_settings_form_fields( $form, 'general' ) as $sub_name => $sub_field ) {
+ if ( self::is_js_code_field( $sub_field ) ) {
+ $names[] = $sub_name;
+ }
+ }
+ return $names;
+ }
+
+ /**
+ * Replaces the given field names on every item of a client repeater value
+ * with the value stored on the matching item, removing the field when there
+ * is nothing stored to restore.
+ *
+ * Handles both array- and object-shaped containers and items: the AJAX
+ * decode path (json_decode( ..., true )) yields associative arrays, while
+ * some payloads and internal callers pass objects. Neither the incoming
+ * value nor the stored value is mutated.
+ *
+ * @param array|object $items The client repeater value.
+ * @param array $field_names Field names to restore or remove.
+ * @param array|object $stored_items The stored repeater value, if any.
+ * @return array|object The repeater value with the fields gated.
+ */
+ static private function strip_js_fields_from_items( $items, $field_names, $stored_items = null ) {
+ if ( ! is_array( $items ) && ! is_object( $items ) ) {
+ return $items;
+ }
+ $items = is_object( $items ) ? clone $items : $items;
+
+ foreach ( $items as $key => $item ) {
+ $stored_item = self::get_container_value( $stored_items, $key );
+ $item = is_object( $item ) ? clone $item : $item;
+
+ foreach ( $field_names as $field_name ) {
+ $item = self::set_container_value( $item, $field_name, self::get_container_value( $stored_item, $field_name ) );
+ }
+
+ if ( is_array( $items ) ) {
+ $items[ $key ] = $item;
+ } else {
+ $items->$key = $item;
+ }
+ }
+ return $items;
+ }
+
+ /**
+ * Reads a key from an array- or object-shaped container.
+ *
+ * @param array|object $container The container to read from.
+ * @param string|int $key The key to read.
+ * @return mixed The value, or null when the container or key is absent.
+ */
+ static private function get_container_value( $container, $key ) {
+ if ( is_array( $container ) ) {
+ return array_key_exists( $key, $container ) ? $container[ $key ] : null;
+ }
+ if ( is_object( $container ) ) {
+ return isset( $container->$key ) ? $container->$key : null;
+ }
+ return null;
+ }
+
+ /**
+ * Writes a key on an array- or object-shaped container, removing the key
+ * instead when the value is null.
+ *
+ * @param array|object $container The container to write to.
+ * @param string|int $key The key to write.
+ * @param mixed $value The value, or null to remove the key.
+ * @return array|object The updated container.
+ */
+ static private function set_container_value( $container, $key, $value ) {
+ if ( is_array( $container ) ) {
+ if ( null === $value ) {
+ unset( $container[ $key ] );
+ } else {
+ $container[ $key ] = $value;
+ }
+ } elseif ( is_object( $container ) ) {
+ if ( null === $value ) {
+ unset( $container->$key );
+ } else {
+ $container->$key = $value;
+ }
+ }
+ return $container;
+ }
+
+ /**
+ * Whether a settings form field is a code field using the JavaScript editor.
+ *
+ * @since 2.10.2.4
+ * @param array $field A settings form field definition.
+ * @return bool
+ */
+ static private function is_js_code_field( $field ) {
+ return isset( $field['type'], $field['editor'] ) && 'code' === $field['type'] && 'javascript' === $field['editor'];
+ }
+
+ /**
* Save the settings for a node.
*
* @since 1.0
@@ -4678,6 +4924,10 @@
);
}
+ // Prevent users without unfiltered_html from introducing raw JS via
+ // code fields (e.g. the Button module's Button Code setting).
+ $settings = self::strip_client_js_code_overrides( $settings, isset( $node->settings->type ) ? $node->settings->type : '', $node->settings );
+
// Merge the new settings.
if ( $is_dynamic_global ) {
$new_settings = FLBuilderDynamicGlobal::merge_settings_for_save( $node, $settings );
@@ -4691,12 +4941,10 @@
$new_settings->dynamic_fields = (object) $new_settings->dynamic_fields;
}
- /**
- * Remove any js setting for users with no unfiltered role
- */
- if ( ! FLBuilderModel::user_has_unfiltered_html() ) {
- unset( $new_settings->bb_js_code );
- }
+ // bb_js_code is gated by strip_client_js_code_overrides() above, which
+ // drops it from the incoming client settings rather than from the merged
+ // object, so an existing admin-authored value survives an edit by a
+ // lower-privileged user.
// Save the settings to the node.
$data = self::get_layout_data();
--- a/beaver-builder-lite-version/classes/class-fl-builder-module-blocks.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-module-blocks.php
@@ -63,6 +63,16 @@
}
/**
+ * Check if we are in the Widgets editor.
+ *
+ * @return bool
+ */
+ static public function is_widgets_editor() {
+ global $pagenow;
+ return 'widgets.php' === $pagenow;
+ }
+
+ /**
* Checks if module blocks should load.
*
* @return bool
@@ -70,6 +80,11 @@
static public function should_load() {
global $wp_version;
+ // Module blocks are not supported in Widgets editor
+ if ( self::is_widgets_editor() ) {
+ return false;
+ }
+
$enabled = self::get_enabled_block_editor_modules();
if ( empty( $enabled ) ) {
--- a/beaver-builder-lite-version/classes/class-fl-builder-module.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-module.php
@@ -115,6 +115,17 @@
public $partial_refresh = false;
/**
+ * Whether shortcodes in this module's rendered output should be processed
+ * by the layout's do_shortcode pass. Modules that render third-party content
+ * (widgets, blocks) can echo untrusted user data and set this to false so
+ * that data cannot inject shortcodes.
+ *
+ * @since 2.10.2.4
+ * @var boolean $renders_shortcodes
+ */
+ public $renders_shortcodes = true;
+
+ /**
* The module settings object.
*
* @since 1.0
@@ -252,20 +263,21 @@
* @since 1.0
*/
public function __construct( $params ) {
- $class_info = new ReflectionClass( $this );
- $class_path = $class_info->getFileName();
- $dir_path = dirname( $class_path );
- $this->slug = isset( $params['slug'] ) ? $params['slug'] : basename( $class_path, '.php' );
- $this->enabled = isset( $params['enabled'] ) ? $params['enabled'] : true;
- $this->editor_export = isset( $params['editor_export'] ) ? $params['editor_export'] : true;
- $this->partial_refresh = isset( $params['partial_refresh'] ) ? $params['partial_refresh'] : false;
- $this->include_wrapper = isset( $params['include_wrapper'] ) ? $params['include_wrapper'] : true;
- $this->element_setting = isset( $params['element_setting'] ) ? $params['element_setting'] : true;
- $this->accepts = isset( $params['accepts'] ) ? $params['accepts'] : [];
- $this->parents = isset( $params['parents'] ) ? $params['parents'] : 'all';
- $this->template = isset( $params['template'] ) ? $params['template'] : [];
- $this->block_editor = isset( $params['block_editor'] ) ? $params['block_editor'] : false;
- $this->auto_style = isset( $params['auto_style'] ) ? $params['auto_style'] : false;
+ $class_info = new ReflectionClass( $this );
+ $class_path = $class_info->getFileName();
+ $dir_path = dirname( $class_path );
+ $this->slug = isset( $params['slug'] ) ? $params['slug'] : basename( $class_path, '.php' );
+ $this->enabled = isset( $params['enabled'] ) ? $params['enabled'] : true;
+ $this->editor_export = isset( $params['editor_export'] ) ? $params['editor_export'] : true;
+ $this->partial_refresh = isset( $params['partial_refresh'] ) ? $params['partial_refresh'] : false;
+ $this->renders_shortcodes = isset( $params['renders_shortcodes'] ) ? $params['renders_shortcodes'] : true;
+ $this->include_wrapper = isset( $params['include_wrapper'] ) ? $params['include_wrapper'] : true;
+ $this->element_setting = isset( $params['element_setting'] ) ? $params['element_setting'] : true;
+ $this->accepts = isset( $params['accepts'] ) ? $params['accepts'] : [];
+ $this->parents = isset( $params['parents'] ) ? $params['parents'] : 'all';
+ $this->template = isset( $params['template'] ) ? $params['template'] : [];
+ $this->block_editor = isset( $params['block_editor'] ) ? $params['block_editor'] : false;
+ $this->auto_style = isset( $params['auto_style'] ) ? $params['auto_style'] : false;
// We need to normalize the paths here since path comparisons
// break on Windows because they use backslashes.
--- a/beaver-builder-lite-version/classes/class-fl-builder-photo.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-photo.php
@@ -50,7 +50,21 @@
* @return object
*/
static public function get_attachment_data( $id ) {
- $data = wp_prepare_attachment_for_js( $id );
+ $attachment = get_post( $id );
+
+ // wp_prepare_attachment_for_js() checks current_user_can('read_post', $post_parent)
+ // which triggers a PHP notice if the parent's post type is not registered (e.g. after
+ // a plugin that registered the post type has been deactivated). Clear the parent
+ // temporarily to avoid this.
+ if ( $attachment && $attachment->post_parent ) {
+ $parent = get_post( $attachment->post_parent );
+ if ( $parent && ! get_post_type_object( $parent->post_type ) ) {
+ $attachment = clone $attachment;
+ $attachment->post_parent = 0;
+ }
+ }
+
+ $data = wp_prepare_attachment_for_js( $attachment ? $attachment : $id );
if ( gettype( $data ) == 'array' ) {
return json_decode( json_encode( $data ) );
--- a/beaver-builder-lite-version/classes/class-fl-builder-ui-settings-forms.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder-ui-settings-forms.php
@@ -383,6 +383,7 @@
'tabs' => $module->form,
'assets' => array(
'css' => $css,
+ 'cssurl' => $css_file_uri,
'js' => $js,
'jsurl' => $js_file_uri,
),
@@ -994,6 +995,10 @@
*/
static public function render_settings_field( $name, $field, $settings = null, $data = null ) {
+ // Normalize $settings to an object before the filter so third-party callbacks
+ // using property_exists()/->prop don't fatal on PHP 8+ when $settings is null.
+ $settings = ! $settings ? new stdClass() : $settings;
+
/**
* Use this filter to modify the config array for a field before it is rendered.
* @see fl_builder_render_settings_field
@@ -1009,7 +1014,6 @@
$i = null;
$is_multiple = isset( $field['multiple'] ) && true === (bool) $field['multiple'];
$supports_multiple = 'editor' != $field['type'] && 'service' != $field['type'];
- $settings = ! $settings ? new stdClass() : $settings;
$preview = isset( $field['preview'] ) ? json_encode( $field['preview'] ) : json_encode( array(
'type' => 'refresh',
) );
--- a/beaver-builder-lite-version/classes/class-fl-builder.php
+++ b/beaver-builder-lite-version/classes/class-fl-builder.php
@@ -105,6 +105,8 @@
add_filter( 'wp_handle_upload_prefilter', __CLASS__ . '::wp_handle_upload_prefilter_filter' );
add_filter( 'wp_link_query_args', __CLASS__ . '::wp_link_query_args_filter' );
add_filter( 'fl_builder_load_modules_paths', __CLASS__ . '::load_module_paths', 9999 );
+ add_filter( 'fl_builder_render_module_content', __CLASS__ . '::escape_foreign_module_shortcodes', 10, 2 );
+ add_filter( 'fl_builder_render_module_html_content', __CLASS__ . '::escape_foreign_module_html_shortcodes', 10, 4 );
}
/**
@@ -2106,6 +2108,67 @@
}
/**
+ * Entity-encodes the brackets of any registered shortcode found in a string
+ * so the layout's do_shortcode passes cannot execute it.
+ *
+ * Modules that render third-party content (widgets, blocks) can echo
+ * untrusted user data — a comment author name, for example — that happens to
+ * contain shortcode syntax. Because BB runs do_shortcode over the assembled
+ * layout, that syntax would otherwise execute. Encoding only the brackets of
+ * matches against the registered-shortcode regex neutralizes them while
+ * leaving unrelated brackets (inline JS, JSON) untouched, and renders the
+ * text literally, exactly as it appears outside a builder layout.
+ *
+ * @since 2.10.2.4
+ * @param string $content The rendered module output to sanitize.
+ * @return string
+ */
+ static public function escape_foreign_shortcodes( $content ) {
+ if ( '' === $content || false === strpos( $content, '[' ) ) {
+ return $content;
+ }
+ return preg_replace_callback(
+ '/' . get_shortcode_regex() . '/s',
+ function ( $matches ) {
+ return str_replace( array( '[', ']' ), array( '[', ']' ), $matches[0] );
+ },
+ $content
+ );
+ }
+
+ /**
+ * Neutralizes shortcodes in a module's rendered output when the module does
+ * not own its content (renders_shortcodes = false). Hooked to both module
+ * content filters so it covers the live-page and AJAX render paths.
+ *
+ * @since 2.10.2.4
+ * @param string $content The rendered module HTML.
+ * @param object $module The module instance (last arg on both filters).
+ * @return string
+ */
+ static public function escape_foreign_module_shortcodes( $content, $module ) {
+ if ( isset( $module->renders_shortcodes ) && ! $module->renders_shortcodes ) {
+ $content = self::escape_foreign_shortcodes( $content );
+ }
+ return $content;
+ }
+
+ /**
+ * Filter adapter for fl_builder_render_module_html_content, whose $module
+ * argument is fourth rather than second.
+ *
+ * @since 2.10.2.4
+ * @param string $content The rendered module HTML.
+ * @param string $type The module type.
+ * @param object $settings The module settings.
+ * @param object $module The module instance.
+ * @return string
+ */
+ static public function escape_foreign_module_html_shortcodes( $content, $type, $settings, $module ) {
+ return self::escape_foreign_module_shortcodes( $content, $module );
+ }
+
+ /**
* Renders the CSS classes for the main content div tag.
*
* @since 1.6.4
@@ -3825,8 +3888,8 @@
$selector_suffix = ' > .fl-module-content';
}
- // Extra specificity for top-level modules
- if ( ! $node->parent ) {
+ // Extra specificity for top-level modules (not applicable for standalone module blocks)
+ if ( ! $node->parent && empty( $node->is_block ) ) {
$selector_prefix = '.fl-builder-content > ' . $selector_prefix;
}
break;
@@ -4019,8 +4082,8 @@
$selector = '.fl-node-' . $module->node . '.fl-module-' . $module->settings->type;
}
- // Extra specificity for top-level modules
- if ( ! $module->parent ) {
+ // Extra specificity for top-level modules (not applicable for standalone module blocks)
+ if ( ! $module->parent && empty( $module->is_block ) ) {
$selector = '.fl-builder-content > ' . $selector;
}
--- a/beaver-builder-lite-version/classes/services/class-fl-builder-service-convertkit.php
+++ b/beaver-builder-lite-version/classes/services/class-fl-builder-service-convertkit.php
@@ -201,7 +201,7 @@
* @type bool|string $error The error message or false if no error.
* }
*/
- public function subscribe( $settings, $email, $name, $custom_fields ) {
+ public function subscribe( $settings, $email, $name, $custom_fields = array() ) {
$account_data = $this->get_account_data( $settings->service_account );
$response = array(
'error' => false,
--- a/beaver-builder-lite-version/extensions/fl-builder-dynamic-global/classes/class-fl-builder-dynamic-global.php
+++ b/beaver-builder-lite-version/extensions/fl-builder-dynamic-global/classes/class-fl-builder-dynamic-global.php
@@ -1475,7 +1475,22 @@
}
if ( isset( $orig_node_settings->connections ) ) {
- $new_settings->connections = $orig_node_settings->connections;
+ // Per-key merge so non-customizable connections stay sourced from the
+ // template — the instance carries a snapshot of them from edit time
+ // and a wholesale replace would freeze it after the template changes.
+ // Instance wins only for keys whose root field is customizable.
+ $template_connections = isset( $new_settings->connections ) ? (array) $new_settings->connections : [];
+ $instance_connections = (array) $orig_node_settings->connections;
+ $merged_connections = $template_connections;
+ foreach ( $instance_connections as $conn_key => $conn_value ) {
+ $root_field = strstr( (string) $conn_key, '.' ) ? strstr( (string) $conn_key, '.', true ) : $conn_key;
+ if ( in_array( $root_field, $merged_dynamic_fields, true ) ) {
+ $merged_connections[ $conn_key ] = $conn_value;
+ }
+ }
+ if ( ! empty( $merged_connections ) ) {
+ $new_settings->connections = $merged_connections;
+ }
}
$new_settings->dynamic_node_settings = $orig_node_settings->dynamic_node_settings;
@@ -1647,6 +1662,8 @@
$template_root_node = null;
$template_post_title = '';
$template_post_id = FLBuilderModel::is_node_global( $node );
+ $modules = [];
+
if ( $template_post_id ) {
$template_post = get_post( $template_post_id );
$template_post_title = isset( $template_post->post_title ) ? $template_post->post_title : '';
@@ -1662,11 +1679,19 @@
}
$root_node_settings[ $node->node ] = $root_settings;
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ if ( 'module' === $node->type && ! empty( $node->moduleType ) ) {
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ $modules[ $node->node ] = $node->moduleType;
+ }
}
$categorized_nodes = FLBuilderModel::get_categorized_child_nodes( $template_root_node );
foreach ( $categorized_nodes as $cat_key => $cat ) {
foreach ( $cat as $node_key => $node_item ) {
+ if ( isset( $node_item->type ) && 'module' === $node_item->type ) {
+ $modules[ $node_item->node ] = $node_item->slug;
+ }
if ( empty( $node_item->settings->dynamic_fields->fields ) ) {
continue;
}
@@ -1684,6 +1709,7 @@
'title' => $template_post_title,
'root' => $root_node_settings,
'child' => $child_settings,
+ 'modules' => $modules,
];
}
@@ -1722,6 +1748,15 @@
}
$categorized_nodes = FLBuilderModel::get_categorized_child_nodes( $node );
+ $cat_modules = $categorized_nodes['modules'] ?? [];
+ $ref_modules = [];
+ $modules = [];
+ foreach ( $cat_modules as $mod_key => $mod ) {
+ if ( isset( $mod->slug ) ) {
+ $parts = explode( '__', $mod_key );
+ $ref_modules[ $parts[0] ] = $mod->slug;
+ }
+ }
$child_obj = $dynamic_node_settings->child;
$child_settings = [];
@@ -1729,6 +1764,10 @@
if ( ! is_object( $child_node ) ) {
continue;
}
+ if ( array_key_exists( $child_node_key, $ref_modules ) ) {
+ $modules[ $child_node_key ] = $ref_modules[ $child_node_key ];
+ }
+
foreach ( $child_node as $field_key => $field_value ) {
if ( 'connections' === $field_key ) {
continue;
@@ -1748,6 +1787,7 @@
'title' => $template_post_title,
'root' => $root_settings,
'child' => $child_settings,
+ 'modules' => $modules,
];
}
--- a/beaver-builder-lite-version/fl-builder.php
+++ b/beaver-builder-lite-version/fl-builder.php
@@ -3,7 +3,7 @@
* Plugin Name: Beaver Builder Plugin (Lite Version)
* Plugin URI: https://www.wpbeaverbuilder.com/?utm_medium=bb&utm_source=plugins-admin-page&utm_campaign=plugins-admin-uri
* Description: A drag and drop frontend WordPress page builder plugin that works with almost any theme!
- * Version: 2.10.2.2
+ * Version: 2.10.3.2
* Author: The Beaver Builder Team
* Author URI: https://www.wpbeaverbuilder.com/?utm_medium=bb&utm_source=plugins-admin-page&utm_campaign=plugins-admin-author
* Copyright: (c) 2014 Beaver Builder
@@ -11,8 +11,8 @@
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: fl-builder
* Requires at least: 6.6
- * Tested up to: 7.0
- * Requires PHP: 7.2
+ * Tested up to: 7.1
+ * Requires PHP: 7.4
*/
require_once 'classes/class-fl-builder-loader.php';
--- a/beaver-builder-lite-version/includes/ui-field.php
+++ b/beaver-builder-lite-version/includes/ui-field.php
@@ -17,7 +17,7 @@
}
}
- if ( data.field.type === 'form' ) {
+ if ( data.field.type === 'form' || data.field.type === 'raw' ) {
dynamicFieldIcons = '';
} else if ( data.field.type === 'button' && data.name === 'service_connect_button' ) {
dynamicFieldIcons = '';
@@ -31,7 +31,7 @@
#>
<# if ( ! data.field.label ) { #>
<td class="fl-field-control" colspan="2">
- <# if ( dynamicFieldIcons && 'fl-builder-template' === FLBuilderConfig.postType && 'form' !== data.field.type ) { #>
+ <# if ( dynamicFieldIcons && 'fl-builder-template' === FLBuilderConfig.postType && 'form' !== data.field.type && ! data.isMultiple ) { #>
<i class="fl-dynamic-node-field dashicons fl-tip {{dynamicFieldIcons}}" title="{{dynamicEditingTitle}}" data-target-field="{{data.name}}" data-target-field-type="{{data.field.type}}"></i>
<# } #>
<# } else { #>
@@ -52,7 +52,7 @@
<span class="fl-builder-field-index">{{ data.index + 1 }}</span>
<# } #>
<# } #>
- <# if ( dynamicFieldIcons && 'fl-builder-template' === FLBuilderConfig.postType && 'form' !== data.field.type ) { #>
+ <# if ( dynamicFieldIcons && 'fl-builder-template' === FLBuilderConfig.postType && 'form' !== data.field.type && ! data.isMultiple ) { #>
<i class="fl-dynamic-node-field dashicons fl-tip {{dynamicFieldIcons}}" title="{{dynamicEditingTitle}}" data-target-field="{{targetFieldName}}" data-target-field-type="{{data.field.type}}" data-target-field-index="{{ ( undefined !== data.index ) ? data.index : '' }}"></i>
<# } #>
<# if ( data.responsive ) { #>
--- a/beaver-builder-lite-version/includes/ui-js-overlay-templates.php
+++ b/beaver-builder-lite-version/includes/ui-js-overlay-templates.php
@@ -110,7 +110,11 @@
<# } #>
<# if ( data.hasRules ) { #>
- <i class="fas fa-eye fl-tip fl-block-has-rules {{data.rulesTypeRow}}" title="<?php _e( 'This row has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextRow}}"></i>
+ <span class="fl-tip fl-block-has-rules {{data.rulesTypeRow}}" title="<?php _e( 'This row has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextRow}}">
+ <svg width="16" height="14" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
+ <path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/>
+ </svg>
+ </span>
<# } #>
</div>
</script>
@@ -322,7 +326,11 @@
<# } #>
<# if ( data.hasRules ) { #>
- <i class="fas fa-eye fl-tip fl-block-has-rules" title="<?php _e( 'This column has visibility rules.', 'fl-builder' ); ?>"></i>
+ <span class="fl-tip fl-block-has-rules" title="<?php _e( 'This column has visibility rules.', 'fl-builder' ); ?>">
+ <svg width="16" height="14" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
+ <path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/>
+ </svg>
+ </span>
<# } #>
<?php if ( ! $simple_ui ) : ?>
@@ -583,9 +591,17 @@
<# } #>
<# if ( data.colHasRules ) { #>
- <i class="fas fa-eye fl-tip fl-block-has-rules {{data.rulesTypeCol}}" title="<?php _e( 'This column has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextCol}}"></i>
+ <span class="fl-tip fl-block-has-rules {{data.rulesTypeCol}}" title="<?php _e( 'This column has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextCol}}">
+ <svg width="16" height="14" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
+ <path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/>
+ </svg>
+ </span>
<# } else if ( data.hasRules ) { #>
- <i class="fas fa-eye fl-tip fl-block-has-rules {{data.rulesTypeModule}}" title="<?php _e( 'This module has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextModule}}"></i>
+ <span class="fl-tip fl-block-has-rules {{data.rulesTypeModule}}" title="<?php _e( 'This module has visibility rules', 'fl-builder' ); ?>: {{data.rulesTextModule}}">
+ <svg width="16" height="14" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
+ <path fill="currentColor" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/>
+ </svg>
+ </span>
<# } #>
<?php if ( ! FLBuilderModel::is_post_user_template( 'module' ) && ! $simple_ui ) : ?>
--- a/beaver-builder-lite-version/includes/ui-settings-form-row.php
+++ b/beaver-builder-lite-version/includes/ui-settings-form-row.php
@@ -29,7 +29,7 @@
#>
<tbody id="fl-field-{{data.rootName}}" class="fl-field fl-builder-field-multiples" data-limit="{{limit}}" data-type="form" data-preview='{{{data.preview}}}' data-connections="{{{connections}}}">
<# if ( ! shouldDeferRendering ) { #>
- <# if ( data.global && data.dynamicOptions?.source !== 'legacy' && data.field.type === 'form' && 'fl-builder-template' === FLBuilderConfig.postType && data.dynamicEditing ) { #>
+ <# if ( data.global && data.dynamicOptions?.source !== 'legacy' && 'fl-builder-template' === FLBuilderConfig.postType && data.dynamicEditing ) { #>
<tr class="fl-builder-field-multiple-label">
<th class="fl-field-label">
<#
--- a/beaver-builder-lite-version/includes/updater-config.php
+++ b/beaver-builder-lite-version/includes/updater-config.php
@@ -3,7 +3,7 @@
if ( class_exists( 'FLUpdater' ) ) {
FLUpdater::add_product(array(
'name' => 'Beaver Builder Plugin (Lite Version)',
- 'version' => '2.10.2.2',
+ 'version' => '2.10.3.2',
'slug' => 'bb-plugin',
'type' => 'plugin',
));
--- a/beaver-builder-lite-version/modules/acf-block/acf-block.php
+++ b/beaver-builder-lite-version/modules/acf-block/acf-block.php
@@ -7,14 +7,17 @@
*/
public function __construct() {
parent::__construct( array(
- 'name' => __( 'ACF Block', 'fl-builder' ),
- 'description' => __( 'Display an ACF block.', 'fl-builder' ),
- 'group' => __( 'ACF Blocks', 'fl-builder' ),
- 'category' => __( 'ACF Blocks', 'fl-builder' ),
- 'icon' => 'layout.svg',
- 'editor_export' => true,
- 'partial_refresh' => true,
- 'enabled' => false, // We use aliases instead.
+ 'name' => __( 'ACF Block', 'fl-builder' ),
+ 'description' => __( 'Display an ACF block.', 'fl-builder' ),
+ 'group' => __( 'ACF Blocks', 'fl-builder' ),
+ 'category' => __( 'ACF Blocks', 'fl-builder' ),
+ 'icon' => 'layout.svg',
+ 'editor_export' => true,
+ 'partial_refresh' => true,
+ 'enabled' => false, // We use aliases instead.
+ // Renders arbitrary block output that can contain untrusted user
+ // data; do not run its shortcodes through the layout pass.
+ 'renders_shortcodes' => false,
) );
}
--- a/beaver-builder-lite-version/modules/button-group/includes/frontend.js.php
+++ b/beaver-builder-lite-version/modules/button-group/includes/frontend.js.php
@@ -17,7 +17,7 @@
<?php echo $settings->items[ $i ]->button; ?>
});
<?php elseif ( 'lightbox' == $settings->items[ $i ]->click_action ) : ?>
- $this.find('.fl-button-lightbox').magnificPopup({
+ $this.find('<?php echo $button_item_id; ?> .fl-button-lightbox').magnificPopup({
<?php if ( 'video' == $settings->items[ $i ]->lightbox_content_type ) : ?>
type: 'iframe',
mainClass: 'fl-button-lightbox-wrap',
--- a/beaver-builder-lite-version/modules/button/button.php
+++ b/beaver-builder-lite-version/modules/button/button.php
@@ -176,6 +176,18 @@
}
/**
+ * Returns just the element name (a or button) for use in closing tags.
+ * @since 2.10
+ * @return string
+ */
+ public function get_tag_name() {
+ if ( isset( $this->settings->click_action ) && 'link' !== $this->settings->click_action && $this->version > 2 ) {
+ return 'button';
+ }
+ return 'a';
+ }
+
+ /**
* Returns a link attribute or data attribute based on the click action
* @since 2.10
* @return string
@@ -349,19 +361,21 @@
),
),
'copy_text' => array(
- 'type' => 'text',
- 'label' => __( 'Text to Copy', 'fl-builder' ),
- 'default' => '',
- 'preview' => array(
+ 'type' => 'text',
+ 'label' => __( 'Text to Copy', 'fl-builder' ),
+ 'default' => '',
+ 'min_version' => 3,
+ 'preview' => array(
'type' => 'none',
),
),
'copy_success_message' => array(
- 'type' => 'text',
- 'label' => __( 'Copy Success Message', 'fl-builder' ),
- 'default' => __( 'Copied!', 'fl-builder' ),
- 'preview' => array(
+ 'type' => 'text',
+ 'label' => __( 'Copy Success Message', 'fl-builder' ),
+ 'default' => __( 'Copied!', 'fl-builder' ),
+ 'min_version' => 3,
+ 'preview' => array(
'type' => 'none',
),
),
--- a/beaver-builder-lite-version/modules/button/deprecated/v1/includes/frontend.php
+++ b/beaver-builder-lite-version/modules/button/deprecated/v1/includes/frontend.php
@@ -39,7 +39,7 @@
?>
<i class="fl-button-icon fl-button-icon-after <?php echo esc_attr( $settings->icon ); ?>" aria-hidden="true"></i>
<?php endif; ?>
- </<?php echo esc_attr( $module->get_tag() ); ?>>
+ </<?php echo esc_attr( $module->get_tag_name() ); ?>>
</div>
<?php if ( 'lightbox' == $settings->click_action && 'html' == $settings->lightbox_content_type && isset( $settings->lightbox_content_html ) ) : ?>
<div class="<?php echo $button_node_id; ?> fl-button-lightbox-content mfp-hide">
--- a/beaver-builder-lite-version/modules/button/includes/frontend.php
+++ b/beaver-builder-lite-version/modules/button/includes/frontend.php
@@ -45,7 +45,7 @@
?>
<i class="fl-button-icon fl-button-icon-after <?php echo esc_attr( $settings->icon ); ?>" aria-hidden="true"></i>
<?php endif; ?>
- </<?php echo esc_attr( $module->get_tag() ); ?>>
+ </<?php echo esc_attr( $module->get_tag_name() ); ?>>
<?php if ( 'lightbox' == $settings->click_action && 'html' == $settings->lightbox_content_type && isset( $settings->lightbox_content_html ) ) : ?>
<div class="<?php echo $button_node_id; ?> fl-button-lightbox-content mfp-hide">
<?php echo $settings->lightbox_content_html; ?>
--- a/beaver-builder-lite-version/modules/numbers/numbers.php
+++ b/beaver-builder-lite-version/modules/numbers/numbers.php
@@ -130,7 +130,10 @@
public function render_circle_bar() {
- $width = ! empty( $this->settings->circle_width ) ? $this->settings->circle_width : 100;
+ // Cast to a number so the value can never break out of the SVG
+ // viewBox attribute below (stored settings are only tag-stripped, not
+ // attribute-safe). CVE-2026-15242.
+ $width = ! empty( $this->settings->circle_width ) ? (float) $this->settings->circle_width : 100;
$pos = ( $width / 2 );
$radius = $pos - 10;
$dash = number_format( ( ( M_PI * 2 ) * $radius ), 2, '.', '' );
--- a/beaver-builder-lite-version/modules/reusable-block/reusable-block.php
+++ b/beaver-builder-lite-version/modules/reusable-block/reusable-block.php
@@ -13,14 +13,17 @@
*/
public function __construct() {
parent::__construct( array(
- 'name' => __( 'WordPress Pattern', 'fl-builder' ),
- 'description' => __( 'Display a WordPress Pattern.', 'fl-builder' ),
- 'group' => __( 'WordPress Patterns', 'fl-builder' ),
- 'category' => __( 'WordPress Patterns', 'fl-builder' ),
- 'icon' => 'layout.svg',
- 'editor_export' => true,
- 'partial_refresh' => true,
- 'enabled' => false, // We use aliases instead.
+ 'name' => __( 'WordPress Pattern', 'fl-builder' ),
+ 'description' => __( 'Display a WordPress Pattern.', 'fl-builder' ),
+ 'group' => __( 'WordPress Patterns', 'fl-builder' ),
+ 'category' => __( 'WordPress Patterns', 'fl-builder' ),
+ 'icon' => 'layout.svg',
+ 'editor_export' => true,
+ 'partial_refresh' => true,
+ 'enabled' => false, // We use aliases instead.
+ // Renders arbitrary block output that can contain untrusted user
+ // data; do not run its shortcodes through the layout pass.
+ 'renders_shortcodes' => false,
) );
}
--- a/beaver-builder-lite-version/modules/video/video.php
+++ b/beaver-builder-lite-version/modules/video/video.php
@@ -110,9 +110,9 @@
$vid_data = $this->get_data();
$preload = FLBuilderModel::is_builder_active() && ! empty( $vid_data->poster ) ? ' preload="none"' : '';
- $video_meta .= '<meta itemprop="url" content="' . ( empty( $vid_data->url ) ? '' : $vid_data->url ) . '" />';
+ $video_meta .= '<meta itemprop="url" content="' . esc_url( empty( $vid_data->url ) ? '' : $vid_data->url ) . '" />';
if ( $schema ) {
- $video_meta .= '<meta itemprop="thumbnail" content="' . $video_poster . '" />';
+ $video_meta .= '<meta itemprop="thumbnail" content="' . esc_url( $video_poster ) . '" />';
}
$video_html = $video_meta;
@@ -120,7 +120,7 @@
$video_sc = sprintf( '%s', __( 'Video not specified. Please select one to display.', 'fl-builder' ) );
if ( ! empty( $vid_data->url ) ) {
- $video_sc = '[video src="' . preg_replace( '//??.*/', '', $vid_data->url ) . '" ' . $vid_data->extension . '="' . preg_replace( '//??.*/', '', $vid_data->url ) . '"' . $vid_data->video_webm . ' poster="' . $video_poster . '" ' . $vid_data->autoplay . $vid_data->loop . $preload . '][/video]';
+ $video_sc = '[video src="' . esc_url( preg_replace( '//??.*/', '', $vid_data->url ) ) . '" ' . $vid_data->extension . '="' . esc_url( preg_replace( '//??.*/', '', $vid_data->url ) ) . '"' . $vid_data->video_webm . ' poster="' . esc_url( $video_poster ) . '" ' . $vid_data->autoplay . $vid_data->loop . $preload . '][/video]';
}
if ( 'yes' === $this->settings->video_lightbox ) {
--- a/beaver-builder-lite-version/modules/widget/widget.php
+++ b/beaver-builder-lite-version/modules/widget/widget.php
@@ -10,12 +10,15 @@
*/
public function __construct() {
parent::__construct(array(
- 'name' => __( 'Widget', 'fl-builder' ),
- 'description' => __( 'Display a WordPress widget.', 'fl-builder' ),
- 'group' => __( 'WordPress Widgets', 'fl-builder' ),
- 'category' => __( 'WordPress Widgets', 'fl-builder' ),
- 'editor_export' => false,
- 'partial_refresh' => true,
+ 'name' => __( 'Widget', 'fl-builder' ),
+ 'description' => __( 'Display a WordPress widget.', 'fl-builder' ),
+ 'group' => __( 'WordPress Widgets', 'fl-builder' ),
+ 'category' => __( 'WordPress Widgets', 'fl-builder' ),
+ 'editor_export' => false,
+ 'partial_refresh' => true,
+ // Widgets render third-party output that can contain untrusted user
+ // data (e.g. comment author names); do not run its shortcodes.
+ 'renders_shortcodes' => false,
));
}