Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2025-14980: BetterDocs <= 4.3.3 – Authenticated (Contributor+) Sensitive Information Exposure (betterdocs)

Plugin betterdocs
Severity Medium (CVSS 6.5)
CWE 200
Vulnerable Version 4.3.3
Patched Version 4.3.4
Disclosed January 7, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14980:
The BetterDocs WordPress plugin, versions up to and including 4.3.3, contains an information exposure vulnerability. The plugin’s scripts() function in the Admin class exposes sensitive plugin settings, including OpenAI API keys, to authenticated users with contributor-level access or higher.

Atomic Edge research identifies the root cause in the betterdocs/includes/Core/Admin.php file. The scripts() function (lines 688-734) localizes JavaScript objects for the FAQ and Glossary admin interfaces. It passes the entire betterdocs_settings option array to the frontend via the betterdocsFaq and betterdocsGlossary objects. This array contains sensitive keys like ai_autowrite_api_key and ai_chatbot_api_key. The function does not filter these keys based on user capabilities before exposing them.

An attacker with contributor-level access can exploit this by accessing the plugin’s FAQ or Glossary admin pages. These pages load JavaScript that receives the localized settings data. The attacker can then extract the sensitive API keys from the JavaScript objects. The attack vector is client-side JavaScript execution within the WordPress admin area, requiring no special HTTP requests beyond normal page access.

The patch modifies the scripts() function in betterdocs/includes/Core/Admin.php. It adds a capability check before localizing the settings. The code now retrieves the settings array (line 736), checks if the current user has the edit_docs_settings capability (line 737), and unsets the ai_autowrite_api_key and ai_chatbot_api_key from the array if the user lacks this permission (lines 738-741). The filtered array is then passed to the JavaScript localization functions (lines 743-765). This ensures only users with appropriate privileges can access the sensitive keys.

Successful exploitation allows an authenticated attacker to obtain the OpenAI API keys stored in the plugin’s configuration. This could lead to unauthorized use of the associated OpenAI API quota, potential financial loss, and compromise of any AI-generated content functionality. The exposed keys could also serve as an initial foothold for further attacks if the keys have permissions beyond the BetterDocs plugin scope.

Differential between vulnerable and patched code

Code Diff
--- a/betterdocs/assets/admin/js/dashboard.asset.php
+++ b/betterdocs/assets/admin/js/dashboard.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-url'), 'version' => 'a1731be925b0bc29aedc');
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-url'), 'version' => '4fde91a302feadfdadc1');
--- a/betterdocs/assets/admin/js/faq.asset.php
+++ b/betterdocs/assets/admin/js/faq.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-html-entities', 'wp-i18n'), 'version' => '02d439b839000e443383');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-html-entities', 'wp-i18n'), 'version' => '3df5baa3c8312ecb1e09');
--- a/betterdocs/assets/admin/js/glossaries.asset.php
+++ b/betterdocs/assets/admin/js/glossaries.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-hooks', 'wp-html-entities', 'wp-i18n'), 'version' => '28cdadfaaa88a1531d28');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-hooks', 'wp-html-entities', 'wp-i18n'), 'version' => 'f287b5e9e058aab6634a');
--- a/betterdocs/assets/admin/js/quick-setup.asset.php
+++ b/betterdocs/assets/admin/js/quick-setup.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-hooks', 'wp-i18n', 'wp-media-utils'), 'version' => '1ede0eed022232a36e73');
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-hooks', 'wp-i18n', 'wp-media-utils'), 'version' => 'fb2bc2676c7879eab169');
--- a/betterdocs/assets/admin/js/settings.asset.php
+++ b/betterdocs/assets/admin/js/settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-hooks', 'wp-i18n', 'wp-media-utils'), 'version' => '54a48a4c4f4a7bab22f8');
+<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-hooks', 'wp-i18n', 'wp-media-utils'), 'version' => 'e3047cc013f6141338e9');
--- a/betterdocs/assets/blocks/editor.asset.php
+++ b/betterdocs/assets/blocks/editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => '7f22720ddbd4c103bceb');
+<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => 'f85f3df0887f67fbd338');
--- a/betterdocs/betterdocs.php
+++ b/betterdocs/betterdocs.php
@@ -4,7 +4,7 @@
  * Plugin Name:       BetterDocs
  * Plugin URI:        https://betterdocs.co/
  * Description:       Create stunning Knowledge base & FAQs for your WordPress website and reduce support pressure with the help of BetterDocs. Get access to amazing templates and create fully customizable KB with AI Write.
- * Version:           4.3.3
+ * Version:           4.3.4
  * Author:            WPDeveloper
  * Author URI:        https://wpdeveloper.com
  * License:           GPL-3.0+
--- a/betterdocs/includes/Admin/Customizer/dynamic.css.php
+++ b/betterdocs/includes/Admin/Customizer/dynamic.css.php
@@ -1295,7 +1295,7 @@

 //Single Doc Common Controllers Content Area Padding Top | Right | Bottom | Left
 $css->add_rule(
-	'.betterdocs-wrapper.betterdocs-single-wrapper .betterdocs-content-wrapper',
+	'.betterdocs-wrapper.betterdocs-single-wrapper .betterdocs-content-wrapper,.betterdocs-wrapper.betterdocs-single-wrapper.betterdocs-single-layout-4 .betterdocs-content-wrapper,.betterdocs-wrapper.betterdocs-single-wrapper.betterdocs-single-layout-5 .betterdocs-content-full',
 	$css->properties(
 		[
 			'padding-top'    => 'betterdocs_doc_single_content_area_padding_top',
@@ -1305,18 +1305,6 @@
 		],
 		'px'
 	)
-);
-
-//Single Doc Common Controllers Content Area Padding Top | Right | Bottom | Left
-$css->add_rule(
-	'.betterdocs-wrapper.betterdocs-single-wrapper.betterdocs-single-layout-5 .betterdocs-content-full',
-	$css->properties(
-		[
-			'padding-top'    => 'betterdocs_doc_single_content_area_padding_top',
-			'padding-bottom' => 'betterdocs_doc_single_content_area_padding_bottom'
-		],
-		'px'
-	)
 );

 //Single Doc Common Controllers Content Area Padding Top | Right | Bottom | Left (Layout-3)
--- a/betterdocs/includes/Core/Admin.php
+++ b/betterdocs/includes/Core/Admin.php
@@ -688,32 +688,31 @@
 				'doc_cat_order_nonce'        => wp_create_nonce( 'doc_cat_order_nonce' ),
 				'knowledge_base_order_nonce' => wp_create_nonce( 'knowledge_base_order_nonce' ),
 				'paged'                      => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : 0, // phpcs:ignore WordPress.Security.NonceVerification.Missing
-			'per_page_id'                    => 'edit_doc_category_per_page',
-			'menu_title'                     => __( 'Switch to BetterDocs UI', 'betterdocs' ),
-			'dark_mode'                      => $dark_mode,
-			'text'                           => __( 'Copied!', 'betterdocs' ),
-			'test_report'                    => __( 'Test Report!', 'betterdocs' ),
-			'sending'                        => __( 'Sending...', 'betterdocs' ),
-			'dir_url'                        => BETTERDOCS_ABSURL,
-			'rest_url'                       => esc_url_raw( rest_url() ),
-			'free_version'                   => betterdocs()->version,
-			'generate_data_url'              => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
-			'nonce'                          => wp_create_nonce( 'wp_rest' ),
-            'sync_nonce'                 	 => wp_create_nonce( 'ai_chatbot_embed' ),
-            'count_all_docs'                 => array_sum((array) wp_count_posts('docs')),
-            'count_all_faq'                  => array_sum((array) wp_count_posts('betterdocs_faq')),
-            'count_new_docs'                 => count(get_option('saved_docs_post_ids', [])) + count(get_option('betterdocs_ai_chatbot_error_posts', [])),
-			'admin_url'                      => admin_url(),
-			'ia_preview'                     => betterdocs()->settings->get( 'ia_enable_preview', false ),
-			'multiple_kb'                    => betterdocs()->settings->get( 'multiple_kb' ),
-			'previewMode'                    => betterdocs()->settings->get( 'ia_enable_preview', false ),
-			'dashboard_mode'                 => get_option( 'dashboard_mode' ),
-			'betterdocs_pro_plugin'          => betterdocs()->is_pro_active(),
-			'betterdocs_pro_version'         => betterdocs()->pro_version(),
-			'analytics_older'                => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
-            'disabled_embed_model_option'    => get_option('disabled_embed_model_option'),
-            'betterdocs_ChatBot_plugin'	     => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
-			'total_doc_category_terms' 		 => wp_count_terms( 'doc_category')
+				'per_page_id'                => 'edit_doc_category_per_page',
+				'menu_title'                 => __( 'Switch to BetterDocs UI', 'betterdocs' ),
+				'dark_mode'                  => $dark_mode,
+				'text'                       => __( 'Copied!', 'betterdocs' ),
+				'test_report'                => __( 'Test Report!', 'betterdocs' ),
+				'sending'                    => __( 'Sending...', 'betterdocs' ),
+				'dir_url'                    => BETTERDOCS_ABSURL,
+				'rest_url'                   => esc_url_raw( rest_url() ),
+				'free_version'               => betterdocs()->version,
+				'generate_data_url'          => get_rest_url( null, '/betterdocs/v1/create-sample-docs' ),
+				'nonce'                      => wp_create_nonce( 'wp_rest' ),
+				'sync_nonce'                 => wp_create_nonce( 'ai_chatbot_embed' ),
+				'count_all_docs'             => array_sum((array) wp_count_posts('docs')),
+				'count_all_faq'              => array_sum((array) wp_count_posts('betterdocs_faq')),
+				'count_new_docs'             => count(get_option('saved_docs_post_ids', [])) + count(get_option('betterdocs_ai_chatbot_error_posts', [])),
+				'admin_url'                  => admin_url(),
+				'ia_preview'                 => betterdocs()->settings->get( 'ia_enable_preview', false ),
+				'multiple_kb'                => betterdocs()->settings->get( 'multiple_kb' ),
+				'previewMode'                => betterdocs()->settings->get( 'ia_enable_preview', false ),
+				'dashboard_mode'             => get_option( 'dashboard_mode' ),
+				'betterdocs_pro_plugin'      => betterdocs()->is_pro_active(),
+				'betterdocs_pro_version'     => betterdocs()->pro_version(),
+				'analytics_older'            => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
+				'disabled_embed_model_option' => get_option('disabled_embed_model_option'),
+				'betterdocs_ChatBot_plugin'  => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' )
 			]
 		);

@@ -735,34 +734,42 @@
 		remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
 		remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );

-		betterdocs()->assets->localize(
-			'betterdocs-admin-faq',
-			'betterdocsFaq',
-			[
-				'dir_url'             => BETTERDOCS_ABSURL,
-				'rest_url'            => esc_url_raw( rest_url() ),
-				'free_version'        => betterdocs()->version,
-				'nonce'               => wp_create_nonce( 'wp_rest' ),
-				'betterdocs_settings' => get_option( 'betterdocs_settings', false )
-			]
-		);
-
-		//Glossaries Related Localization
-		betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );

-		betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
+	// Get settings and remove unnecessary keys
+	$betterdocs_settings = get_option( 'betterdocs_settings', false );
+	if ( is_array( $betterdocs_settings ) && ! current_user_can( 'edit_docs_settings' ) ) {
+		unset( $betterdocs_settings['ai_autowrite_api_key'] );
+		unset( $betterdocs_settings['ai_chatbot_api_key'] );
+	}

-		betterdocs()->assets->localize(
-			'betterdocs-admin-glossaries',
-			'betterdocsGlossary',
-			[
-				'dir_url'             => BETTERDOCS_ABSURL,
-				'rest_url'            => esc_url_raw( rest_url() ),
-				'free_version'        => betterdocs()->version,
-				'nonce'               => wp_create_nonce( 'wp_rest' ),
-				'betterdocs_settings' => get_option( 'betterdocs_settings', false )
-			]
-		);
+	betterdocs()->assets->localize(
+		'betterdocs-admin-faq',
+		'betterdocsFaq',
+		[
+			'dir_url'             => BETTERDOCS_ABSURL,
+			'rest_url'            => esc_url_raw( rest_url() ),
+			'free_version'        => betterdocs()->version,
+			'nonce'               => wp_create_nonce( 'wp_rest' ),
+			'betterdocs_settings' => $betterdocs_settings
+		]
+	);
+
+	//Glossaries Related Localization
+	betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/css/faq.css' );
+
+	betterdocs()->assets->enqueue( 'betterdocs-admin-glossaries', 'admin/js/glossaries.js' );
+
+	betterdocs()->assets->localize(
+		'betterdocs-admin-glossaries',
+		'betterdocsGlossary',
+		[
+			'dir_url'             => BETTERDOCS_ABSURL,
+			'rest_url'            => esc_url_raw( rest_url() ),
+			'free_version'        => betterdocs()->version,
+			'nonce'               => wp_create_nonce( 'wp_rest' ),
+			'betterdocs_settings' => $betterdocs_settings
+		]
+	);
 	}

 	/**
--- a/betterdocs/includes/Core/Settings.php
+++ b/betterdocs/includes/Core/Settings.php
@@ -85,11 +85,21 @@

 		wp_enqueue_media();
 		wp_enqueue_script( 'betterdocs-admin' );
-		betterdocs()->assets->localize( 'betterdocs-admin', 'betterdocsAdminSettings', GlobalFields::normalize( $this->settings_args() ) );
+
+		$settings = GlobalFields::normalize( $this->settings_args() );
+
+		// Remove sensitive API keys if user doesn't have permission to edit settings
+		if ( ! current_user_can( 'edit_docs_settings' ) ) {
+			if ( isset( $settings['values']['ai_autowrite_api_key'] ) ) {
+				unset( $settings['values']['ai_autowrite_api_key'] );
+			}
+			if ( isset( $settings['values']['ai_chatbot_api_key'] ) ) {
+				unset( $settings['values']['ai_chatbot_api_key'] );
+			}
+		}
+
+		betterdocs()->assets->localize( 'betterdocs-admin', 'betterdocsAdminSettings', $settings );
 		betterdocs()->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
-
-		// betterdocs()->assets->enqueue( 'betterdocs-settings', 'admin/css/settings.css' );
-		// betterdocs()->assets->enqueue( 'betterdocs-settings', 'admin/js/settings.js' );
 	}

 	public function enqueue_old( $hook ) {
@@ -2272,45 +2282,6 @@
 						]
 					]
 				] ),
-				// 'tab-ai-chatbot'       => [
-				// 	'id'       => 'tab-ai-chatbot',
-				// 	'name'     => 'tab-ai-chatbot',
-				// 	'type'     => 'section',
-				// 	'label'    => __( 'AI Chatbot', 'betterdocs' ),
-				// 	// 'is_pro'   => ! defined('BETTERDOCS_PRO_VERSION'),
-				// 	'priority' => 76,
-				// 	'save'     => false,
-				// 	'submit'   => [
-				// 		'show' => false
-				// 	],
-
-				// 	'fields' => [
-				// 		'title-ai-chatbot' => apply_filters( 'betterdocs_settings_ai_chatbot_fields', [
-				// 			'name'     => 'title-ai-chatbot-tab',
-				// 			'type'     => 'section',
-				// 			'label'    => __( 'AI Chatbot', 'betterdocs' ),
-				// 			'priority' => 60,
-				// 			'save'     => false,
-				// 			'submit'   => [
-				// 				'show' => false
-				// 			],
-
-				// 			'fields' => [
-				// 				'enable_ai_chatbot' => [
-				// 					'name'              => 'enable_ai_chatbot',
-				// 					'label'             => __( 'AI Chatbot', 'betterdocs' ),
-				// 					'description'       => __( 'Enable AI Chatbot', 'betterdocs' ),
-				// 					'type'              => 'toggle',
-				// 					'priority'          => 5,
-				// 					'default'           => 0,
-				// 					'is_pro'            => true,
-				// 					'is_license_active' => false,
-				// 					'disabled'          => ! betterdocs()->is_pro_active() || ! defined( 'BETTERDOCS_CHATBOT_FILE' )
-				// 				],
-				// 			]
-				// 		] ),
-				// 	]
-				// ],
 			] ),
 			'TAB_AI_CHATBOT'  => get_option( 'enable_ai_chatbot' ),
 			'CHATBOT_LICENSE' => get_option( 'betterdocs_chatbot_software__license_status' ),
--- a/betterdocs/includes/Core/WriteWithAI.php
+++ b/betterdocs/includes/Core/WriteWithAI.php
@@ -233,12 +233,6 @@
 									insertContentToEditor(title, response.data, isOverwrite, keywords);
 									docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
 									jQuery('.generate-btn').removeAttr('disabled');
-
-									// console.log(jQuery('.betterdocs-ai-autowrite-form-container').data('new-doc-page'));
-
-									// if(jQuery('.betterdocs-ai-autowrite-form-container')?.data('new-doc-page')) {
-									//     docGenerateBtnTxt.innerHTML = reGenerateDocLabel;
-									// }
 								}, 2000);
 							} else {
 								// alert(response.data.message);
@@ -325,7 +319,6 @@

 				// Check if match is null before accessing match[1]
 				if (match) {
-					console.log(match[1].replace(/<p>s+/g, '<p>'));
 					return match[1].replace(/<p>s+/g, '<p>');
 				} else {
 					return '';
--- a/betterdocs/includes/Plugin.php
+++ b/betterdocs/includes/Plugin.php
@@ -125,7 +125,7 @@
 	 * Plugin Version
 	 * @var string
 	 */
-	public $version = '4.3.3';
+	public $version = '4.3.4';

 	/**
 	 * WriteWithAI Class

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.

 

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