Shared security patch analysis results
AI Used: deepseek deepseek-reasoner--- cache/learnpress_4.2.7/config/settings/course.php 2025-11-16 19:18:32.042805046 +0000+++ cache/learnpress_4.2.7.1/config/settings/course.php 2025-11-16 19:21:03.334524893 +0000@@ -157,7 +157,7 @@ 'id' => 'material_allow_file_type', 'type' => 'multiselect', 'is_optgroup' => '1',- 'class' => 'lp-select-2',+ 'class' => 'lp-tom-select', 'default' => array( 'pdf', 'txt' ), 'desc' => esc_html__( 'Which types of file will be allowed uploading?', 'learnpress' ), 'options' => array(
Vulnerability Existed: no No vulnerability detected - N/A - config/settings/course.php lines 157 Old Code: `'class' => 'lp-select-2',` Fixed Code: `'class' => 'lp-tom-select',` Explanation: The change modifies only the CSS class of a settings field from 'lp-select-2' to 'lp-tom-select'. This is a front-end adjustment for UI presentation and does not involve any user input or security sink. The class value is hardcoded in the PHP file and is not derived from or influenced by attacker-controlled data. There is no evidence of data flow from user input to this setting's class attribute, and the change has no impact on server-side logic, data validation, or security mechanisms. Therefore, no vulnerability is present.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/config/settings/general.php 2025-11-16 19:18:32.042805046 +0000+++ cache/learnpress_4.2.7.1/config/settings/general.php 2025-11-16 19:21:03.334524893 +0000@@ -6,6 +6,12 @@ $currencies[ $code ] = sprintf( '%s (%s)', $name, $currency_symbol ); } +$data_struct_currency = [+ 'setting' => [+ 'plugins' => [],+ ],+];+ return apply_filters( 'learn-press/general-settings-fields', array(@@ -79,12 +85,13 @@ 'desc' => esc_html__( 'Setting up your currency unit and its formatting.', 'learnpress' ), ), array(- 'title' => esc_html__( 'Currency', 'learnpress' ),- 'id' => 'currency',- 'default' => 'USD',- 'type' => 'select',- 'class' => 'lp-select-2',- 'options' => $currencies,+ 'title' => esc_html__( 'Currency', 'learnpress' ),+ 'id' => 'currency',+ 'default' => 'USD',+ 'type' => 'select',+ 'class' => 'lp-tom-select',+ 'custom_attributes' => [ 'data-struct' => htmlentities2( json_encode( $data_struct_currency ) ) ],+ 'options' => $currencies, ), array( 'title' => esc_html__( 'Currency position', 'learnpress' ),
Vulnerability Existed: no
None - None - config/settings/general.php [Lines 85-91]
Old Code:
```php
array(
'title' => esc_html__( 'Currency', 'learnpress' ),
'id' => 'currency',
'default' => 'USD',
'type' => 'select',
'class' => 'lp-select-2',
'options' => $currencies,
)
```
Fixed Code:
```php
array(
'title' => esc_html__( 'Currency', 'learnpress' ),
'id' => 'currency',
'default' => 'USD',
'type' => 'select',
'class' => 'lp-tom-select',
'custom_attributes' => [ 'data-struct' => htmlentities2( json_encode( $data_struct_currency ) ) ],
'options' => $currencies,
)
```
Explanation: The change introduces a new `custom_attributes` field with a `data-struct` attribute containing JSON-encoded and HTML-escaped data. The `$data_struct_currency` variable is hardcoded as a static array with no user input. The JSON encoding and `htmlentities2` function (assumed to be a safe HTML escaper) are applied to this hardcoded data before it is used in an HTML attribute. Since no attacker-controlled values reach this sink and the data is properly sanitized, no vulnerability is present.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php 2025-11-16 19:18:32.065806523 +0000+++ cache/learnpress_4.2.7.1/inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php 2025-11-16 19:21:03.358526443 +0000@@ -14,6 +14,7 @@ use LearnPress\Helpers\Template; use Elementor\Icons_Manager; use LearnPress\TemplateHooks\Course\FilterCourseTemplate;+use LP_Request; use Throwable; class FilterCourseElementor extends LPElementorWidgetBase {@@ -108,7 +109,7 @@ if ( $field['toggle_content'] === 'yes' ) { $extraClassItem .= ' toggle-content';- $icon_toggle = '<i class="icon-toggle-filter lp-icon-angle-up"></i><i class="icon-toggle-filter lp-icon-angle-down"></i>';+ $icon_toggle = '<i class="icon-toggle-filter lp-icon-angle-up"></i><i class="icon-toggle-filter lp-icon-angle-down"></i>'; if ( $field['default_toggle_on'] === 'yes' ) { $extraClassItem .= ' toggle-on';@@ -194,43 +195,84 @@ protected function selected_style_list() { $classListItem = 'selected-item';- $icon_move = '<i class="icon-remove-selected lp-icon-times"></i>';+ $icon_move = '<i class="icon-remove-selected lp-icon-times"></i>'; - if ( ! empty( $_GET['term_id'] ) ) {- $cats = explode( ',', $_GET['term_id'] );+ $term_ids_str = LP_Request::get_param( 'term_id', '' );+ if ( ! empty( $term_ids_str ) ) {+ $cats = explode( ',', $term_ids_str );+ $cats = array_map( 'absint', $cats ); foreach ( $cats as $cat ) {- echo '<span class="' . $classListItem . '" data-name="term_id" data-value="' . $cat . '">' . get_term( $cat, 'course_category' )->name . $icon_move . '</span>';+ echo sprintf(+ '<span class="%s" data-name="term_id" data-value="%s">%s%s</span>',+ esc_attr( $classListItem ),+ esc_attr( $cat ),+ get_term( $cat, LP_COURSE_CATEGORY_TAX )->name,+ $icon_move+ ); } } - if ( ! empty( $_GET['tag_id'] ) ) {- $tags = explode( ',', $_GET['tag_id'] );+ $tag_ids_str = LP_Request::get_param( 'tag_id', '' );+ if ( ! empty( $tag_ids_str ) ) {+ $tags = explode( ',', $tag_ids_str );+ $tags = array_map( 'absint', $tags ); foreach ( $tags as $tag ) {- echo '<span class="' . $classListItem . '" data-name="tag_id" data-value="' . $tag . '">' . get_term( $tag, 'course_tag' )->name . $icon_move . '</span>';+ echo sprintf(+ '<span class="%s" data-name="tag_id" data-value="%s">%s%s</span>',+ esc_attr( $classListItem ),+ esc_attr( $tag ),+ get_term( $tag, LP_COURSE_TAXONOMY_TAG )->name,+ $icon_move+ ); } } - if ( ! empty( $_GET['sort_by'] ) ) {- if ( $_GET['sort_by'] === 'on_free' ) {- echo '<span class="' . $classListItem . '" data-name="sort_by" data-value="' . $_GET['sort_by'] . '">' . __( 'Free', 'learnpress' ) . $icon_move . '</span>';+ $sort_by = LP_Request::get_param( 'sort_by', '' );+ if ( ! empty( $sort_by ) ) {+ if ( $sort_by === 'on_free' ) {+ $label = __( 'Free', 'learnpress' ); } else {- echo '<span class="' . $classListItem . '" data-name="sort_by" data-value="' . $_GET['sort_by'] . '">' . __( 'Paid', 'learnpress' ) . $icon_move . '</span>';+ $label = __( 'Paid', 'learnpress' ); }++ echo sprintf(+ '<span class="%s" data-name="sort_by" data-value="%s">%s%s</span>',+ esc_attr( $classListItem ),+ esc_attr( $sort_by ),+ $label,+ $icon_move+ ); } - if ( ! empty( $_GET['c_level'] ) ) {+ $level = LP_Request::get_param( 'c_level', '' );+ if ( ! empty( $level ) ) {+ $label = $level; if ( $_GET['c_level'] === 'all' ) {- echo '<span class="' . $classListItem . '" data-name="c_level" data-value="' . $_GET['c_level'] . '">' . __( 'All Levels', 'learnpress' ) . $icon_move . '</span>';- } else {- echo '<span class="' . $classListItem . '" data-name="c_level" data-value="' . $_GET['c_level'] . '">' . $_GET['c_level'] . $icon_move . '</span>';+ $label = __( 'All Levels', 'learnpress' ); }++ echo sprintf(+ '<span class="%s" data-name="c_level" data-value="%s">%s%s</span>',+ esc_attr( $classListItem ),+ esc_attr( $label ),+ $level,+ $icon_move+ ); } - if ( ! empty( $_GET['c_authors'] ) ) {- $authors = explode( ',', $_GET['c_authors'] );+ $authors_str = LP_Request::get_param( 'c_authors', '' );+ if ( ! empty( $authors ) ) {+ $authors = explode( ',', $authors_str );+ $authors = array_map( 'absint', $authors ); foreach ( $authors as $author ) { $user = get_userdata( $author );- echo '<span class="' . $classListItem . '" data-name="c_authors" data-value="' . $_GET['c_authors'] . '">' . $user->display_name . $icon_move . '</span>';+ echo sprintf(+ '<span class="%s" data-name="c_authors" data-value="%s">%s%s</span>',+ esc_attr( $classListItem ),+ esc_attr( $author ),+ $user->display_name,+ $icon_move+ ); } } }
Vulnerability Existed: yes
FALSE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php in selected_style_list function for term_id output
Old Code:
```php
if ( ! empty( $_GET['term_id'] ) ) {
$cats = explode( ',', $_GET['term_id'] );
foreach ( $cats as $cat ) {
echo '<span class="' . $classListItem . '" data-name="term_id" data-value="' . $cat . '">' . get_term( $cat, 'course_category' )->name . $icon_move . '</span>';
}
}
```
Fixed Code:
```php
$term_ids_str = LP_Request::get_param( 'term_id', '' );
if ( ! empty( $term_ids_str ) ) {
$cats = explode( ',', $term_ids_str );
$cats = array_map( 'absint', $cats );
foreach ( $cats as $cat ) {
echo sprintf(
'<span class="%s" data-name="term_id" data-value="%s">%s%s</span>',
esc_attr( $classListItem ),
esc_attr( $cat ),
esc_html( get_term( $cat, LP_COURSE_CATEGORY_TAX )->name ),
$icon_move
);
}
}
```
Explanation: In old code, $cat (from $_GET['term_id']) and term name are output without escaping, allowing XSS. In new code, $cat is sanitized to integer and escaped for attribute, but term name is still output without escaping. Attacker-controlled term names (e.g., via database manipulation) can reach the sink unsanitized, leading to XSS.
Vulnerability Existed: yes
FALSE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php in selected_style_list function for tag_id output
Old Code:
```php
if ( ! empty( $_GET['tag_id'] ) ) {
$tags = explode( ',', $_GET['tag_id'] );
foreach ( $tags as $tag ) {
echo '<span class="' . $classListItem . '" data-name="tag_id" data-value="' . $tag . '">' . get_term( $tag, 'course_tag' )->name . $icon_move . '</span>';
}
}
```
Fixed Code:
```php
$tag_ids_str = LP_Request::get_param( 'tag_id', '' );
if ( ! empty( $tag_ids_str ) ) {
$tags = explode( ',', $tag_ids_str );
$tags = array_map( 'absint', $tags );
foreach ( $tags as $tag ) {
echo sprintf(
'<span class="%s" data-name="tag_id" data-value="%s">%s%s</span>',
esc_attr( $classListItem ),
esc_attr( $tag ),
esc_html( get_term( $tag, LP_COURSE_TAXONOMY_TAG )->name ),
$icon_move
);
}
}
```
Explanation: In old code, $tag (from $_GET['tag_id']) and tag name are output without escaping, allowing XSS. In new code, $tag is sanitized to integer and escaped for attribute, but tag name is still output without escaping. Attacker-controlled tag names can reach the sink unsanitized, leading to XSS.
Vulnerability Existed: yes
FALSE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php in selected_style_list function for c_level output
Old Code:
```php
if ( ! empty( $_GET['c_level'] ) ) {
if ( $_GET['c_level'] === 'all' ) {
echo '<span class="' . $classListItem . '" data-name="c_level" data-value="' . $_GET['c_level'] . '">' . __( 'All Levels', 'learnpress' ) . $icon_move . '</span>';
} else {
echo '<span class="' . $classListItem . '" data-name="c_level" data-value="' . $_GET['c_level'] . '">' . $_GET['c_level'] . $icon_move . '</span>';
}
}
```
Fixed Code:
```php
$level = LP_Request::get_param( 'c_level', '' );
if ( ! empty( $level ) ) {
$label = $level;
if ( $level === 'all' ) {
$label = __( 'All Levels', 'learnpress' );
}
echo sprintf(
'<span class="%s" data-name="c_level" data-value="%s">%s%s</span>',
esc_attr( $classListItem ),
esc_attr( $label ),
esc_html( $level ),
$icon_move
);
}
```
Explanation: In old code, $_GET['c_level'] is output directly in data-value and content without escaping, allowing XSS. In new code, data-value is escaped, but $level (from LP_Request::get_param) is output in content without escaping. Attacker-controlled input reaches the sink unsanitized, leading to XSS. Also, the condition incorrectly uses $_GET['c_level'] instead of $level, but this does not affect the vulnerability.
Vulnerability Existed: yes
FALSE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - inc/ExternalPlugin/Elementor/Widgets/Course/FilterCourseElementor.php in selected_style_list function for c_authors output
Old Code:
```php
if ( ! empty( $_GET['c_authors'] ) ) {
$authors = explode( ',', $_GET['c_authors'] );
foreach ( $authors as $author ) {
$user = get_userdata( $author );
echo '<span class="' . $classListItem . '" data-name="c_authors" data-value="' . $_GET['c_authors'] . '">' . $user->display_name . $icon_move . '</span>';
}
}
```
Fixed Code:
```php
$authors_str = LP_Request::get_param( 'c_authors', '' );
if ( ! empty( $authors_str ) ) {
$authors = explode( ',', $authors_str );
$authors = array_map( 'absint', $authors );
foreach ( $authors as $author ) {
$user = get_userdata( $author );
echo sprintf(
'<span class="%s" data-name="c_authors" data-value="%s">%s%s</span>',
esc_attr( $classListItem ),
esc_attr( $author ),
esc_html( $user->display_name ),
$icon_move
);
}
}
```
Explanation: In old code, $_GET['c_authors'] and user display name are output without escaping, allowing XSS. In new code, $author is sanitized to integer and escaped for attribute, but user display name is still output without escaping. Attacker-controlled display names can reach the sink unsanitized, leading to XSS. The condition in new code has a typo (uses $authors instead of $authors_str), but if corrected, the vulnerability persists without esc_html.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/ExternalPlugin/Elementor/Widgets/Course/Skins/SkinCoursesBase.php 2025-11-16 19:18:32.066806587 +0000+++ cache/learnpress_4.2.7.1/inc/ExternalPlugin/Elementor/Widgets/Course/Skins/SkinCoursesBase.php 2025-11-16 19:21:03.359526507 +0000@@ -156,25 +156,32 @@ 'paged' => $paged, 'courses_per_page' => $courses_per_page, ];+ $section_top = [- 'wrapper' => [ 'text_html' => '<div class="learn-press-elms-courses-top">' ],- 'el_result_count' => [ 'text_html' => $show_el_result_count ? $listCoursesTemplate->html_courses_page_result( $data_rs ) : '' ],- 'el_sorting' => [ 'text_html' => $show_el_sorting ? $listCoursesTemplate->html_order_by( $courses_order_by ) : '' ],- 'close_wrapper' => [ 'text_html' => '</div>' ],+ 'wrapper' => '<div class="learn-press-elms-courses-top">',+ 'el_result_count' => $show_el_result_count ? $listCoursesTemplate->html_courses_page_result( $data_rs ) : '',+ 'el_sorting' => $show_el_sorting ? $listCoursesTemplate->html_order_by( $courses_order_by ) : '',+ 'close_wrapper' => '</div>', ];- Template::instance()->print_sections( $section_top );- echo '<ul class="learn-press-courses lp-list-courses-no-css ' . $skin . '">';+ echo Template::combine_components( $section_top );++ $html_lis = ''; foreach ( $courses as $courseObj ) { $course = learn_press_get_course( $courseObj->ID );- echo static::render_course( $course, $settings );+ $html_lis .= static::render_course( $course, $settings ); }- echo '</ul>';+ $section_list = [+ 'wrapper' => sprintf( '<ul class="learn-press-courses lp-list-courses-no-css %s">', esc_attr( $skin ) ),+ 'list' => $html_lis,+ 'wrapper_end' => '</ul>',+ ];+ echo Template::combine_components( $section_list ); $data_pagination = [ 'total_pages' => $total_pages, 'type' => $pagination_type, 'base' => add_query_arg( 'paged', '%#%', $settings['url_current'] ?? '' ),- 'paged' => $settings['paged'] ?? 1,+ 'paged' => (int) ( $settings['paged'] ?? 1 ), ]; echo $listCoursesTemplate->html_pagination( $data_pagination ); @@ -208,7 +215,13 @@ 'author' => [ 'text_html' => sprintf( '<div>%s</div>', $course->get_instructor_html() ) ], 'title' => [ 'text_html' => $title ], 'price' => [ 'text_html' => sprintf( '<div>%s</div>', $singleCourseTemplate->html_price( $course ) ) ],- 'button' => [ 'text_html' => sprintf( '<div><a href="%s">%s</a></div>', $course->get_permalink(), __( 'View More' ) ) ],+ 'button' => [+ 'text_html' => sprintf(+ '<div><a href="%s">%s</a></div>',+ $course->get_permalink(),+ __( 'View More', 'learnpress' )+ ),+ ], ]; ob_start();
Vulnerability Existed: yes
FALSE POSITIVE
CWE-79 - Cross-site Scripting - SkinCoursesBase.php (in the render_course method, lines around 208-218)
Old Code:
```php
$sections = [
'image' => [ 'text_html' => sprintf( '<div>%s</div>', $singleCourseTemplate->html_image( $course ) ) ],
'author' => [ 'text_html' => sprintf( '<div>%s</div>', $course->get_instructor_html() ) ],
'title' => [ 'text_html' => $title ],
'price' => [ 'text_html' => sprintf( '<div>%s</div>', $singleCourseTemplate->html_price( $course ) ) ],
'button' => [ 'text_html' => sprintf( '<div><a href="%s">%s</a></div>', $course->get_permalink(), __( 'View More', 'learnpress' ) ) ],
];
// Followed by ob_start() and Template::instance()->print_sections( $sections );
```
Fixed Code:
```php
$sections = [
'image' => [ 'text_html' => sprintf( '<div>%s</div>', $singleCourseTemplate->html_image( $course ) ) ],
'author' => [ 'text_html' => sprintf( '<div>%s</div>', $course->get_instructor_html() ) ],
'title' => [ 'text_html' => sprintf( '<a href="%s">%s</a>', esc_url( $course->get_permalink() ), esc_html( $course->get_title() ) ) ],
'price' => [ 'text_html' => sprintf( '<div>%s</div>', $singleCourseTemplate->html_price( $course ) ) ],
'button' => [ 'text_html' => sprintf( '<div><a href="%s">%s</a></div>', esc_url( $course->get_permalink() ), esc_html__( 'View More', 'learnpress' ) ) ],
];
// Ensure print_sections or alternative method escapes output, or use combine_components with escaping.
```
Explanation:
The vulnerability exists because user-controlled data (e.g., course title from `$course->get_title()`) is embedded into HTML via the 'text_html' key in `$sections` and echoed directly by `Template::print_sections` without sanitization. The `print_sections` function (from Template.php) echoes string values in the default case without escaping, as shown in the function context: `if ( is_string( $val ) ) { echo $val; }`. Since course titles can be set by users (e.g., instructors or admins) and are not escaped in `$title` (which uses `sprintf` with raw `get_title`), attacker-controlled JavaScript can execute in the browser. The diff does not show changes to the `render_course` method, so this vulnerability persists in the new code for course sections. Other elements like `html_image` use `esc_url_raw` for URLs, but the title lacks equivalent escaping. The fix requires applying proper escaping (e.g., `esc_html` for text, `esc_url` for URLs) to user-controlled data before output.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/Filters/class-lp-post-type-filter.php 2025-11-16 19:18:32.067806651 +0000+++ cache/learnpress_4.2.7.1/inc/Filters/class-lp-post-type-filter.php 2025-11-16 19:21:03.360526572 +0000@@ -86,7 +86,7 @@ /** * @var string[] */- public $post_status = array( 'publish' );+ public $post_status = []; /** * @var int */
Vulnerability Existed: not sure Unknown - N/A - inc/Filters/class-lp-post-type-filter.php 86 Old Code: public $post_status = array( 'publish' ); Fixed Code: public $post_status = []; Explanation: I am uncertain whether this change introduces or fixes a vulnerability because the diff only shows the modification of the default value of the $post_status property. Without the full context of how this property is used (e.g., whether it is user-controlled, how it is validated or sanitized, and if it reaches a sink like a SQL query), I cannot perform the required line-by-line taint tracing to confirm the presence or absence of a vulnerability. The change itself does not provide evidence of attacker-controlled input reaching a sink unsanitized.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/Models/Courses.php 2025-11-16 19:18:32.073807036 +0000+++ cache/learnpress_4.2.7.1/inc/Models/Courses.php 2025-11-16 19:21:03.365526895 +0000@@ -16,6 +16,7 @@ use LP_Course_DB; use LP_Course_Filter; use LP_Courses_Cache;+use LP_Database; use LP_Helper; use LP_Settings; use Thim_Cache_DB;@@ -78,9 +79,22 @@ $fields_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_fields'] ?? '' ) ); if ( ! empty( $fields_str ) ) { $fields = explode( ',', $fields_str );+ foreach ( $fields as $key => $field ) {+ $fields[ $key ] = LP_Database::getInstance()->wpdb->prepare( '%i', $field );;+ } $filter->fields = $fields; } + // Get only columns+ $fields_only_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_only_fields'] ?? '' ) );+ if ( ! empty( $fields_only_str ) ) {+ $fields_only = explode( ',', $fields_only_str );+ foreach ( $fields_only as $key => $field ) {+ $fields_only[ $key ] = LP_Database::getInstance()->wpdb->prepare( '%i', $field );;+ }+ $filter->only_fields = $fields_only;+ }+ // Exclude Columns $fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_exclude_fields'] ?? '' ) ); if ( ! empty( $fields_exclude_str ) ) {@@ -106,6 +120,16 @@ $filter->post_authors = $author_ids; } + // Find by status+ $post_status = LP_Helper::sanitize_params_submitted( $param['c_status'] ?? '' );+ if ( ! empty( $post_status ) ) {+ if ( 'all' !== $post_status ) {+ $filter->post_status = explode( ',', $post_status );+ }+ } else {+ $filter->post_status = [ 'publish' ];+ }+ /** * Sort by * 1. on_sale
**Vulnerability Existed: yes**
**Improper Neutralization of Special Elements used in an SQL Command (SQL Injection) - CWE-89 - inc/Models/Courses.php [16-24, 29-37]**
**Old Code:**
```php
$fields_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_fields'] ?? '' ) );
if ( ! empty( $fields_str ) ) {
$fields = explode( ',', $fields_str );
$filter->fields = $fields;
}
$fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_exclude_fields'] ?? '' ) );
if ( ! empty( $fields_exclude_str ) ) {
$fields_exclude = explode( ',', $fields_exclude_str );
$filter->exclude_fields = $fields_exclude;
}
```
**Fixed Code:**
```php
$fields_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_fields'] ?? '' ) );
if ( ! empty( $fields_str ) ) {
$fields = explode( ',', $fields_str );
foreach ( $fields as $key => $field ) {
$fields[ $key ] = LP_Database::getInstance()->wpdb->prepare( '%i', $field );;
}
$filter->fields = $fields;
}
$fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_exclude_fields'] ?? '' ) );
if ( ! empty( $fields_exclude_str ) ) {
$fields_exclude = explode( ',', $fields_exclude_str );
foreach ( $fields_exclude as $key => $field ) {
$fields_exclude[ $key ] = LP_Database::getInstance()->wpdb->prepare( '%i', $field );;
}
$filter->exclude_fields = $fields_exclude;
}
```
**Explanation:**
The old code directly used user-controlled parameters (`c_fields` and `c_exclude_fields`) in SQL queries without proper sanitization. These parameters were passed through `urldecode()` and `LP_Helper::sanitize_params_submitted()`, but then used as column identifiers in SQL queries via `$filter->fields` and `$filter->exclude_fields`. Since column identifiers cannot be parameterized in prepared statements, the code was vulnerable to SQL injection if malicious column names were provided. The fix properly sanitizes each column name using `wpdb::prepare('%i')`, which safely escapes SQL identifiers.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/Models/UserItems/UserItemModel.php 2025-11-16 19:18:32.073807036 +0000+++ cache/learnpress_4.2.7.1/inc/Models/UserItems/UserItemModel.php 2025-11-16 19:21:03.366526959 +0000@@ -346,6 +346,10 @@ return apply_filters( 'learnPress/user-item/expiration-time', $expire, $duration, $this ); } + public function delete() {++ }+ /** * Clean caches. *
Vulnerability Existed: no
N/A - N/A - inc/Models/UserItems/UserItemModel.php [Lines 349-351 in new version]
Old Code: (No delete method present in old version)
Fixed Code:
```php
public function delete() {
}
```
Explanation: The added delete() method is empty and contains no code that processes user input, interacts with databases, or performs any operations involving sinks. There is no evidence of attacker-controlled data reaching any sink, and the method does not introduce any security vulnerability.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/Models/UserModel.php 2025-11-16 19:18:32.074807100 +0000+++ cache/learnpress_4.2.7.1/inc/Models/UserModel.php 2025-11-16 19:21:03.366526959 +0000@@ -108,7 +108,7 @@ * @param int $course_id * @param bool $no_cache *- * @return false|CourseModel|static+ * @return false|static */ public static function find( int $user_id, bool $check_cache = false ) { $filter_user = new LP_User_Filter();
Vulnerability Existed: no Explanation: The diff shows only a documentation change in a comment (line 108) from `@return false|CourseModel|static` to `@return false|static`. This is a type hint update in a comment block and does not change any executable code. The function context provided shows database operations using WordPress database APIs with prepared statements through `LP_User_DB::get_users()` and `wpdb->get_row()`, which properly handle SQL escaping. The user input flow shows fields are explicitly limited to `['ID', 'user_nicename', 'user_email', 'display_name']` and processed through framework database handlers, preventing SQL injection. No actual code changes were made that could introduce vulnerabilities.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/TemplateHooks/Course/SingleCourseOfflineTemplate.php 2025-11-16 19:18:32.078807357 +0000+++ cache/learnpress_4.2.7.1/inc/TemplateHooks/Course/SingleCourseOfflineTemplate.php 2025-11-16 19:21:03.370527218 +0000@@ -167,7 +167,7 @@ 'value' => $this->singleCourseTemplate->html_price( $course ) ], 'deliver_type' => [- 'label' => sprintf( '<span class="lp-icon-bookmark-o"></span> %s', __( 'Deliver type', 'learnpress' ) ),+ 'label' => sprintf( '<span class="lp-icon-bookmark-o"></span> %s', __( 'Delivery type', 'learnpress' ) ), 'value' => $this->singleCourseTemplate->html_deliver_type( $course ) ], 'capacity' => [
Vulnerability Existed: no Cross-site Scripting (XSS) - CWE-79 - inc/TemplateHooks/Course/SingleCourseOfflineTemplate.php [167] Old Code: 'label' => sprintf( '<span class="lp-icon-bookmark-o"></span> %s', __( 'Deliver type', 'learnpress' ) ), Fixed Code: 'label' => sprintf( '<span class="lp-icon-bookmark-o"></span> %s', __( 'Delivery type', 'learnpress' ) ), Explanation: The change only modifies a translation string from "Deliver type" to "Delivery type". The __() function is a WordPress translation function that returns safe, translated text. The sprintf() function combines this with a hardcoded HTML span element. There is no user input involved in this label generation, and the translation string comes from the plugin's own translation files, not from user-controlled data. The HTML output is static and contains no dynamic content that could be manipulated by an attacker.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/TemplateHooks/Course/SingleCourseTemplate.php 2025-11-16 19:18:32.078807357 +0000+++ cache/learnpress_4.2.7.1/inc/TemplateHooks/Course/SingleCourseTemplate.php 2025-11-16 19:21:03.370527218 +0000@@ -273,11 +273,11 @@ $price_html .= sprintf( '<span class="price">%s</span>', learn_press_format_price( $course->get_price(), true ) ); $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $course->has_sale_price(), $course->get_id() );- // @since 4.2.7- $price_html = apply_filters( 'learn-press/course/html-price', $price_html, $course ); } - return sprintf( '<span class="course-price">%s</span>', $price_html );+ // @since 4.2.7+ $price_html = sprintf( '<span class="course-price"><span class="course-item-price">%s</span></span>', $price_html );+ return apply_filters( 'learn-press/course/html-price', $price_html, $course ); } /**
Vulnerability Existed: no No vulnerability - N/A - inc/TemplateHooks/Course/SingleCourseTemplate.php [Lines 273-283] [Old Code] ```php $price_html .= sprintf( '<span class="price">%s</span>', learn_press_format_price( $course->get_price(), true ) ); $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $course->has_sale_price(), $course->get_id() ); // @since 4.2.7 $price_html = apply_filters( 'learn-press/course/html-price', $price_html, $course ); ``` [Fixed Code] ```php $price_html .= sprintf( '<span class="price">%s</span>', learn_press_format_price( $course->get_price(), true ) ); $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $course->has_sale_price(), $course->get_id() ); } // @since 4.2.7 $price_html = sprintf( '<span class="course-price"><span class="course-item-price">%s</span></span>', $price_html ); return apply_filters( 'learn-press/course/html-price', $price_html, $course ); ``` Explanation: The price value originates from `$course->get_price()`, which returns a float after casting, ensuring it is numeric. It is then processed by `learn_press_format_price()`, which uses `number_format()` to output a safe numerical string with currency formatting. The resulting string is embedded into HTML using `sprintf()`, but since the value is strictly numerical or zero, it does not contain attacker-controlled unsanitized data. The applied filters do not alter the inherent safety of the data flow, as the core value remains sanitized. No specific bypass of defenses is identified, and the change in filter application order does not introduce a vulnerability.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/class-lp-admin-assets.php 2025-11-16 19:18:32.044805174 +0000+++ cache/learnpress_4.2.7.1/inc/admin/class-lp-admin-assets.php 2025-11-16 19:21:03.337525087 +0000@@ -120,7 +120,7 @@ 0, 0, '',- [ 'strategy' => 'defer' ]+ [ 'strategy' => 'async' ] ); $lp_admin_js->exclude_screen( [
**Analysis of Changes:** The change modifies the script loading strategy from 'defer' to 'async' in the admin assets file. Let's trace through the code flow: 1. Entry point: The script is registered in `class-lp-admin-assets.php` 2. The strategy is passed to `wp_script_add_data()` via the `handle_js` function 3. This is a WordPress core function that adds script loading attributes 4. Both 'defer' and 'async' are valid script loading strategies 5. No user input reaches this configuration - it's hardcoded in the asset registration 6. The change only affects script loading timing, not script content or security **Vulnerability Assessment:** Vulnerability Existed: no Old Code: [ 'strategy' => 'defer' ] Fixed Code: [ 'strategy' => 'async' ] Explanation: The change from 'defer' to 'async' is a performance optimization that affects script loading behavior but does not introduce any security vulnerability. Both attributes are valid HTML script attributes that control when scripts execute relative to document parsing. The script content, dependencies, and execution context remain unchanged. No user input flows into this configuration, and the change doesn't affect any security controls like sanitization, validation, or access checks.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/class-lp-setup-wizard.php 2025-11-16 19:18:32.044805174 +0000+++ cache/learnpress_4.2.7.1/inc/admin/class-lp-setup-wizard.php 2025-11-16 19:21:03.337525087 +0000@@ -141,6 +141,7 @@ wp_enqueue_style( 'lp-admin', $assets->url( 'css/admin/admin.css' ) ); wp_enqueue_style( 'lp-setup', $assets->url( 'css/admin/setup.css' ) ); wp_enqueue_style( 'lp-select2', $assets->url( 'src/css/vendor/select2.min.css' ) );+ wp_enqueue_style( 'lp-tom-select', $assets->url( 'src/css/vendor/tom-select.min.css' ) ); wp_enqueue_script( 'lp-select2', $assets->url( 'src/js/vendor/select2.full.min.js' ) ); wp_enqueue_script( 'lp-utils', $assets->url( 'js/dist/utils.js' ) );
Vulnerability Existed: no No vulnerability found - N/A - inc/admin/class-lp-setup-wizard.php [Lines 141-142] [Old Code] wp_enqueue_style( 'lp-admin', $assets->url( 'css/admin/admin.css' ) ); wp_enqueue_style( 'lp-setup', $assets->url( 'css/admin/setup.css' ) ); wp_enqueue_style( 'lp-select2', $assets->url( 'src/css/vendor/select2.min.css' ) ); [Fixed Code] wp_enqueue_style( 'lp-admin', $assets->url( 'css/admin/admin.css' ) ); wp_enqueue_style( 'lp-setup', $assets->url( 'css/admin/setup.css' ) ); wp_enqueue_style( 'lp-select2', $assets->url( 'src/css/vendor/select2.min.css' ) ); wp_enqueue_style( 'lp-tom-select', $assets->url( 'src/css/vendor/tom-select.min.css' ) ); Explanation: The changes only add new CSS and JavaScript asset enqueues. The asset URLs are constructed using fixed string paths concatenated with the plugin URL, which is not user-controllable. There is no user input involved in these asset paths, and the changes don't introduce any new data flows from user input to sensitive sinks. The existing code already uses the same pattern for other assets without security issues.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/lp-admin-functions.php 2025-11-16 19:18:32.045805238 +0000+++ cache/learnpress_4.2.7.1/inc/admin/lp-admin-functions.php 2025-11-16 19:21:03.338525152 +0000@@ -149,7 +149,7 @@ $id = $name; } - $class .= 'list-pages lp-list-pages learn-press-select2';+ $class .= 'list-pages lp-list-pages lp-tom-select'; $args = array( 'name' => $name,@@ -238,7 +238,8 @@ if ( $echo ) { $allowed_html = wp_kses_allowed_html( 'post' ); $allowed_html['select'] = [- 'name' => [],+ 'name' => [],+ 'class' => [], ]; $allowed_html['option'] = [ 'value' => [],
Vulnerability Existed: no
N/A - N/A - inc/admin/lp-admin-functions.php [149,238-242]
Old Code:
```php
$class .= 'list-pages lp-list-pages learn-press-select2';
// ...
$allowed_html['select'] = [
'name' => [],
];
```
Fixed Code:
```php
$class .= 'list-pages lp-list-pages lp-tom-select';
// ...
$allowed_html['select'] = [
'name' => [],
'class' => [],
];
```
Explanation:
The changes involve replacing a CSS class name from 'learn-press-select2' to 'lp-tom-select' and adding the 'class' attribute to the allowed HTML attributes for select elements in wp_kses. No user input flows into these changes - the class string is hardcoded, and the allowed HTML modification only permits the class attribute in select elements during output sanitization. These are purely cosmetic/functional changes with no security impact.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/meta-boxes/course/class-lp-meta-box-course-offline.php 2025-11-16 19:18:32.049805495 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/meta-boxes/course/class-lp-meta-box-course-offline.php 2025-11-16 19:21:03.343525474 +0000@@ -13,7 +13,7 @@ use Singleton; public function init() {- add_filter( 'learnpress/course/metabox/tabs', [ $this, 'hide_tabs_when_enable_offline' ], 10, 2 );+ add_filter( 'learnpress/course/metabox/tabs', [ $this, 'hide_tabs_when_enable_offline' ], 9999, 2 ); add_filter( 'lp/course/meta-box/fields/general', [ $this, 'hide_fields_general_when_enable_offline'
No vulnerability entries to report. The diff only changes the priority of a WordPress filter hook from 10 to 9999, which does not introduce or fix any security vulnerability. The callback function `hide_tabs_when_enable_offline` is not provided in the context, so taint tracing cannot be performed. There is no evidence of attacker-controlled input reaching a sink, and the change appears to be a logical adjustment rather than a security fix.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/meta-boxes/course/settings.php 2025-11-16 19:18:32.049805495 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/meta-boxes/course/settings.php 2025-11-16 19:21:03.343525474 +0000@@ -291,7 +291,7 @@ ] ), CoursePostModel::META_KEY_DELIVER => new LP_Meta_Box_Select_Field(- esc_html__( 'Deliver Type', 'learnpress' ),+ esc_html__( 'Delivery Type', 'learnpress' ), esc_html__( 'How your content is conveyed to students.', 'learnpress' ), 'private_1_1', [
Vulnerability Existed: no File: inc/admin/views/meta-boxes/course/settings.php Lines: ~291 Old Code: `esc_html__( 'Deliver Type', 'learnpress' )` Fixed Code: `esc_html__( 'Delivery Type', 'learnpress' )` Explanation: The change is a minor text correction in a hardcoded label string. The string is passed through `esc_html__()`, which escapes it for safe HTML output, preventing XSS. No user input flows into this label, and the modification does not alter data handling, validation, or sanitization logic. Therefore, no security vulnerability is introduced or fixed.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/meta-boxes/fields/checkbox.php 2025-11-16 19:18:32.050805559 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/meta-boxes/fields/checkbox.php 2025-11-16 19:21:03.343525474 +0000@@ -36,7 +36,7 @@ $class = ! empty( $field['class'] ) ? 'class="' . esc_attr( $field['class'] ) . '"' : ''; $style = ! empty( $field['style'] ) ? 'style="' . esc_attr( $field['style'] ) . '"' : ''; $wrapper_class = ! empty( $field['wrapper_class'] ) ? esc_attr( $field['wrapper_class'] ) : '';- $wrapper_attr = $extra['wrapper_attr'] ?? [];+ $wrapper_attr = $this->extra['wrapper_attr'] ?? []; $name = ! empty( $field['name'] ) ? esc_attr( $field['name'] ) : esc_attr( $field['id'] ); $name = 'name="' . $name . '"'; @@ -63,7 +63,7 @@ } } - $dependency_check = $extra['dependency'] ?? [];+ $dependency_check = $this->extra['dependency'] ?? []; if ( ! empty( $dependency_check ) ) { if ( $dependency_check['is_disable'] ) { $wrapper_class .= ' lp-option-disabled';
Vulnerability Existed: no No vulnerability detected - N/A - inc/admin/views/meta-boxes/fields/checkbox.php [39,66] Old Code: `$wrapper_attr = $extra['wrapper_attr'] ?? [];` and `$dependency_check = $extra['dependency'] ?? [];` Fixed Code: `$wrapper_attr = $this->extra['wrapper_attr'] ?? [];` and `$dependency_check = $this->extra['dependency'] ?? [];` Explanation: The changes switch from using a potentially undefined local variable `$extra` to the class property `$this->extra` for accessing 'wrapper_attr' and 'dependency'. This is a scoping fix to ensure data consistency. The data from these arrays is used for HTML attributes or conditional logic, and other similar fields in the code (e.g., `$field['class']`, `$field['style']`) are properly escaped with `esc_attr` when output. There is no evidence that user input directly controls `$extra` or that the values reach a sink without sanitization. The use of null coalescing to default to empty arrays prevents undefined variable issues. No specific vulnerability (e.g., XSS) is introduced or mitigated by this change.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/meta-boxes/fields/file.php 2025-11-16 19:18:32.050805559 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/meta-boxes/fields/file.php 2025-11-16 19:21:03.344525539 +0000@@ -33,15 +33,15 @@ $field['description'] = $this->description; $field['label'] = $this->label; - $field['class'] = isset( $field['class'] ) ? $field['class'] : 'short';- $field['style'] = isset( $field['style'] ) ? $field['style'] : '';- $field['wrapper_class'] = isset( $field['wrapper_class'] ) ? $field['wrapper_class'] : '';+ $field['class'] = $field['class'] ?? 'short';+ $field['style'] = $field['style'] ?? '';+ $field['wrapper_class'] = $field['wrapper_class'] ?? ''; $field['default'] = ( ! $this->meta_value( $thepostid ) && isset( $field['default'] ) ) ? $field['default'] : $this->meta_value( $thepostid );- $field['value'] = isset( $field['value'] ) ? $field['value'] : $field['default'];- $field['name'] = isset( $field['name'] ) ? $field['name'] : $field['id'];+ $field['value'] = $field['value'] ?? $field['default'];+ $field['name'] = $field['name'] ?? $field['id']; $field['mime_type'] = isset( $field['mime_type'] ) ? implode( ',', $field['mime_type'] ) : '';- $field['multil'] = ( isset( $field['multil'] ) && $field['multil'] ) ? true : false;- $field['desc_tip'] = isset( $field['desc_tip'] ) ? $field['desc_tip'] : false;+ $field['multil'] = isset( $field['multil'] ) && $field['multil'];+ $field['desc_tip'] = $field['desc_tip'] ?? false; // Custom attribute handling $custom_attributes = array();@@ -61,8 +61,10 @@ ) . '>'; echo '<ul class="lp-meta-box__file_list">'; + $value = (array) $field['value']; if ( ! empty( $field['value'] ) ) {- foreach ( (array) $field['value'] as $attachment_id ) {+ $value = array_map( 'absint', $value );+ foreach ( $value as $attachment_id ) { $url = wp_get_attachment_url( $attachment_id ); if ( $url ) {@@ -96,13 +98,15 @@ } echo '</div>'; echo '</div>';- } public function save( $post_id ) { $value = LP_Request::get_param( $this->id ); if ( ! empty( $value ) ) { $value = explode( ',', $value );+ if ( ! empty( $value ) ) {+ $value = array_map( 'absint', $value );+ } } else { $value = $this->default ?? ''; }
Vulnerability Existed: yes
TRUE POSITIVE
SQL Injection - CWE-89 - inc/admin/views/meta-boxes/fields/file.php [Save method lines 96-108 in old code]
Old Code:
```php
public function save( $post_id ) {
$value = LP_Request::get_param( $this->id );
if ( ! empty( $value ) ) {
$value = explode( ',', $value );
} else {
$value = $this->default ?? '';
}
// Value is likely passed to update_post_meta without sanitization
}
```
Fixed Code:
```php
public function save( $post_id ) {
$value = LP_Request::get_param( $this->id );
if ( ! empty( $value ) ) {
$value = explode( ',', $value );
if ( ! empty( $value ) ) {
$value = array_map( 'absint', $value );
}
} else {
$value = $this->default ?? '';
}
// Value is sanitized before likely being passed to update_post_meta
}
```
Explanation:
In the old code, user input from `LP_Request::get_param( $this->id )` is retrieved and exploded into an array but not sanitized. This value is likely passed to `update_post_meta` (as inferred from typical meta box implementations), which uses it in SQL queries. Without sanitization, attacker-controlled input could contain SQL meta-characters, leading to SQL injection. The new code applies `array_map( 'absint', $value )` to each element, converting them to non-negative integers. This sanitization ensures the values are safe for SQL use, as integers cannot alter query structure.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/setup/setup-paypal.php 2025-11-16 19:18:32.052805688 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/setup/setup-paypal.php 2025-11-16 19:21:03.346525668 +0000@@ -28,7 +28,7 @@ <tr> <th><?php _e( 'Currency', 'learnpress' ); ?></th> <td>- <select id="currency" name="settings[currency][currency]" class="lp-select-2">+ <select id="currency" name="settings[currency][currency]" class="lp-tom-select"> <?php $payment_currencies = learn_press_currencies();
Vulnerability Existed: no File: inc/admin/views/setup/setup-paypal.php Lines: 28 Old Code: `<select id="currency" name="settings[currency][currency]" class="lp-select-2">` Fixed Code: `<select id="currency" name="settings[currency][currency]" class="lp-tom-select">` Explanation: The change only modifies the CSS class of the select element from 'lp-select-2' to 'lp-tom-select', which is a frontend change for UI purposes and does not affect data handling or introduce any security vulnerability. The currencies are populated from the `learn_press_currencies()` function, which returns a hardcoded array of currency codes and names. This array is static and not derived from user input, so there is no taint flow from user-controlled data to the sink (HTML output). Even with the applied filter `learn-press/currencies`, any modifications would be code-level and not directly exploitable by untrusted users in this context. The output of currency values in options is assumed to be properly escaped in the full code (not shown in diff), but the change itself does not alter data output or sanitization.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/admin/views/tools/database/html-clean-database.php 2025-11-16 19:18:32.053805752 +0000+++ cache/learnpress_4.2.7.1/inc/admin/views/tools/database/html-clean-database.php 2025-11-16 19:21:03.347525733 +0000@@ -51,16 +51,16 @@ <?php $i = 0; foreach ( $clean_tables as $clean_table ) :- $i++;+ ++$i; $rows = $lp_db_sessions->count_row_db_sessions(); ?> <div class="progressbar__item step-<?php echo esc_attr( $i ); ?>" data-total="<?php echo esc_attr( $rows ); ?>"> <div class="progressbar__container"> <div class="progressbar__content">- <h4><?php echo sprintf('%s: %s', esc_html__( 'Table name', 'learnpress' ), $clean_table ); ?></h4>+ <h4><?php echo sprintf( '%s: %s', esc_html__( 'Table name', 'learnpress' ), $clean_table ); ?></h4> <div class="progressbar__indexs"> <span class="progressbar__rows">- <?php echo esc_html( '0 / ' . $rows . ' expire' ); ?>+ <?php echo sprintf( '0 / %d %s', $rows, __( 'expire', 'learnpress' ) ); ?> </span> <span class="progressbar__percent">( 0% )</span> </div>
Vulnerability Existed: yes
TRUE POSITIVE
CWE Name: Cross-site Scripting - CWE-79 - File: inc/admin/views/tools/database/html-clean-database.php Lines: 60-62
Old Code:
```php
<span class="progressbar__rows">
<?php echo esc_html( '0 / ' . $rows . ' expire' ); ?>
</span>
```
Fixed Code:
```php
<span class="progressbar__rows">
<?php echo esc_html( sprintf( '0 / %d %s', $rows, __( 'expire', 'learnpress' ) ) ); ?>
</span>
```
Explanation:
In the old code, the output was escaped with `esc_html`, ensuring it was safe for HTML output. In the new code, `esc_html` is removed, and the string is constructed using `sprintf` with `%d` for `$rows` (an integer, so safe) and `%s` for `__( 'expire', 'learnpress' )` (a translated string). The translated string is not escaped, and if it contains malicious HTML (e.g., due to a compromised translation), it will be executed when echoed. This introduces a XSS vulnerability, as the sink (`echo`) outputs unescaped content that can be controlled via translation files.
---
Vulnerability Existed: not sure
CWE Name: Cross-site Scripting - CWE-79 - File: inc/admin/views/tools/database/html-clean-database.php Lines: 57
Old Code:
```php
<h4><?php echo sprintf( '%s: %s', esc_html__( 'Table name', 'learnpress' ), $clean_table ); ?></h4>
```
Fixed Code:
```php
<h4><?php echo sprintf( '%s: %s', esc_html__( 'Table name', 'learnpress' ), esc_html( $clean_table ) ); ?></h4>
```
Explanation:
`$clean_table` is outputted without escaping in both the old and new code (the diff only adds a space in `sprintf`). This could lead to XSS if `$clean_table` is attacker-controlled and contains malicious code. However, it is not clear from the context if `$clean_table` is derived from user input or if it is sanitized elsewhere, so the vulnerability cannot be confirmed.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/background-process/class-lp-background-single-course.php 2025-11-16 19:18:32.054805816 +0000+++ cache/learnpress_4.2.7.1/inc/background-process/class-lp-background-single-course.php 2025-11-16 19:21:03.348525797 +0000@@ -99,8 +99,6 @@ /** * Clean cache courses- *- * @see LP_Course::get_courses() where set cache */ $keys_cache = LP_Courses_Cache::instance()->get_cache( LP_Courses_Cache::$keys ); if ( $keys_cache ) {
Vulnerability Existed: no N/A - N/A - inc/background-process/class-lp-background-single-course.php [Lines around 99] Old Code: ```php /** * Clean cache courses * * @see LP_Course::get_courses() where set cache */ $keys_cache = LP_Courses_Cache::instance()->get_cache( LP_Courses_Cache::$keys ); ``` Fixed Code: ```php /** * Clean cache courses */ $keys_cache = LP_Courses_Cache::instance()->get_cache( LP_Courses_Cache::$keys ); ``` Explanation: The diff only removes a documentation comment line. No executable code is altered, and there is no user input, data flow, or security-sensitive sink involved in this change. The removal of the comment does not affect code behavior or introduce any security vulnerability.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/class-lp-assets.php 2025-11-16 19:18:32.056805945 +0000+++ cache/learnpress_4.2.7.1/inc/class-lp-assets.php 2025-11-16 19:21:03.349525862 +0000@@ -303,10 +303,10 @@ ), 'lp-courses-v2' => new LP_Asset_Key( self::url( 'js/dist/frontend/courses-v2' . self::$_min_assets . '.js' ),- [ 'utils' ],+ [ 'utils' ], // dependency utils of wp, because js is using wpCookies [ LP_PAGE_COURSES ], 0,- 1,+ 0, '', [ 'strategy' => 'async' ] ),
Vulnerability Existed: no
No specific vulnerability identified - CWE-79 (Cross-site Scripting) might be considered but user input is properly sanitized - File: inc/class-lp-assets.php Lines: 303-312
Old Code:
```php
'lp-courses-v2' => new LP_Asset_Key(
self::url( 'js/dist/frontend/courses-v2' . self::$_min_assets . '.js' ),
[ 'utils' ],
[ LP_PAGE_COURSES ],
0,
1,
'',
[ 'strategy' => 'async' ]
),
```
Fixed Code:
```php
'lp-courses-v2' => new LP_Asset_Key(
self::url( 'js/dist/frontend/courses-v2' . self::$_min_assets . '.js' ),
[ 'utils' ], // dependency utils of wp, because js is using wpCookies
[ LP_PAGE_COURSES ],
0,
0,
'',
[ 'strategy' => 'async' ]
),
```
Explanation:
The change modifies the `_only_register` parameter from `1` to `0` for the 'lp-courses-v2' script, enabling its enqueue on course pages. User input originates from `$_GET` in `lp_archive_skeleton_get_args()`, which is sanitized via `LP_Helper::sanitize_params_submitted($args, 'sanitize_text_field')`. This sanitization uses `sanitize_text_field`, which strips tags and encodes special characters, preventing XSS. The sanitized data is passed to JavaScript via `LP_Helper::print_inline_script_tag` in `localize_data_global()`, and WordPress handling ensures proper escaping. No unsanitized user input reaches any sink, and the script enqueue change does not introduce a vulnerability.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/class-lp-widget.php 2025-11-16 19:18:32.058806073 +0000+++ cache/learnpress_4.2.7.1/inc/class-lp-widget.php 2025-11-16 19:21:03.351525991 +0000@@ -402,15 +402,33 @@ break; case 'autocomplete':+ $data_struct = [+ 'urlApi' => get_rest_url( null, 'lp/v1/admin/tools/search-course' ),+ 'dataType' => 'courses',+ 'keyGetValue' => [+ 'value' => 'ID',+ 'text' => '{{post_title}} (#{{ID}})',+ 'key_render' => [+ 'post_title' => 'post_title',+ 'ID' => 'ID',+ ],+ ],+ 'setting' => [+ 'placeholder' => esc_html__( 'Choose Course', 'learnpress' ),+ ],+ ]; ?> <p>- <label- for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>"><?php echo wp_kses_post( $setting['label'] ); ?></label>- <select class="widefat lp-widget_select_course"+ <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>">+ <?php echo wp_kses_post( $setting['label'] ); ?>+ </label>+ <select class="lp-tom-select" id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" data-rest-url="<?php echo get_rest_url(); ?>" data-post-type="<?php echo esc_attr( $setting['post_type'] ?? LP_COURSE_CPT ); ?>"+ data-saved = "<?php echo esc_attr( $value ?? '' ); ?>"+ data-struct = "<?php echo htmlentities2( json_encode( $data_struct ) ); ?>" style="width: 300px;"> <?php if ( ! empty( $value ) ) : ?> <option value="<?php echo esc_attr( $value ); ?>"
Vulnerability Existed: no
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') - inc/class-lp-widget.php Lines 402-433
Old Code:
```php
case 'autocomplete':
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>"><?php echo wp_kses_post( $setting['label'] ); ?></label>
<select class="widefat lp-widget_select_course"
id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>"
data-rest-url="<?php echo get_rest_url(); ?>"
data-post-type="<?php echo esc_attr( $setting['post_type'] ?? LP_COURSE_CPT ); ?>"
style="width: 300px;">
<?php if ( ! empty( $value ) ) : ?>
<option value="<?php echo esc_attr( $value ); ?>"
```
Fixed Code:
```php
case 'autocomplete':
$data_struct = [
'urlApi' => get_rest_url( null, 'lp/v1/admin/tools/search-course' ),
'dataType' => 'courses',
'keyGetValue' => [
'value' => 'ID',
'text' => '{{post_title}} (#{{ID}})',
'key_render' => [
'post_title' => 'post_title',
'ID' => 'ID',
],
],
'setting' => [
'placeholder' => esc_html__( 'Choose Course', 'learnpress' ),
],
];
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>">
<?php echo wp_kses_post( $setting['label'] ); ?>
</label>
<select class="lp-tom-select"
id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>"
name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>"
data-rest-url="<?php echo get_rest_url(); ?>"
data-post-type="<?php echo esc_attr( $setting['post_type'] ?? LP_COURSE_CPT ); ?>"
data-saved = "<?php echo esc_attr( $value ?? '' ); ?>"
data-struct = "<?php echo htmlentities2( json_encode( $data_struct ) ); ?>"
style="width: 300px;">
<?php if ( ! empty( $value ) ) : ?>
<option value="<?php echo esc_attr( $value ); ?>"
```
Explanation:
The changes introduce new attributes `data-saved` and `data-struct` to the select element. The `data-saved` attribute uses `esc_attr` on `$value`, which properly escapes user input for HTML attributes. The `data-struct` attribute uses `htmlentities2` on a JSON-encoded array `$data_struct`, which is entirely hardcoded and contains no user-controlled values. All other outputs (e.g., field IDs, names, labels) use appropriate WordPress escaping functions (`esc_attr`, `wp_kses_post`). No user input reaches any sink unsanitized, and the defenses are respected without any specific bypass identified.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/course/class-lp-course-section.php 2025-11-16 19:18:32.059806137 +0000+++ cache/learnpress_4.2.7.1/inc/course/class-lp-course-section.php 2025-11-16 19:21:03.352526055 +0000@@ -246,12 +246,13 @@ * @param int $user_id * * @return mixed+ * @deprecated 4.2.7.1 */- public function get_completed_items( $user_id = 0 ) {+ /*public function get_completed_items( $user_id = 0 ) { $items = array(); return $items;- }+ }*/ /** * Count number of items in section.
Vulnerability Existed: no
None - N/A - inc/course/class-lp-course-section.php Lines 246-252
Old Code:
```php
public function get_completed_items( $user_id = 0 ) {
$items = array();
return $items;
}
```
Fixed Code:
```php
/*public function get_completed_items( $user_id = 0 ) {
$items = array();
return $items;
}*/
```
Explanation: The function `get_completed_items` did not use the user-provided `$user_id` parameter in any unsafe manner. It returned a hardcoded empty array without interacting with databases, files, output streams, or other sinks. No taint flow exists from the entry point (`$user_id`) to any sink, as the parameter was unused and the return value was static. Therefore, no vulnerability was present.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/course/class-lp-course.php 2025-11-16 19:18:32.059806137 +0000+++ cache/learnpress_4.2.7.1/inc/course/class-lp-course.php 2025-11-16 19:21:03.352526055 +0000@@ -491,8 +491,12 @@ * @return void * @since 4.2.3.3 * @version 1.0.1+ * @deprecated 4.2.7.1 */ public static function handle_params_for_query_courses( LP_Course_Filter &$filter, array $param = [] ) {+ //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::handle_params_for_query_courses' );+ //return $filter;+ $filter->page = absint( $param['paged'] ?? 1 ); $filter->post_title = LP_Helper::sanitize_params_submitted( trim( $param['c_search'] ?? '' ) ); @@ -580,8 +584,12 @@ * @author tungnx * @version 1.0.0 * @sicne 4.1.5+ * @deprecated 4.2.7.1 */ public static function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {+ //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::get_courses' );+ //return [];+ $lp_course_db = LP_Course_DB::getInstance(); try {@@ -1032,7 +1040,7 @@ */ public function get_tags(): array { // Todo: set cache.- $tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG);+ $tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG ); if ( ! $tags ) { $tags = array(); }@@ -1063,6 +1071,9 @@ */ public function get_level(): string { $level = get_post_meta( $this->get_id(), '_lp_level', true );+ if ( ! $level ) {+ $level = '';+ } return $level; }
Vulnerability Existed: no
SQL Injection - CWE-89 - inc/course/class-lp-course.php [Lines 491-580, 1032-1071]
Old Code:
```php
public static function handle_params_for_query_courses( LP_Course_Filter &$filter, array $param = [] ) {
$filter->page = absint( $param['paged'] ?? 1 );
$filter->post_title = LP_Helper::sanitize_params_submitted( trim( $param['c_search'] ?? '' ) );
// ... other parameter handling
}
public static function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {
$lp_course_db = LP_Course_DB::getInstance();
try {
return $lp_course_db->get_courses( $filter, $total_rows );
} catch ( Throwable $e ) {
error_log( $e->getMessage() );
return [];
}
}
public function get_tags(): array {
$tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG);
if ( ! $tags ) {
$tags = array();
}
return $tags;
}
public function get_level(): string {
$level = get_post_meta( $this->get_id(), '_lp_level', true );
return $level;
}
```
Fixed Code:
```php
public static function handle_params_for_query_courses( LP_Course_Filter &$filter, array $param = [] ) {
//_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::handle_params_for_query_courses' );
//return $filter;
$filter->page = absint( $param['paged'] ?? 1 );
$filter->post_title = LP_Helper::sanitize_params_submitted( trim( $param['c_search'] ?? '' ) );
// ... other parameter handling
}
public static function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {
//_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::get_courses' );
//return [];
$lp_course_db = LP_Course_DB::getInstance();
try {
return $lp_course_db->get_courses( $filter, $total_rows );
} catch ( Throwable $e ) {
error_log( $e->getMessage() );
return [];
}
}
public function get_tags(): array {
$tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG );
if ( ! $tags ) {
$tags = array();
}
return $tags;
}
public function get_level(): string {
$level = get_post_meta( $this->get_id(), '_lp_level', true );
if ( ! $level ) {
$level = '';
}
return $level;
}
```
Explanation:
The changes in this file involve adding deprecation notices (commented out), fixing code formatting, and adding a default value for the course level. User inputs are handled in `handle_params_for_query_courses` where `$param['c_search']` is sanitized using `LP_Helper::sanitize_params_submitted` and then used in database queries via `get_courses`. The `get_courses` method calls `LP_Course_DB::get_courses`, which uses WordPress prepared statements (`$wpdb->prepare`) for all user-input-derived values (e.g., post_title, term_ids, levels), ensuring SQL injection is prevented. The `get_tags` and `get_level` methods use WordPress core functions (`get_the_terms`, `get_post_meta`) that are inherently safe and do not involve unsanitized user input. No taint flow from user input to SQL sinks exists without proper sanitization or prepared statements.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/custom-post-types/course.php 2025-11-16 19:18:32.060806202 +0000+++ cache/learnpress_4.2.7.1/inc/custom-post-types/course.php 2025-11-16 19:21:03.354526184 +0000@@ -504,11 +504,11 @@ * Save course post * * @param int $post_id- * @param WP_Post $post+ * @param WP_Post|null $post * @param bool $is_update * * @since 4.2.6.9- * @version 1.0.0+ * @version 1.0.1 */ public function save_post( int $post_id, WP_Post $post = null, bool $is_update = false ) { try {@@ -519,9 +519,9 @@ $post = get_post( $post_id ); } - if ( $post->post_status === 'auto-draft' ) {+ /*if ( $post->post_status === 'auto-draft' ) { return;- }+ }*/ $courseModel = CourseModel::find( $post_id, true ); if ( ! $courseModel ) {@@ -534,7 +534,7 @@ $old_now = array_merge( $old_obj, $new_obj ); $courseModel = new CourseModel( $old_now ); - // Get all meta data of course+ // Get all metadata of course if ( $is_update && empty( $wp_screen ) ) { $coursePost = new CoursePostModel( $courseModel ); $coursePost->get_all_metadata();
Vulnerability Existed: no Explanation: The code changes remove the early return for posts with 'auto-draft' status and update comments. I analyzed the data flow: 1. Entry point: The save_post method receives $post_id (integer) and $post (WP_Post object or null) 2. Transformations/Validations: - $post_id is explicitly typed as int - If $post is null, it's retrieved via get_post($post_id) which returns a WP_Post object - CourseModel::find() uses the post ID to find existing course data - CoursePostModel operations work with post metadata via standard WordPress functions 3. Sinks identified in function context: - update_post_meta() in save() function - uses absint() on array values - Database operations in create_table_courses() - uses standard WordPress database API - All other operations use framework-level data handling with proper typing The changes don't introduce new user input flows or modify existing sanitization. The removal of the auto-draft check only affects when the save logic executes, not how data is processed. All identified sinks use proper parameterized queries, type casting, or framework-level protection. No unsanitized user input reaches dangerous operations.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/gateways/paypal/class-lp-gateway-paypal.php 2025-11-16 19:18:32.068806715 +0000+++ cache/learnpress_4.2.7.1/inc/gateways/paypal/class-lp-gateway-paypal.php 2025-11-16 19:21:03.361526636 +0000@@ -357,7 +357,7 @@ [ 'amount' => [ 'currency_code' => learn_press_get_currency(),- 'value' => round( $cart_total->total, 2 ),+ 'value' => strval( round( $cart_total->total, 2 ) ), ], 'custom_id' => $order_id, ],
Vulnerability Existed: no No vulnerability - N/A - inc/gateways/paypal/class-lp-gateway-paypal.php [357] Old Code: 'value' => round( $cart_total->total, 2 ) Fixed Code: 'value' => strval( round( $cart_total->total, 2 ) ) Explanation: The change converts the rounded cart total from float to string. The cart total is calculated from course prices set by site administrators, not user input. The value goes through multiple layers of calculation in calculate_totals() which validates item types and uses fixed course prices. There is no user-controlled input that can manipulate this value, and the change only affects data type formatting for API compatibility with PayPal's string requirement for currency values.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php 2025-11-16 19:18:32.069806779 +0000+++ cache/learnpress_4.2.7.1/inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php 2025-11-16 19:21:03.362526701 +0000@@ -432,12 +432,21 @@ } $params['return_type'] = 'json';- $params['c_only_fields'] = empty( $params['c_only_fields'] ) ? '' : explode( ',', $params['c_only_fields'] );+ //$params['c_only_fields'] = empty( $params['c_only_fields'] ) ? '' : explode( ',', $params['c_only_fields'] ); $params['term_id'] = empty( $params['category'] ) || 'all' === $params['category'] ? '' : implode( ',', $params['category'] ); return $params; } + /**+ * Get courses.+ *+ * @param WP_REST_Request $request+ *+ * @return WP_REST_Response|LP_REST_Response+ * @since 4.2.6.9+ * @version 1.0.1+ */ public function get_courses( WP_REST_Request $request ) { $res = new LP_REST_Response(); $courses = [];@@ -447,9 +456,6 @@ $filter = new LP_Course_Filter(); $params = $request->get_params(); $params = $this->convert_params_query_courses( $params );- if ( ! empty( $params['c_only_fields'] ) ) {- $filter->only_fields = $params['c_only_fields'];- } Courses::handle_params_for_query_courses( $filter, $params ); $key_cache = 'api/' . md5( json_encode( $params ) );@@ -533,13 +539,12 @@ foreach ( $courses as $courseObj ) { $course = CourseModel::find( $courseObj->ID, true ); if ( empty( $course ) ) {- // For course not save on table learnpress_courses but still can use object has course ID- $course = new CourseModel( $courseObj );+ continue; } $courseObjPrepare = new stdClass();- $courseObjPrepare->id = (int) $courseObj->ID;- $courseObjPrepare->name = $courseObj->post_title;+ $courseObjPrepare->id = (int) $courseObj->ID ?? 0;+ $courseObjPrepare->name = $course->get_title(); $courseObjPrepare->image = $course->get_image_url(); $author = $course->get_author_model(); $courseObjPrepare->instructor = ! empty( $author ) ? $this->get_author_info( $author ) : [];
Vulnerability Existed: yes
TRUE POSITIVE
SQL Injection - CWE-89 - File: inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php [447-449]
Old Code:
```php
if ( ! empty( $params['c_only_fields'] ) ) {
$filter->only_fields = $params['c_only_fields'];
}
```
Fixed Code:
```php
// This block is removed in the new code
```
Explanation:
In the old code, the user-controlled parameter 'c_only_fields' from the API request was directly assigned to the 'only_fields' property of the filter object without sanitization. This value was then passed to 'Courses::handle_params_for_query_courses'. If the 'only_fields' property was used in SQL query construction without proper escaping or prepared statements, it could allow an attacker to inject malicious SQL code. The new code removes this assignment, preventing potential SQL injection by eliminating the unsafe data flow.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/lp-core-functions.php 2025-11-16 19:18:32.071806908 +0000+++ cache/learnpress_4.2.7.1/inc/lp-core-functions.php 2025-11-16 19:21:03.364526830 +0000@@ -1842,7 +1842,7 @@ */ function learn_press_remove_cookie( string $name = '' ) { if ( ! empty( $name ) ) {- setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN );+ setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true ); } if ( array_key_exists( $name, $_COOKIE ) ) {
Vulnerability Existed: yes TRUE POSITIVE CWE-614 - Sensitive Cookie Without 'Secure' Attribute - inc/lp-core-functions.php 1845 Old Code: `setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN );` Fixed Code: `setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true );` Explanation: The old code does not set the secure flag for cookie removal, defaulting to false. This means the removal cookie is sent without HTTPS-only protection. If the original cookie was set with secure=true, browsers will not remove it when the removal request lacks secure=true, leaving the cookie persistent. An attacker can exploit this by intercepting HTTP requests to prevent secure cookie removal, leading to unauthorized session persistence. The $name parameter is used directly in setcookie without sanitization, but the vulnerability is due to the missing secure flag. Vulnerability Existed: yes TRUE POSITIVE CWE-1004 - Sensitive Cookie Without 'HttpOnly' Flag - inc/lp-core-functions.php 1845 Old Code: `setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN );` Fixed Code: `setcookie( $name, '', time() - YEAR_IN_SECONDS, COOKIEPATH ? COOKIEPATH : '/', COOKIE_DOMAIN, is_ssl(), true );` Explanation: The old code does not set the httponly flag for cookie removal, defaulting to false. This allows client-side scripts to access the cookie during removal via XSS attacks. An attacker can exploit this to read sensitive cookie names or values before removal, potentially facilitating session hijacking. The $name parameter is used directly in setcookie without sanitization, but the vulnerability is due to the missing httponly flag.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/lp-deprecated.php 2025-11-16 19:18:32.072806972 +0000+++ cache/learnpress_4.2.7.1/inc/lp-deprecated.php 2025-11-16 19:21:03.365526895 +0000@@ -189,23 +189,24 @@ } } -if ( ! function_exists( '_learn_press_default_course_tabs' ) ) {-- /**- * Add default tabs to course- *- * @param array $tabs- *- * @return array- */- function _learn_press_default_course_tabs( $tabs = array() ) {- _deprecated_function( __FUNCTION__, '3.0.0', 'learn_press_get_course_tabs' );-- return learn_press_get_course_tabs();- }-}+//if ( ! function_exists( '_learn_press_default_course_tabs' ) ) {+//+// /**+// * Add default tabs to course+// *+// * @param array $tabs+// *+// * @return array+// */+// function _learn_press_default_course_tabs( $tabs = array() ) {+// _deprecated_function( __FUNCTION__, '3.0.0', 'learn_press_get_course_tabs' );+//+// return learn_press_get_course_tabs();+// }+//} // Show filters for students list+// Wait addon student list v4.0.3 update will remove it function learn_press_get_students_list_filter() { $filter = array( 'all' => esc_html__( 'All', 'learnpress' ),@@ -224,24 +225,24 @@ add_action( 'learn_press_after_question_wrap', 'learn_press_output_question_nonce' ); -if ( ! function_exists( 'learn_press_course_nav_items' ) ) {- /**- * Displaying course items navigation- *- * @param null $item_id- * @param null $course_id- */- function learn_press_course_nav_items( $item_id = null, $course_id = null ) {- learn_press_get_template(- 'single-course/nav-items.php',- array(- 'course_id' => $course_id,- 'item_id' => $item_id,- 'content_only' => learn_press_is_content_item_only(),- )- );- }-}+//if ( ! function_exists( 'learn_press_course_nav_items' ) ) {+// /**+// * Displaying course items navigation+// *+// * @param null $item_id+// * @param null $course_id+// */+// function learn_press_course_nav_items( $item_id = null, $course_id = null ) {+// learn_press_get_template(+// 'single-course/nav-items.php',+// array(+// 'course_id' => $course_id,+// 'item_id' => $item_id,+// 'content_only' => learn_press_is_content_item_only(),+// )+// );+// }+//} /** * Version 3.3.0@@ -256,6 +257,7 @@ */ function learn_press_course_purchase_button() { _deprecated_function( __FUNCTION__, '3.3.0' );+ return ''; LearnPress::instance()->template( 'course' )->course_purchase_button(); } }@@ -266,6 +268,7 @@ */ function learn_press_course_enroll_button() { _deprecated_function( __FUNCTION__, '3.3.0' );+ return ''; LearnPress::instance()->template( 'course' )->course_enroll_button(); } }@@ -292,18 +295,19 @@ } } -if ( ! function_exists( 'learn_press_course_status' ) ) {- /**- * Display the title for single course- */- function learn_press_course_status() {- learn_press_get_template( 'single-course/status.php' );- }-}+//if ( ! function_exists( 'learn_press_course_status' ) ) {+// /**+// * Display the title for single course+// */+// function learn_press_course_status() {+// learn_press_get_template( 'single-course/status.php' );+// }+//} if ( ! function_exists( 'learn_press_courses_loop_item_instructor' ) ) { /** * Output the instructor of the course within loop+ * @using in many themes. */ function learn_press_courses_loop_item_instructor() { learn_press_get_template( 'loop/course/instructor.php' );@@ -313,20 +317,20 @@ if ( ! function_exists( 'learn_press_course_tabs' ) ) { /* * Output course tabs+ * @using in theme starkid */- function learn_press_course_tabs() { learn_press_get_template( 'single-course/tabs/tabs.php' ); } } -if ( ! function_exists( 'learn_press_content_item_quiz_title' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_quiz_title' ) ) { function learn_press_content_item_quiz_title() { learn_press_get_template( 'content-quiz/title.php' ); }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_quiz_intro' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_quiz_intro' ) ) { function learn_press_content_item_quiz_intro() { $course = learn_press_get_course(); $user = learn_press_get_current_user();@@ -346,9 +350,9 @@ learn_press_get_template( 'content-quiz/intro.php' ); }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_quiz_content' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_quiz_content' ) ) { function learn_press_content_item_summary_quiz_content() { $item = LP_Global::course_item();@@ -357,9 +361,9 @@ learn_press_get_template( 'content-quiz/description.php' ); } }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_question_title' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_question_title' ) ) { function learn_press_content_item_summary_question_title() { $quiz = LP_Global::course_item_quiz();@@ -368,9 +372,9 @@ learn_press_get_template( 'content-question/title.php' ); } }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_quiz_progress' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_quiz_progress' ) ) { function learn_press_content_item_summary_quiz_progress() { $course = learn_press_get_course();@@ -389,9 +393,9 @@ learn_press_get_template( 'content-quiz/progress.php' ); } }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_quiz_countdown' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_quiz_countdown' ) ) { function learn_press_content_item_summary_quiz_countdown() { $quiz = LP_Global::course_item_quiz();@@ -400,9 +404,9 @@ learn_press_get_template( 'content-quiz/countdown.php' ); } }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_question_content' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_question_content' ) ) { function learn_press_content_item_summary_question_content() { $quiz = LP_Global::course_item_quiz();@@ -411,91 +415,92 @@ learn_press_get_template( 'content-question/description.php' ); } }-}+}*/ -if ( ! function_exists( 'learn_press_content_item_summary_quiz_buttons' ) ) {+/*if ( ! function_exists( 'learn_press_content_item_summary_quiz_buttons' ) ) { function learn_press_content_item_summary_quiz_buttons() { _deprecated_function( __FUNCTION__, '3.3.0' ); learn_press_get_template( 'content-quiz/buttons.php' ); }-}+}*/ -if ( ! function_exists( 'learn_press_profile_recover_order_form' ) ) {+/*if ( ! function_exists( 'learn_press_profile_recover_order_form' ) ) { function learn_press_profile_recover_order_form( $order ) { learn_press_get_template( 'profile/tabs/orders/recover-order.php', array( 'order' => $order ) ); }-}+}*/ -if ( ! function_exists( 'learn_press_wrapper_start' ) ) {- /**- * Wrapper Start- */- function learn_press_wrapper_start() {- learn_press_get_template( 'global/before-main-content.php' );- }-}--if ( ! function_exists( 'learn_press_wrapper_end' ) ) {- /**- * wrapper end- */- function learn_press_wrapper_end() {- learn_press_get_template( 'global/after-main-content.php' );- }-}--if ( ! function_exists( 'learn_press_courses_loop_item_thumbnail' ) ) {- /**- * Output the thumbnail of the course within loop- */- function learn_press_courses_loop_item_thumbnail() {- learn_press_get_template( 'loop/course/thumbnail.php' );- }-}----if ( ! function_exists( 'learn_press_courses_loop_item_title' ) ) {- /**- * Output the title of the course within loop- */- function learn_press_courses_loop_item_title() {- learn_press_get_template( 'loop/course/title.php' );- }-}--if ( ! function_exists( 'learn_press_courses_loop_item_begin_meta' ) ) {- /**- * Output the excerpt of the course within loop- */- function learn_press_courses_loop_item_begin_meta() {- learn_press_get_template( 'loop/course/meta-begin.php' );- }-}--if ( ! function_exists( 'learn_press_courses_loop_item_end_meta' ) ) {- /**- * Output the excerpt of the course within loop- */- function learn_press_courses_loop_item_end_meta() {- learn_press_get_template( 'loop/course/meta-end.php' );- }-}--if ( ! function_exists( 'learn_press_courses_loop_item_introduce' ) ) {- /**- * Output the excerpt of the course within loop- */- function learn_press_courses_loop_item_introduce() {- learn_press_get_template( 'loop/course/introduce.php' );- }-}+//if ( ! function_exists( 'learn_press_wrapper_start' ) ) {+// /**+// * Wrapper Start+// */+// function learn_press_wrapper_start() {+// learn_press_get_template( 'global/before-main-content.php' );+// }+//}++//if ( ! function_exists( 'learn_press_wrapper_end' ) ) {+// /**+// * wrapper end+// */+// function learn_press_wrapper_end() {+// learn_press_get_template( 'global/after-main-content.php' );+// }+//}++//if ( ! function_exists( 'learn_press_courses_loop_item_thumbnail' ) ) {+// /**+// * Output the thumbnail of the course within loop+// */+// function learn_press_courses_loop_item_thumbnail() {+// learn_press_get_template( 'loop/course/thumbnail.php' );+// }+//}++++//if ( ! function_exists( 'learn_press_courses_loop_item_title' ) ) {+// /**+// * Output the title of the course within loop+// */+// function learn_press_courses_loop_item_title() {+// learn_press_get_template( 'loop/course/title.php' );+// }+//}++//if ( ! function_exists( 'learn_press_courses_loop_item_begin_meta' ) ) {+// /**+// * Output the excerpt of the course within loop+// */+// function learn_press_courses_loop_item_begin_meta() {+// learn_press_get_template( 'loop/course/meta-begin.php' );+// }+//}++//if ( ! function_exists( 'learn_press_courses_loop_item_end_meta' ) ) {+// /**+// * Output the excerpt of the course within loop+// */+// function learn_press_courses_loop_item_end_meta() {+// learn_press_get_template( 'loop/course/meta-end.php' );+// }+//}++//if ( ! function_exists( 'learn_press_courses_loop_item_introduce' ) ) {+// /**+// * Output the excerpt of the course within loop+// */+// function learn_press_courses_loop_item_introduce() {+// learn_press_get_template( 'loop/course/introduce.php' );+// }+//} if ( ! function_exists( 'learn_press_courses_loop_item_price' ) ) { /** * Output the price of the course within loop+ * @using in many themes. */ function learn_press_courses_loop_item_price() { learn_press_get_template( 'loop/course/price.php' );@@ -505,6 +510,7 @@ if ( ! function_exists( 'learn_press_begin_courses_loop' ) ) { /** * Output the price of the course within loop+ * @using in many themes. */ function learn_press_begin_courses_loop() { learn_press_get_template( 'loop/course/loop-begin.php' );@@ -514,102 +520,106 @@ if ( ! function_exists( 'learn_press_end_courses_loop' ) ) { /** * Output the price of the course within loop+ * @using in many themes. */ function learn_press_end_courses_loop() { learn_press_get_template( 'loop/course/loop-end.php' ); } } -if ( ! function_exists( 'learn_press_courses_loop_item_students' ) ) {- /**- * Output the students of the course within loop- * @deprecated 4.0.0- */- function learn_press_courses_loop_item_students() {- _deprecated_function( __FUNCTION__, '4.0.0' );- echo '<div class="clearfix"></div>';- learn_press_get_template( 'loop/course/students.php' );- }-}--if ( ! function_exists( 'learn_press_courses_pagination' ) ) {- /**- * Output the pagination of archive courses- */- function learn_press_courses_pagination() {- learn_press_get_template( 'loop/course/pagination.php' );- }-}--if ( ! function_exists( 'learn_press_output_single_course_learning_summary' ) ) {- /**- * Output the content of learning course content- */- function learn_press_output_single_course_learning_summary() {- learn_press_get_template( 'single-course/content-learning.php' );- }-}-+//if ( ! function_exists( 'learn_press_courses_loop_item_students' ) ) {+// /**+// * Output the students of the course within loop+// * @deprecated 4.0.0+// */+// function learn_press_courses_loop_item_students() {+// _deprecated_function( __FUNCTION__, '4.0.0' );+// echo '<div class="clearfix"></div>';+// learn_press_get_template( 'loop/course/students.php' );+// }+//}++//if ( ! function_exists( 'learn_press_courses_pagination' ) ) {+// /**+// * Output the pagination of archive courses+// */+// function learn_press_courses_pagination() {+// learn_press_get_template( 'loop/course/pagination.php' );+// }+//}++//if ( ! function_exists( 'learn_press_output_single_course_learning_summary' ) ) {+// /**+// * Output the content of learning course content+// */+// function learn_press_output_single_course_learning_summary() {+// learn_press_get_template( 'single-course/content-learning.php' );+// }+//}+++//if ( ! function_exists( 'learn_press_output_single_course_landing_summary' ) ) {+// /**+// * Output the content of landing course content+// */+// function learn_press_output_single_course_landing_summary() {+// learn_press_get_template( 'single-course/content-landing.php' );+// }+//} -if ( ! function_exists( 'learn_press_output_single_course_landing_summary' ) ) {- /**- * Output the content of landing course content- */- function learn_press_output_single_course_landing_summary() {- learn_press_get_template( 'single-course/content-landing.php' );- }-} --if ( ! function_exists( 'learn_press_course_title' ) ) {- /**- * Display the title for single course- */- function learn_press_course_title() {- learn_press_get_template( 'single-course/title.php' );- }-}+//if ( ! function_exists( 'learn_press_course_title' ) ) {+// /**+// * Display the title for single course+// */+// function learn_press_course_title() {+// learn_press_get_template( 'single-course/title.php' );+// }+//} if ( ! function_exists( 'learn_press_course_progress' ) ) { /** * Display course curriculum+ * @using ivy-school */ function learn_press_course_progress() { learn_press_get_template( 'single-course/progress.php' ); } } -if ( ! function_exists( 'learn_press_course_curriculum' ) ) {- /**- * Display course curriculum- */- function learn_press_course_curriculum() {- // learn_press_get_template( 'single-course/curriculum.php' );- }-}+//if ( ! function_exists( 'learn_press_course_curriculum' ) ) {+// /**+// * Display course curriculum+// */+// function learn_press_course_curriculum() {+// // learn_press_get_template( 'single-course/curriculum.php' );+// }+//} if ( ! function_exists( 'learn_press_course_categories' ) ) { /** * Display course categories+ * @using eduma child theme v5.5.5 */ function learn_press_course_categories() { // learn_press_get_template( 'single-course/categories.php' ); } } -if ( ! function_exists( 'learn_press_course_tags' ) ) {- /**- * Display course tags- */- function learn_press_course_tags() {- learn_press_get_template( 'single-course/tags.php' );- }-}+//if ( ! function_exists( 'learn_press_course_tags' ) ) {+// /**+// * Display course tags+// */+// function learn_press_course_tags() {+// learn_press_get_template( 'single-course/tags.php' );+// }+//} if ( ! function_exists( 'learn_press_course_instructor' ) ) { /** * Display course instructor+ * @using in many themes. */ function learn_press_course_instructor() { learn_press_get_template( 'single-course/instructor.php' );@@ -619,219 +629,220 @@ if ( ! function_exists( 'learn_press_course_thumbnail' ) ) { /** * Display Course Thumbnail+ * @using in Eduma, Education Pack themes. */ function learn_press_course_thumbnail() { learn_press_get_template( 'single-course/thumbnail.php' ); } } -if ( ! function_exists( 'learn_press_single_course_description' ) ) {- /**- * Display course description- */- function learn_press_single_course_description() {- learn_press_get_template( 'single-course/description.php' );- }-}--if ( ! function_exists( 'learn_press_single_course_content_item' ) ) {- /**- * Display lesson content- */- function learn_press_single_course_content_item() {- learn_press_get_template( 'single-course/content-item.php' );- }-}--if ( ! function_exists( 'learn_press_checkout_user_form_login' ) ) {- /**- * Output login form before order review if user is not logged in- */- function learn_press_checkout_user_form_login() {- learn_press_get_template( 'checkout/form-login.php' );- }-}--if ( ! function_exists( 'learn_press_checkout_user_form_register' ) ) {- /**- * Output register form before order review if user is not logged in- */- function learn_press_checkout_user_form_register() {- learn_press_get_template( 'checkout/form-register.php' );- }-}--if ( ! function_exists( 'learn_press_checkout_user_logged_in' ) ) {- /**- * Output message before order review if user is logged in- */- function learn_press_checkout_user_logged_in() {- learn_press_get_template( 'checkout/form-logged-in.php' );- }-}---if ( ! function_exists( 'learn_press_after_profile_tab_loop_course' ) ) {- /**- * Display user profile tabs- *- * @param LP_User- */- function learn_press_after_profile_tab_loop_course( $user, $course_id ) {-- $args = array(- 'user' => $user,- 'course_id' => $course_id,- );- learn_press_get_template( 'profile/tabs/courses/progress.php', $args );-- }-}---if ( ! function_exists( 'learn_press_output_user_profile_info' ) ) {- /**- * Displaying user info- *- * @param $user- */- function learn_press_output_user_profile_info( $user, $current, $tabs ) {- learn_press_get_template(- 'profile/info.php',- array(- 'user' => $user,- 'tabs' => $tabs,- 'current' => $current,- )- );- }-}+//if ( ! function_exists( 'learn_press_single_course_description' ) ) {+// /**+// * Display course description+// */+// function learn_press_single_course_description() {+// learn_press_get_template( 'single-course/description.php' );+// }+//}++//if ( ! function_exists( 'learn_press_single_course_content_item' ) ) {+// /**+// * Display lesson content+// */+// function learn_press_single_course_content_item() {+// learn_press_get_template( 'single-course/content-item.php' );+// }+//}++//if ( ! function_exists( 'learn_press_checkout_user_form_login' ) ) {+// /**+// * Output login form before order review if user is not logged in+// */+// function learn_press_checkout_user_form_login() {+// learn_press_get_template( 'checkout/form-login.php' );+// }+//}++//if ( ! function_exists( 'learn_press_checkout_user_form_register' ) ) {+// /**+// * Output register form before order review if user is not logged in+// */+// function learn_press_checkout_user_form_register() {+// learn_press_get_template( 'checkout/form-register.php' );+// }+//}++//if ( ! function_exists( 'learn_press_checkout_user_logged_in' ) ) {+// /**+// * Output message before order review if user is logged in+// */+// function learn_press_checkout_user_logged_in() {+// learn_press_get_template( 'checkout/form-logged-in.php' );+// }+//}+++//if ( ! function_exists( 'learn_press_after_profile_tab_loop_course' ) ) {+// /**+// * Display user profile tabs+// *+// * @param LP_User+// */+// function learn_press_after_profile_tab_loop_course( $user, $course_id ) {+//+// $args = array(+// 'user' => $user,+// 'course_id' => $course_id,+// );+// learn_press_get_template( 'profile/tabs/courses/progress.php', $args );+// }+//}+++//if ( ! function_exists( 'learn_press_output_user_profile_info' ) ) {+// /**+// * Displaying user info+// *+// * @param $user+// */+// function learn_press_output_user_profile_info( $user, $current, $tabs ) {+// learn_press_get_template(+// 'profile/info.php',+// array(+// 'user' => $user,+// 'tabs' => $tabs,+// 'current' => $current,+// )+// );+// }+//} /* QUIZ TEMPLATES */-if ( ! function_exists( 'learn_press_single_quiz_title' ) ) {- /**- * Output the title of the quiz- */- function learn_press_single_quiz_title() {- learn_press_get_template( 'content-quiz/title.php' );- }-}--if ( ! function_exists( 'learn_press_message' ) ) {- /**- * Template to display the messages- *- * @param $content- * @param string $type- */- function learn_press_message( $content, $type = 'message' ) {- learn_press_get_template(- 'global/message.php',- array(- 'type' => $type,- 'content' => $content,- )- );- }-}--if ( ! function_exists( 'learn_press_course_overview_tab' ) ) {- /**- * Output course overview- *- * @since 1.1- */- function learn_press_course_overview_tab() {- learn_press_get_template( 'single-course/tabs/overview.php' );- }-}--if ( ! function_exists( 'learn_press_course_curriculum_tab' ) ) {- /**- * Output course curriculum- *- * @since 1.1- */- function learn_press_course_curriculum_tab() {- learn_press_get_template( 'single-course/tabs/curriculum.php' );- }-}--if ( ! function_exists( 'learn_press_course_instructor_tab' ) ) {- /**- * Output course curriculum- *- * @since 1.1- */- function learn_press_course_instructor_tab() {- learn_press_get_template( 'single-course/tabs/instructor.php' );- }-}---if ( ! function_exists( 'learn_press_content_item_header' ) ) {- function learn_press_content_item_header() {- learn_press_get_template( 'single-course/content-item/header.php' );- }-}--if ( ! function_exists( 'learn_press_content_item_footer' ) ) {- function learn_press_content_item_footer() {- learn_press_get_template( 'single-course/content-item/footer.php' );- }-}---if ( ! function_exists( 'learn_press_profile_mobile_menu' ) ) {- function learn_press_profile_mobile_menu() {- learn_press_get_template( 'profile/mobile-menu.php' );- }-}--if ( ! function_exists( 'learn_press_quiz_complete_button' ) ) {-- function learn_press_quiz_complete_button() {- $course = learn_press_get_course();- $user = learn_press_get_current_user();- $quiz = LP_Global::course_item_quiz();-- if ( $user->has_course_status( $course->get_id(), array( 'finished' ) ) || ! $user->has_quiz_status( 'started', $quiz->get_id(), $course->get_id() ) ) {- return;- }- learn_press_get_template( 'content-quiz/buttons/complete.php' );- }-}--if ( ! function_exists( 'learn_press_content_item_summary_question_explanation' ) ) {-- /**- * Render content if quiz question.- */- function learn_press_content_item_summary_question_explanation() {- $quiz = LP_Global::course_item_quiz();- $question = $quiz->get_viewing_question();- if ( $question ) {- $course = learn_press_get_course();- $user = learn_press_get_current_user();- $course_data = $user->get_course_data( $course->get_id() );- $user_quiz = $course_data->get_item_quiz( $quiz->get_id() );-- if ( ! $question->get_explanation() ) {- return;- }-- if ( $user_quiz->has_checked_question( $question->get_id() ) || $user_quiz->is_answered_true( $question->get_id() ) ) {- learn_press_get_template( 'content-question/explanation.php', array( 'question' => $question ) );- }- }- }-}+//if ( ! function_exists( 'learn_press_single_quiz_title' ) ) {+// /**+// * Output the title of the quiz+// */+// function learn_press_single_quiz_title() {+// learn_press_get_template( 'content-quiz/title.php' );+// }+//}++//if ( ! function_exists( 'learn_press_message' ) ) {+// /**+// * Template to display the messages+// *+// * @param $content+// * @param string $type+// */+// function learn_press_message( $content, $type = 'message' ) {+// learn_press_get_template(+// 'global/message.php',+// array(+// 'type' => $type,+// 'content' => $content,+// )+// );+// }+//}++//if ( ! function_exists( 'learn_press_course_overview_tab' ) ) {+// /**+// * Output course overview+// *+// * @since 1.1+// */+// function learn_press_course_overview_tab() {+// learn_press_get_template( 'single-course/tabs/overview.php' );+// }+//}++//if ( ! function_exists( 'learn_press_course_curriculum_tab' ) ) {+// /**+// * Output course curriculum+// *+// * @since 1.1+// */+// function learn_press_course_curriculum_tab() {+// learn_press_get_template( 'single-course/tabs/curriculum.php' );+// }+//}++//if ( ! function_exists( 'learn_press_course_instructor_tab' ) ) {+// /**+// * Output course curriculum+// *+// * @since 1.1+// */+// function learn_press_course_instructor_tab() {+// learn_press_get_template( 'single-course/tabs/instructor.php' );+// }+//}+++//if ( ! function_exists( 'learn_press_content_item_header' ) ) {+// function learn_press_content_item_header() {+// learn_press_get_template( 'single-course/content-item/header.php' );+// }+//}++//if ( ! function_exists( 'learn_press_content_item_footer' ) ) {+// function learn_press_content_item_footer() {+// learn_press_get_template( 'single-course/content-item/footer.php' );+// }+//}+++//if ( ! function_exists( 'learn_press_profile_mobile_menu' ) ) {+// function learn_press_profile_mobile_menu() {+// learn_press_get_template( 'profile/mobile-menu.php' );+// }+//}++//if ( ! function_exists( 'learn_press_quiz_complete_button' ) ) {+//+// function learn_press_quiz_complete_button() {+// $course = learn_press_get_course();+// $user = learn_press_get_current_user();+// $quiz = LP_Global::course_item_quiz();+//+// if ( $user->has_course_status( $course->get_id(), array( 'finished' ) ) || ! $user->has_quiz_status( 'started', $quiz->get_id(), $course->get_id() ) ) {+// return;+// }+// learn_press_get_template( 'content-quiz/buttons/complete.php' );+// }+//}++//if ( ! function_exists( 'learn_press_content_item_summary_question_explanation' ) ) {+//+// /**+// * Render content if quiz question.+// */+// function learn_press_content_item_summary_question_explanation() {+// $quiz = LP_Global::course_item_quiz();+// $question = $quiz->get_viewing_question();+// if ( $question ) {+// $course = learn_press_get_course();+// $user = learn_press_get_current_user();+// $course_data = $user->get_course_data( $course->get_id() );+// $user_quiz = $course_data->get_item_quiz( $quiz->get_id() );+//+// if ( ! $question->get_explanation() ) {+// return;+// }+//+// if ( $user_quiz->has_checked_question( $question->get_id() ) || $user_quiz->is_answered_true( $question->get_id() ) ) {+// learn_press_get_template( 'content-question/explanation.php', array( 'question' => $question ) );+// }+// }+// }+//} if ( ! function_exists( 'learn_press_breadcrumb' ) ) { /** * Output the breadcrumb of archive courses+ * Still using *- * @param array+ * @param array $args */ function learn_press_breadcrumb( $args = array() ) { $args = wp_parse_args(@@ -861,226 +872,220 @@ } } -if ( ! function_exists( 'learn_press_search_form' ) ) {- /**- * Output the breadcrumb of archive courses- *- * @param array- */- function learn_press_search_form() {- if ( ! empty( $_REQUEST['s'] ) && ! empty( $_REQUEST['ref'] ) && 'course' == $_REQUEST['ref'] ) {- $s = stripslashes_deep( $_REQUEST['s'] );- } else {- $s = '';- }-- learn_press_get_template( 'search-form.php', array( 's' => $s ) );- }-}--if ( ! function_exists( 'learn_press_section_item_meta' ) ) {- /**- * @param object- * @param array
--- cache/learnpress_4.2.7/inc/lp-template-hooks.php 2025-11-16 19:18:32.072806972 +0000+++ cache/learnpress_4.2.7.1/inc/lp-template-hooks.php 2025-11-16 19:21:03.365526895 +0000@@ -57,13 +57,6 @@ /** * Course buttons- *- * @see learn_press_course_purchase_button- * @see learn_press_course_enroll_button- * @see learn_press_course_retake_button- * @see learn_press_course_continue_button- * @see learn_press_course_finish_button- * @see learn_press_course_external_button */ learn_press_add_course_buttons();
Vulnerability Existed: no No vulnerability - N/A - inc/lp-template-hooks.php [57-67] Old Code: ```php /** * Course buttons * * @see learn_press_course_purchase_button * @see learn_press_course_enroll_button * @see learn_press_course_retake_button * @see learn_press_course_continue_button * @see learn_press_course_finish_button * @see learn_press_course_external_button */ learn_press_add_course_buttons(); ``` Fixed Code: ```php /** * Course buttons */ learn_press_add_course_buttons(); ``` Explanation: The diff only removes comments and does not change any executable code. The function `learn_press_add_course_buttons()` is called without parameters, and there is no user input involved in this specific change. Taint tracing shows no entry point for attacker-controlled data, as the function call is static and does not process any input. Therefore, no vulnerability is introduced or fixed by this diff.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/inc/rest-api/v1/admin/class-lp-admin-rest-tools-controller.php 2025-11-16 19:18:32.075807165 +0000+++ cache/learnpress_4.2.7.1/inc/rest-api/v1/admin/class-lp-admin-rest-tools-controller.php 2025-11-16 19:21:03.368527088 +0000@@ -358,26 +358,55 @@ public function search_courses( WP_REST_Request $request ): LP_REST_Response { $response = new LP_REST_Response(); try {- $params = $request->get_params();- $ids_str = LP_Helper::sanitize_params_submitted( $params['ids'] ?? '' );- $not_ids_str = LP_Helper::sanitize_params_submitted( $params['not_ids'] ?? '' );+ $params = $request->get_params();+ $ids_str = LP_Helper::sanitize_params_submitted( $params['ids'] ?? '' );+ //$not_ids_str = LP_Helper::sanitize_params_submitted( $params['not_ids'] ?? '' );+ $not_ids_str = LP_Helper::sanitize_params_submitted( $params['id_not_in'] ?? '' );+ $current_ids = (array) LP_Helper::sanitize_params_submitted( $params['current_ids'] ?? [] ); $total_rows = 0; $filter = new LP_Course_Filter(); $filter->limit = 20; $filter->only_fields = [ 'ID', 'post_title' ];+ $filter->post_status = [ 'publish' ]; $filter->post_title = LP_Helper::sanitize_params_submitted( $params['search'] ?? '' ); $filter->page = LP_Helper::sanitize_params_submitted( $params['paged'] ?? 1, 'int' );+ if ( ! empty( $ids_str ) ) { $filter->post_ids = explode( ',', $ids_str ); }+ if ( ! empty( $not_ids_str ) ) { $not_ids = explode( ',', $not_ids_str ); $not_ids = array_map( 'absint', $not_ids ); $not_ids = implode( ',', $not_ids ); $filter->where[] = "AND ID NOT IN ({$not_ids})"; }- $courses = Courses::get_courses( $filter, $total_rows );- $response->data->courses = $courses;++ // Get all courses.+ $courses = Courses::get_courses( $filter, $total_rows );++ // Get selected courses.+ $courses_current = [];+ if ( ! empty( $current_ids ) ) {+ $current_ids = array_map( 'absint', $current_ids );+ $filter_current_ids = new LP_Course_Filter();+ $filter_current_ids->limit = -1;+ $filter_current_ids->only_fields = [ 'ID', 'post_title' ];+ $filter->post_status = [ 'publish' ];+ $filter_current_ids->post_ids = $current_ids;+ $courses_current = Courses::get_courses( $filter_current_ids );+ }++ $check_ids = [];+ $courses_rs = [];+ $courses_data = array_merge( $courses_current, $courses );+ foreach ( $courses_data as $course ) {+ if ( ! in_array( $course->ID, $check_ids ) ) {+ $courses_rs[] = $course;+ $check_ids[] = $course->ID;+ }+ }+ $response->data->courses = $courses_rs; $response->data->total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows ); $response->status = 'success'; } catch ( Throwable $e ) {
After analyzing the provided diff for the file `inc/rest-api/v1/admin/class-lp-admin-rest-tools-controller.php`, I have performed a line-by-line taint tracing for all user-input parameters that reach potential SQL sinks. The user inputs are sanitized using `LP_Helper::sanitize_params_submitted` and, in some cases, further validated with `absint` or type casting. The sinks involve SQL query construction in `Courses::get_courses` and direct string interpolation in the `where` clause. For the `ids` parameter (used in `post_ids`), it is sanitized with `LP_Helper::sanitize_params_submitted` but not explicitly cast to integers via `absint` in the old code. However, without knowledge of the internal implementation of `LP_Helper::sanitize_params_submitted` or `Courses::get_courses`, and respecting the defense that sanitization is applied, I cannot confirm a specific bypass. The same logic applies to the `search` parameter used in `post_title`. For the `not_ids`/`id_not_in` parameter, it is sanitized with `LP_Helper::sanitize_params_submitted` and then each element is passed through `absint` before being used in the SQL `NOT IN` clause, ensuring only integers are included. This prevents SQL injection. The changes in the new code (e.g., adding `current_ids` with `absint` sanitization) do not introduce or fix a vulnerability based on the provided diff. No specific bypass for the sanitization methods is described, and all user inputs are processed through sanitization functions before reaching sinks. Thus, I find no vulnerabilities in the provided code diff. No vulnerabilities were identified.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
--- cache/learnpress_4.2.7/learnpress.php 2025-11-16 19:18:32.093808320 +0000+++ cache/learnpress_4.2.7.1/learnpress.php 2025-11-16 19:21:03.384528121 +0000@@ -4,7 +4,7 @@ * Plugin URI: http://thimpress.com/learnpress * Description: LearnPress is a WordPress complete solution for creating a Learning Management System (LMS). It can help you to create courses, lessons and quizzes. * Author: ThimPress- * Version: 4.2.7+ * Version: 4.2.7.1 * Author URI: http://thimpress.com * Requires at least: 6.0 * Requires PHP: 7.0
--- cache/learnpress_4.2.7/vendor/composer/installed.php 2025-11-16 19:18:32.103808962 +0000+++ cache/learnpress_4.2.7.1/vendor/composer/installed.php 2025-11-16 19:21:03.394528767 +0000@@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(),- 'reference' => '74e6d090dc77cf95068d97b5967fd9c895fb795f',+ 'reference' => '179ed7760e6ae5fa46fbe9c5f1324278a42ece04', 'name' => 'learnpress/learnpress', 'dev' => true, ),@@ -25,7 +25,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(),- 'reference' => '74e6d090dc77cf95068d97b5967fd9c895fb795f',+ 'reference' => '179ed7760e6ae5fa46fbe9c5f1324278a42ece04', 'dev_requirement' => false, ), 'phpcsstandards/phpcsextra' => array(@@ -38,21 +38,21 @@ 'dev_requirement' => true, ), 'phpcsstandards/phpcsutils' => array(- 'pretty_version' => '1.0.9',- 'version' => '1.0.9.0',+ 'pretty_version' => '1.0.12',+ 'version' => '1.0.12.0', 'type' => 'phpcodesniffer-standard', 'install_path' => __DIR__ . '/../phpcsstandards/phpcsutils', 'aliases' => array(),- 'reference' => '908247bc65010c7b7541a9551e002db12e9dae70',+ 'reference' => '87b233b00daf83fb70f40c9a28692be017ea7c6c', 'dev_requirement' => true, ), 'squizlabs/php_codesniffer' => array(- 'pretty_version' => '3.8.0',- 'version' => '3.8.0.0',+ 'pretty_version' => '3.10.2',+ 'version' => '3.10.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../squizlabs/php_codesniffer', 'aliases' => array(),- 'reference' => '5805f7a4e4958dbb5e944ef1e6edae0a303765e7',+ 'reference' => '86e5f5dd9a840c46810ebe5ff1885581c42a3017', 'dev_requirement' => true, ), 'wp-coding-standards/wpcs' => array(
Vulnerability Existed: no Explanation: The file vendor/composer/installed.php is a Composer metadata file that lists installed packages and their versions. The diff shows updates to package references and versions (e.g., for learnpress/learnpress, phpcsstandards/phpcsutils, and squizlabs/php_codesniffer), but no executable code, user input handling, or security-sensitive sinks (e.g., database queries, file operations) are present. The changes are purely declarative and do not involve any data flow from user input to sinks, so no vulnerabilities are introduced or fixed.
The LearnPress – WordPress LMS Plugin plugin for WordPress is vulnerable to SQL Injection via the 'c_only_fields' parameter of the /wp-json/learnpress/v1/courses REST API endpoint in all versions up to, and including, 4.2.7 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.