CVE-2024-1995 (published 2024-03-20T02:15Z)

The Smart Custom Fields plugin for WordPress is vulnerable to unauthorized access of data due to a missing capability check on the relational_posts_search() function in all versions up to, and including, 4.2.2. This makes it possible for authenticated attackers, with subscrber-level access and above, to retrieve post content that is password protected and/or private.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/inc2734/smart-custom-fields
commit_details: repo_url: https://github.com/inc2734/smart-custom-fields
author: inc2734
author_date: 2024-03-16 23:20:17+09:00
msg: Security fix
repository: repo_url: https://github.com/inc2734/smart-custom-fields
repo_name: inc2734/smart-custom-fields
repo_language: PHP
owner: inc2734
file_changes: filename: class.field-related-posts.php
change_type: ModificationType.MODIFY
diff: @@ -77,56 +77,70 @@ class Smart_Custom_Fields_Field_Related_Posts extends Smart_Custom_Fields_Field_
 	 */
 	public function relational_posts_search() {
 		check_ajax_referer( SCF_Config::NAME . '-relation-post-types', 'nonce' );
+
 		$_posts = array();
 
 		$post_types = filter_input( INPUT_POST, 'post_types' );
 		if ( $post_types ) {
-			$post_type = explode( ',', $post_types );
-			$args      = array(
-				'post_type'      => $post_type,
-				'order'          => 'ASC',
-				'orderby'        => 'ID',
-				'posts_per_page' => -1,
-				'post_status'    => 'any',
-			);
+			$post_type              = explode( ',', $post_types );
+			$retrievable_post_types = array();
 
-			$click_count = filter_input( INPUT_POST, 'click_count' );
-			if ( $click_count ) {
-				$posts_per_page = get_option( 'posts_per_page' );
-				$offset         = $click_count * $posts_per_page;
-				$args           = array_merge(
-					$args,
-					array(
-						'offset'         => $offset,
-						'posts_per_page' => $posts_per_page,
-					)
-				);
+			foreach ( $post_type as $_post_type ) {
+				$post_type_object = get_post_type_object( $_post_type );
+
+				if ( current_user_can( $post_type_object->cap->edit_posts ) ) {
+					$retrievable_post_types[] = $_post_type;
+				}
 			}
 
-			$s = filter_input( INPUT_POST, 's' );
-			if ( $s ) {
-				$args = array_merge(
-					$args,
-					array(
-						's' => $s,
-					)
+			if ( $retrievable_post_types ) {
+				$args = array(
+					'post_type'      => $retrievable_post_types,
+					'order'          => 'ASC',
+					'orderby'        => 'ID',
+					'posts_per_page' => -1,
+					'post_status'    => 'any',
 				);
-			}
 
-			$field_name = sanitize_text_field( filter_input( INPUT_POST, 'field_name' ) );
+				$click_count = filter_input( INPUT_POST, 'click_count' );
+				if ( $click_count ) {
+					$posts_per_page = get_option( 'posts_per_page' );
+					$offset         = $click_count * $posts_per_page;
+					$args           = array_merge(
+						$args,
+						array(
+							'offset'         => $offset,
+							'posts_per_page' => $posts_per_page,
+						)
+					);
+				}
+
+				$s = filter_input( INPUT_POST, 's' );
+				if ( $s ) {
+					$args = array_merge(
+						$args,
+						array(
+							's' => $s,
+						)
+					);
+				}
+
+				$field_name = sanitize_text_field( filter_input( INPUT_POST, 'field_name' ) );
 
-			/**
-			 * This filter will be always applied when it queries posts in related posts field.
-			 */
-			$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args', $args, $field_name, $post_type );
+				/**
+				* This filter will be always applied when it queries posts in related posts field.
+				*/
+				$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args', $args, $field_name, $post_type );
 
-			/**
-			 * This filter will only be applied when getting posts via ajax call, therefore it won't be applied for the first load.
-			 */
-			$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_ajax_call', $args, $field_name, $post_type );
+				/**
+				* This filter will only be applied when getting posts via ajax call, therefore it won't be applied for the first load.
+				*/
+				$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_ajax_call', $args, $field_name, $post_type );
 
-			$_posts = get_posts( $args );
+				$_posts = get_posts( $args );
+			}
 		}
+
 		header( 'Content-Type: application/json; charset=utf-8' );
 		echo wp_json_encode( $_posts );
 		die();
@@ -144,33 +158,48 @@ class Smart_Custom_Fields_Field_Related_Posts extends Smart_Custom_Fields_Field_
 		$disabled  = $this->get_disable_attribute( $index );
 		$post_type = $this->get( 'post-type' );
 		$limit     = $this->get( 'limit' );
-		if ( ! $post_type ) {
-			$post_type = array( 'post' );
-		}
-		if ( ! preg_match( '/^\d+$/', $limit ) ) {
-			$limit = '';
-		}
+
+		$choices_posts  = array();
 		$posts_per_page = get_option( 'posts_per_page' );
 
-		$args = array(
-			'post_type'      => $post_type,
-			'order'          => 'ASC',
-			'orderby'        => 'ID',
-			'posts_per_page' => $posts_per_page,
-			'post_status'    => 'any',
-		);
+		if ( $post_type ) {
+			$retrievable_post_types = array();
+
+			foreach ( $post_type as $_post_type ) {
+				$post_type_object = get_post_type_object( $_post_type );
 
-		/**
-		 * This filter will be always applied when it queries posts in related posts field.
-		 */
-		$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args', $args, $name, $post_type );
-		/**
-		 * This filter will only be applied in the first load, therefore it won't be applied when getting posts via ajax call.
-		 */
-		$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_first_load', $args, $name, $post_type );
-
-		// Get posts to show in the first load.
-		$choices_posts = get_posts( $args );
+				if ( current_user_can( $post_type_object->cap->edit_posts ) ) {
+					$retrievable_post_types[] = $_post_type;
+				}
+			}
+
+			if ( $retrievable_post_types ) {
+				if ( ! preg_match( '/^\d+$/', $limit ) ) {
+					$limit = '';
+				}
+
+				$args = array(
+					'post_type'      => $retrievable_post_types,
+					'order'          => 'ASC',
+					'orderby'        => 'ID',
+					'posts_per_page' => $posts_per_page,
+					'post_status'    => 'any',
+				);
+
+				/**
+				* This filter will be always applied when it queries posts in related posts field.
+				*/
+				$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args', $args, $name, $post_type );
+
+				/**
+				* This filter will only be applied in the first load, therefore it won't be applied when getting posts via ajax call.
+				*/
+				$args = apply_filters( SCF_Config::PREFIX . 'custom_related_posts_args_first_load', $args, $name, $post_type );
+
+				// Get posts to show in the first load.
+				$choices_posts = get_posts( $args );
+			}
+		}
 
 		$choices_li = array();
 		foreach ( $choices_posts as $_post ) {

programming_language: PHP
filename: class.field-related-terms.php
change_type: ModificationType.MODIFY
diff: @@ -77,46 +77,59 @@ class Smart_Custom_Fields_Field_Related_Terms extends Smart_Custom_Fields_Field_
 	 */
 	public function relational_terms_search() {
 		check_ajax_referer( SCF_Config::NAME . '-relation-taxonomies', 'nonce' );
+
 		$_terms = array();
-		$args   = array();
 
 		$taxonomies = filter_input( INPUT_POST, 'taxonomies' );
 		if ( $taxonomies ) {
-			$taxonomies = explode( ',', $taxonomies );
-			$args       = array(
-				'taxonomy'     => $taxonomies,
-				'order'        => 'ASC',
-				'orderby'      => 'ID',
-				'number'       => '',
-				'hide_empty'   => false,
-				'hierarchical' => false,
-			);
+			$taxonomies             = explode( ',', $taxonomies );
+			$retrievable_taxonomies = array();
 
-			$click_count = filter_input( INPUT_POST, 'click_count' );
-			if ( $click_count ) {
-				$number = get_option( 'posts_per_page' );
-				$offset = $click_count * $number;
-				$args   = array_merge(
-					$args,
-					array(
-						'offset' => $offset,
-						'number' => $number,
-					)
-				);
+			foreach ( $taxonomies as $_taxonomy ) {
+				$tax = get_taxonomy( $_taxonomy );
+
+				if ( current_user_can( $tax->cap->manage_terms ) ) {
+					$retrievable_taxonomies[] = $_taxonomy;
+				}
 			}
 
-			$search = filter_input( INPUT_POST, 'search' );
-			if ( $search ) {
-				$args = array_merge(
-					$args,
-					array(
-						'search' => $search,
-					)
+			if ( $retrievable_taxonomies ) {
+				$args = array(
+					'taxonomy'     => $retrievable_taxonomies,
+					'order'        => 'ASC',
+					'orderby'      => 'ID',
+					'number'       => '',
+					'hide_empty'   => false,
+					'hierarchical' => false,
 				);
-			}
 
-			$_terms = get_terms( $args );
+				$click_count = filter_input( INPUT_POST, 'click_count' );
+				if ( $click_count ) {
+					$number = get_option( 'posts_per_page' );
+					$offset = $click_count * $number;
+					$args   = array_merge(
+						$args,
+						array(
+							'offset' => $offset,
+							'number' => $number,
+						)
+					);
+				}
+
+				$search = filter_input( INPUT_POST, 'search' );
+				if ( $search ) {
+					$args = array_merge(
+						$args,
+						array(
+							'search' => $search,
+						)
+					);
+				}
+
+				$_terms = get_terms( $args );
+			}
 		}
+
 		header( 'Content-Type: application/json; charset=utf-8' );
 		echo wp_json_encode( $_terms );
 		die();
@@ -134,26 +147,41 @@ class Smart_Custom_Fields_Field_Related_Terms extends Smart_Custom_Fields_Field_
 		$disabled   = $this->get_disable_attribute( $index );
 		$taxonomies = $this->get( 'taxonomy' );
 		$limit      = $this->get( 'limit' );
-		if ( ! $taxonomies ) {
-			$taxonomies = array( 'category' );
-		}
-		if ( ! preg_match( '/^\d+$/', $limit ) ) {
-			$limit = '';
+
+		$choices_terms = array();
+		$number        = get_option( 'posts_per_page' );
+
+		if ( $taxonomies ) {
+			$retrievable_taxonomies = array();
+
+			foreach ( $taxonomies as $_taxonomy ) {
+				$tax = get_taxonomy( $_taxonomy );
+
+				if ( current_user_can( $tax->cap->manage_terms ) ) {
+					$retrievable_taxonomies[] = $_taxonomy;
+				}
+			}
+
+			if ( $retrievable_taxonomies ) {
+				if ( ! preg_match( '/^\d+$/', $limit ) ) {
+					$limit = '';
+				}
+
+				// choicse
+				$choices_terms = get_terms(
+					array(
+						'taxonomy'     => $taxonomies,
+						'order'        => 'ASC',
+						'orderby'      => 'ID',
+						'hide_empty'   => false,
+						'hierarchical' => false,
+						'number'       => $number,
+					)
+				);
+			}
 		}
-		$number = get_option( 'posts_per_page' );
 
-		// choicse
-		$choices_terms = get_terms(
-			array(
-				'taxonomy'     => $taxonomies,
-				'order'        => 'ASC',
-				'orderby'      => 'ID',
-				'hide_empty'   => false,
-				'hierarchical' => false,
-				'number'       => $number,
-			)
-		);
-		$choices_li    = array();
+		$choices_li = array();
 		foreach ( $choices_terms as $_term ) {
 			$term_name = $_term->name;
 			if ( empty( $term_name ) ) {

programming_language: PHP