Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/tutor/classes/Quiz.php
+++ b/tutor/classes/Quiz.php
@@ -283,9 +283,9 @@
$attempt_details = self::attempt_details( Input::post( 'attempt_id', 0, Input::TYPE_INT ) );
$feedback = Input::post( 'feedback', '', Input::TYPE_KSES_POST );
$attempt_info = isset( $attempt_details->attempt_info ) ? $attempt_details->attempt_info : false;
- $course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_info, 0 );
-
- if ( ! tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id ) ) {
+ $course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_details, 0 );
+ $is_instructor = tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id );
+ if ( ! current_user_can( 'manage_options' ) && ! $is_instructor ) {
wp_send_json_error( tutor_utils()->error_message() );
}
--- a/tutor/classes/User.php
+++ b/tutor/classes/User.php
@@ -245,7 +245,9 @@
private function delete_existing_user_photo( $user_id, $type ) {
$meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
$photo_id = get_user_meta( $user_id, $meta_key, true );
- is_numeric( $photo_id ) ? wp_delete_attachment( $photo_id, true ) : 0;
+ if ( is_numeric( $photo_id ) ) {
+ wp_delete_attachment( $photo_id, true );
+ }
delete_user_meta( $user_id, $meta_key );
}
@@ -281,7 +283,7 @@
/**
* Photo Update from profile
*/
- $photo = tutor_utils()->array_get( 'photo_file', $_FILES );
+ $photo = tutor_utils()->array_get( 'photo_file', $_FILES ); //phpcs:ignore -- already sanitized.
$photo_size = tutor_utils()->array_get( 'size', $photo );
$photo_type = tutor_utils()->array_get( 'type', $photo );
@@ -373,6 +375,14 @@
$_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
$_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
+ if ( is_numeric( $_tutor_profile_image ) ) {
+ $attachment = get_post( $_tutor_profile_image );
+
+ if ( 'attachment' === $attachment->post_type && $user_id !== $attachment->post_author ) {
+ return;
+ }
+ }
+
update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );
--- a/tutor/classes/Utils.php
+++ b/tutor/classes/Utils.php
@@ -4066,11 +4066,13 @@
WHERE comments.comment_post_ID = %d
AND comments.comment_type = %s
AND commentmeta.meta_key = %s
+ AND comments.comment_approved = %s
GROUP BY CAST(commentmeta.meta_value AS SIGNED);
",
$course_id,
'tutor_course_rating',
- 'tutor_rating'
+ 'tutor_rating',
+ 'approved'
)
);
--- a/tutor/models/CartModel.php
+++ b/tutor/models/CartModel.php
@@ -73,13 +73,19 @@
);
}
+ if ( 'gift' === $item_type ) {
+ $item_details = wp_json_encode( $item_details, JSON_UNESCAPED_UNICODE );
+ } else {
+ $item_details = wp_json_encode( $item_details );
+ }
+
return QueryHelper::insert(
"{$wpdb->prefix}tutor_cart_items",
array(
'cart_id' => $user_cart_id,
'course_id' => $course_id,
'item_type' => $item_type,
- 'item_details' => $item_details ? wp_json_encode( $item_details ) : null,
+ 'item_details' => $item_details,
)
);
}
@@ -261,5 +267,4 @@
)
);
}
-
}
--- a/tutor/templates/single/course/reviews.php
+++ b/tutor/templates/single/course/reviews.php
@@ -25,7 +25,7 @@
$current_user_id = get_current_user_id();
$course_id = Input::post( 'course_id', get_the_ID(), Input::TYPE_INT );
$reviews = tutor_utils()->get_course_reviews( $course_id, $offset, $per_page, false, array( 'approved' ), $current_user_id );
-$reviews_total = tutor_utils()->get_course_reviews( $course_id, null, null, true, array( 'approved' ), $current_user_id );
+$reviews_total = tutor_utils()->get_course_reviews( $course_id, null, null, true, array( 'approved' ) );
$my_rating = tutor_utils()->get_reviews_by_user( 0, 0, 150, false, $course_id, array( 'approved', 'hold' ) );
if ( Input::has( 'course_id' ) ) {
--- a/tutor/tutor.php
+++ b/tutor/tutor.php
@@ -4,7 +4,7 @@
* Plugin URI: https://tutorlms.com
* Description: Tutor is a complete solution for creating a Learning Management System in WordPress way. It can help you to create small to large scale online education site very conveniently. Power features like report, certificate, course preview, private file sharing make Tutor a robust plugin for any educational institutes.
* Author: Themeum
- * Version: 3.9.4
+ * Version: 3.9.5
* Author URI: https://themeum.com
* Requires PHP: 7.4
* Requires at least: 5.3
@@ -26,7 +26,7 @@
*
* @since 1.0.0
*/
-define( 'TUTOR_VERSION', '3.9.4' );
+define( 'TUTOR_VERSION', '3.9.5' );
define( 'TUTOR_FILE', __FILE__ );
/**
--- a/tutor/vendor/composer/installed.php
+++ b/tutor/vendor/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'themeum/tutor',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '043bcc9b76cd1e56219d167b8be313b5aa933109',
+ 'reference' => 'ad35941bc49eab600939a865a136e4a05cf217f8',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -13,7 +13,7 @@
'themeum/tutor' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '043bcc9b76cd1e56219d167b8be313b5aa933109',
+ 'reference' => 'ad35941bc49eab600939a865a136e4a05cf217f8',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
--- a/tutor/views/pages/view_attempt.php
+++ b/tutor/views/pages/view_attempt.php
@@ -16,12 +16,13 @@
use TUTORInput;
use TutorModelsQuizModel;
-$attempt_id = Input::get( 'view_quiz_attempt_id', 0, Input::TYPE_INT );
-$attempt = tutor_utils()->get_attempt( $attempt_id );
-$attempt_data = $attempt;
-$user_id = tutor_utils()->avalue_dot( 'user_id', $attempt_data );
-$quiz_id = $attempt && isset( $attempt->quiz_id ) ? $attempt->quiz_id : 0;
-$course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_data );
+$attempt_id = Input::get( 'view_quiz_attempt_id', 0, Input::TYPE_INT );
+$attempt = tutor_utils()->get_attempt( $attempt_id );
+$attempt_data = $attempt;
+$user_id = tutor_utils()->avalue_dot( 'user_id', $attempt_data );
+$quiz_id = $attempt && isset( $attempt->quiz_id ) ? $attempt->quiz_id : 0;
+$course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_data );
+$is_instructor = tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id );
if ( ! $attempt ) {
tutor_utils()->tutor_empty_state( __( 'Attempt not found', 'tutor' ) );
return;
@@ -31,8 +32,8 @@
return;
}
-if ( ! tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id ) ) {
- tutor_utils()->tutor_empty_state();
+if ( ! current_user_can( 'manage_options' ) && ! $is_instructor ) {
+ tutor_utils()->tutor_empty_state( __( 'Access denied!', 'tutor' ) );
return;
}