Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/funnel-builder/admin/class-wffn-admin.php
+++ b/funnel-builder/admin/class-wffn-admin.php
@@ -822,7 +822,7 @@
wp_enqueue_style( 'wffn-flex-admin', $this->get_admin_url() . '/assets/css/admin.css', array(), WFFN_VERSION_DEV );
if ( WFFN_Core()->admin->is_wffn_flex_page() ) {
- $this->load_react_app( 'main-20260422140937' ); //phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation
+ $this->load_react_app( 'main-20260423144444' ); //phpcs:ignore WordPressVIPMinimum.Security.Mustache.OutputNotation
if ( isset( $_GET['page'] ) && $_GET['page'] === 'bwf' && method_exists( 'BWF_Admin_General_Settings', 'get_localized_bwf_data' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_localize_script( 'wffn-contact-admin', 'bwfAdminGen', BWF_Admin_General_Settings::get_instance()->get_localized_bwf_data() );
--- a/funnel-builder/admin/views/contact/dist/main-20260422140937.asset.php
+++ b/funnel-builder/admin/views/contact/dist/main-20260422140937.asset.php
@@ -1 +0,0 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-components/build-style/style.css', 'wp-compose', 'wp-date', 'wp-deprecated', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-is-shallow-equal', 'wp-keycodes', 'wp-polyfill', 'wp-primitives', 'wp-url', 'wp-viewport', 'wp-warning'), 'version' => 'c343bee2b9c2ffd1a8a5073adf38798f');
No newline at end of file
--- a/funnel-builder/admin/views/contact/dist/main-20260423144444.asset.php
+++ b/funnel-builder/admin/views/contact/dist/main-20260423144444.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-components/build-style/style.css', 'wp-compose', 'wp-date', 'wp-deprecated', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-is-shallow-equal', 'wp-keycodes', 'wp-polyfill', 'wp-primitives', 'wp-url', 'wp-viewport', 'wp-warning'), 'version' => 'c343bee2b9c2ffd1a8a5073adf38798f');
No newline at end of file
--- a/funnel-builder/funnel-builder.php
+++ b/funnel-builder/funnel-builder.php
@@ -3,7 +3,7 @@
* Plugin Name: FunnelKit Funnel Builder
* Plugin URI: https://funnelkit.com/wordpress-funnel-builder/
* Description: Create high-converting sales funnels on WordPress that look professional by following a well-guided step-by-step process.
- * Version: 3.15.0.1
+ * Version: 3.15.0.2
* Author: FunnelKit
* Author URI: https://funnelkit.com
* License: GPLv3 or later
@@ -150,8 +150,8 @@
*/
public function define_plugin_properties() {
- define( 'WFFN_VERSION', '3.15.0.1-relase.3.15.0.1' );
- define( 'WFFN_BWF_VERSION', '1.10.12.77' );
+ define( 'WFFN_VERSION', '3.15.0.2' );
+ define( 'WFFN_BWF_VERSION', '1.10.12.78' );
define( 'WFFN_MIN_WC_VERSION', '3.5.0' );
define( 'WFFN_MIN_WP_VERSION', '5.4.0' );
--- a/funnel-builder/includes/class-wffn-common.php
+++ b/funnel-builder/includes/class-wffn-common.php
@@ -613,7 +613,10 @@
* @return bool
*/
public static function is_divi5_active() {
- return function_exists( 'et_builder_d5_enabled' ) && et_builder_d5_enabled();
+ if ( did_action( 'after_setup_theme' ) ) {
+ return function_exists( 'et_builder_d5_enabled' ) && et_builder_d5_enabled();
+ }
+ return false;
}
/**
--- a/funnel-builder/includes/class-wffn-public.php
+++ b/funnel-builder/includes/class-wffn-public.php
@@ -280,6 +280,11 @@
$get_data = json_decode( stripslashes( $get_data ), true );
if ( is_array( $get_data ) ) {
+ $validation = $this->validate_funnel_setup_data( $get_data );
+ if ( is_wp_error( $validation ) ) {
+ wp_send_json( $result );
+ return;
+ }
$result = $this->maybe_record_data_with_funnel_setup( array( 'data' => $get_data ) );
}
} catch ( Exception | Error $e ) {
@@ -974,7 +979,7 @@
WFFN_Core()->data->set(
'current_step',
array(
- 'id' => $data['current_step']['id'],
+ 'id' => absint( $data['current_step']['id'] ),
'type' => ( $data['current_step']['post_type'] === 'wffn_oty' ) ? 'optin_ty' : 'wc_thankyou',
)
);
@@ -1022,6 +1027,30 @@
}
}
+ public function validate_funnel_setup_data( $data, $request = null, $param = null ) {
+ if ( ! is_array( $data ) ) {
+ return true;
+ }
+ $allowed_post_types = array( 'wffn_ty', 'wffn_landing', 'wffn_optin', 'wffn_oty' );
+ $track_data = isset( $data['track_data'] ) ? $data['track_data'] : array();
+ if ( ! is_array( $track_data ) ) {
+ return new WP_Error( 'invalid_track_data', 'track_data must be an array.' );
+ }
+ foreach ( $track_data as $item ) {
+ if ( ! is_array( $item ) || ! isset( $item['current_step'] ) || ! is_array( $item['current_step'] ) ) {
+ continue;
+ }
+ $step = $item['current_step'];
+ if ( isset( $step['id'] ) && ! is_numeric( $step['id'] ) ) {
+ return new WP_Error( 'invalid_step_id', 'current_step.id must be numeric.' );
+ }
+ if ( isset( $step['post_type'] ) && ! in_array( $step['post_type'], $allowed_post_types, true ) ) {
+ return new WP_Error( 'invalid_post_type', 'current_step.post_type is not allowed.' );
+ }
+ }
+ return true;
+ }
+
public function register_routes() {
register_rest_route(
'wffn',
@@ -1030,6 +1059,16 @@
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'handle_api_request' ),
'permission_callback' => '__return_true',
+ 'args' => array(
+ 'action' => array(
+ 'type' => 'string',
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
+ 'data' => array(
+ 'type' => 'object',
+ 'validate_callback' => array( $this, 'validate_funnel_setup_data' ),
+ ),
+ ),
)
);
}
--- a/funnel-builder/modules/checkouts/builder/divi/class-wfacp-divi.php
+++ b/funnel-builder/modules/checkouts/builder/divi/class-wfacp-divi.php
@@ -8,15 +8,6 @@
private function __construct() {
add_action( 'after_setup_theme', array( $this, 'init' ), 5 );
- // Register global D5 hooks (conversion outlines, legacy hiding) early.
- // Post-type-specific D5 module loading is deferred to init:10 via
- // maybe_load_d5_modules() where url_to_postid() can resolve the page.
- if ( did_action( 'plugins_loaded' ) ) {
- $this->load_divi5_modules();
- } else {
- add_action( 'plugins_loaded', array( $this, 'load_divi5_modules' ), 2 );
- }
-
add_action( 'wfacp_register_template_types', array( $this, 'register_template_type' ), 12 );
add_filter( 'wfacp_register_templates', array( $this, 'register_templates' ) );
add_filter( 'wfacp_template_edit_link', array( $this, 'add_template_edit_link' ), 10, 2 );
@@ -30,6 +21,7 @@
if ( ! ( class_exists( 'ET_Builder_Plugin' ) || function_exists( 'et_setup_theme' ) ) ) {
return;
}
+ $this->load_divi5_modules();
add_filter( 'wfacp_is_theme_builder', array( $this, 'is_divi_page' ) );
add_action( 'wfacp_template_removed', array( $this, 'delete_divi_data' ) );
add_action( 'wfacp_duplicate_pages', array( $this, 'duplicate_template' ), 10, 3 );
--- a/funnel-builder/modules/optins/modules/optin-pages/compatibilities/page-builders/divi/class-wffn-optin-pages-divi.php
+++ b/funnel-builder/modules/optins/modules/optin-pages/compatibilities/page-builders/divi/class-wffn-optin-pages-divi.php
@@ -24,8 +24,7 @@
// Add Divi 5 detection hook - use priority 2 like upsell sadad
// Also add init hook as fallback in case plugins_loaded fires too early
- add_action( 'plugins_loaded', array( $this, 'initialize_deep_integration' ), 2 );
- add_action( 'init', array( $this, 'initialize_deep_integration' ), 1 );
+ add_action( 'after_setup_theme', array( $this, 'initialize_deep_integration' ), 2 );
// Keep Divi 4 hook for backward compatibility
add_action( 'divi_extensions_init', array( $this, 'init_extension' ) );
--- a/funnel-builder/modules/thankyou-pages/compatibilities/page-builders/divi/class-wffn-thankyou-wc-pages-divi.php
+++ b/funnel-builder/modules/thankyou-pages/compatibilities/page-builders/divi/class-wffn-thankyou-wc-pages-divi.php
@@ -21,8 +21,7 @@
*/
public function __construct() {
$this->url = plugin_dir_url( __FILE__ );
- add_action( 'plugins_loaded', array( $this, 'initialize_deep_integration' ), 2 );
- add_action( 'init', array( $this, 'initialize_deep_integration' ), 1 );
+ add_action( 'after_setup_theme', array( $this, 'initialize_deep_integration' ), 2 );
add_action( 'divi_extensions_init', array( $this, 'init_extension' ) );
}
@@ -46,9 +45,10 @@
*/
private function is_divi_5(): bool {
if ( function_exists( 'et_builder_d5_enabled' ) ) {
+
return et_builder_d5_enabled();
}
- if ( class_exists( 'ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface' ) ) {
+ if ( interface_exists( 'ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface' ) ) {
return true;
}
if ( defined( 'ET_BUILDER_5_DIR' ) ) {
--- a/funnel-builder/woofunnels/includes/class-wfco-model-report-views.php
+++ b/funnel-builder/woofunnels/includes/class-wfco-model-report-views.php
@@ -37,64 +37,73 @@
public static function update_data( $date = '', $object_id = '', $type = 1 ) {
global $wpdb;
- $where = [];
- $insert = [];
+ $has_object_id = ( '' !== $object_id );
+ $object_id = absint( $object_id );
+ $type = absint( $type );
+ $insert = [];
+
if ( $date !== '' ) {
- $where['date'] = "`date`='$date'";
- $insert['date'] = $date;
+ $date = sanitize_text_field( $date );
} else {
- $date = date( 'Y-m-d' );
- $where['date'] = "`date`='$date'";
- $insert['date'] = $date;
+ $date = date( 'Y-m-d' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
- if ( $object_id !== '' ) {
- $where['object_id'] = "`object_id`='$object_id'";
+ $insert['date'] = $date;
+
+ if ( $has_object_id ) {
$insert['object_id'] = $object_id;
}
- $where['type'] = "`type`='$type'";
$insert['type'] = $type;
- $where_string = implode( ' and ', $where );
- $table = self::_table();
- $get_sql = "SELECT * FROM $table WHERE {$where_string};";
- $result = $wpdb->get_results( $get_sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
+ $table = self::_table();
+
+ if ( $has_object_id ) {
+ $get_sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `object_id` = %d AND `type` = %d", $date, $object_id, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ } else {
+ $get_sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `type` = %d", $date, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
+ $result = $wpdb->get_results( $get_sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
if ( ! empty( $result ) ) {
- $primary_id = $result[0]['id'];
- $sql = "UPDATE $table set no_of_sessions=no_of_sessions+1 where id ='{$primary_id}';";
- $wpdb->query( $sql ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
+ $primary_id = absint( $result[0]['id'] );
+ $wpdb->query( $wpdb->prepare( "UPDATE `$table` SET no_of_sessions = no_of_sessions + 1 WHERE id = %d", $primary_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
} else {
- $wpdb->insert( $table, $insert ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+ $wpdb->insert( $table, $insert ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- $wpdb->insert() parameterizes internally; safe.
}
}
public static function get_data( $date = '', $object_id = '', $type = 1, $interval = false ) {
- $where = [];
+ global $wpdb;
+ $has_object_id = ( '' !== $object_id );
+ $object_id = absint( $object_id );
+ $type = absint( $type );
+ $table = self::_table();
if ( $date !== '' ) {
if ( true === $interval ) {
- $where['date'] = $date;
-
+ // $date is an internally-generated SQL date-range fragment, not user input.
+ if ( $has_object_id ) {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE {$date} AND `object_id` = %d AND `type` = %d", $object_id, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ } else {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE {$date} AND `type` = %d", $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
} else {
- $where['date'] = "`date`='$date'";
+ $date = sanitize_text_field( $date );
+ if ( $has_object_id ) {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `object_id` = %d AND `type` = %d", $date, $object_id, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ } else {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `type` = %d", $date, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
}
} else {
- $date = date( 'Y-m-d' );
- $where['date'] = "`date`='$date'";
- }
-
- if ( $object_id !== '' ) {
- $where['object_id'] = "`object_id`='$object_id'";
+ $date = date( 'Y-m-d' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
+ if ( $has_object_id ) {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `object_id` = %d AND `type` = %d", $date, $object_id, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ } else {
+ $sql = $wpdb->prepare( "SELECT * FROM `$table` WHERE `date` = %s AND `type` = %d", $date, $type ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
}
- $where['type'] = "`type`='$type'";
- $where_string = implode( ' and ', $where );
- global $wpdb;
- $table = self::_table();
- $sql = "select * from `{$table}` WHERE {$where_string};";
- $data = $wpdb->get_results( $sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
-
- return $data;
+ return $wpdb->get_results( $sql, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL
}
--- a/funnel-builder/woofunnels/includes/class-wfco-model.php
+++ b/funnel-builder/woofunnels/includes/class-wfco-model.php
@@ -73,14 +73,15 @@
$sql = 'SELECT COUNT(*) FROM ' . self::_table();
if ( ! is_null( $dependency ) ) {
+ $col_value = isset( $dependency['col_value'] ) ? $dependency['col_value'] : '';
$sql .= ' INNER JOIN ' . $dependency['dependency_table'];
$sql .= ' on ' . self::_table() . '.' . $dependency['dependent_col'];
$sql .= ' =' . $dependency['dependency_table'] . '.' . $dependency['dependency_col'];
- $sql .= ' WHERE ' . $dependency['dependency_table'] . '.' . $dependency['col_name'];
- $sql .= ' =' . $dependency['col_value'];
+ $sql .= $wpdb->prepare( ' WHERE ' . $dependency['dependency_table'] . '.' . $dependency['col_name'] . ' = %s', $col_value ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( isset( $dependency['connector_id'] ) ) {
- $sql .= ' AND ' . $dependency['connector_table'] . '.' . $dependency['connector_col'] . '=' . $dependency['connector_id'];
+ $connector_id = absint( $dependency['connector_id'] );
+ $sql .= $wpdb->prepare( ' AND ' . $dependency['connector_table'] . '.' . $dependency['connector_col'] . ' = %d', $connector_id ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}
--- a/funnel-builder/woofunnels/includes/class-woofunnels-dashboard-loader.php
+++ b/funnel-builder/woofunnels/includes/class-woofunnels-dashboard-loader.php
@@ -8,7 +8,7 @@
*/
-define( 'BWF_VERSION', '1.10.12.77' );
+define( 'BWF_VERSION', '1.10.12.78' );
define( 'BWF_DB_VERSION', '1.0.6' );
if ( ! class_exists( 'WooFunnels_Dashboard' ) ) {
#[AllowDynamicProperties]