@@ -879,9 +879,9 @@ private static function execute_get_single( int $post_id, WP_Post_Type $post_typ
879879 *
880880 * @param WP_Post_Type $post_type_object The post type object.
881881 * @param array $input The input parameters.
882- * @return array Query results with posts, total, and total_pages.
882+ * @return array|WP_Error Query results with posts, total, and total_pages, or error .
883883 */
884- private static function execute_get_query ( WP_Post_Type $ post_type_object , array $ input ): array {
884+ private static function execute_get_query ( WP_Post_Type $ post_type_object , array $ input ) {
885885 $ per_page = $ input ['per_page ' ] ?? 10 ;
886886 $ page = $ input ['page ' ] ?? 1 ;
887887
@@ -939,6 +939,23 @@ private static function execute_get_query( WP_Post_Type $post_type_object, array
939939 $ query_input = $ input ['query ' ];
940940
941941 if ( ! empty ( $ query_input ['tax ' ] ) ) {
942+ // Validate that all taxonomies in the query are public.
943+ $ taxonomies_in_query = self ::extract_taxonomies_from_query ( $ query_input ['tax ' ] );
944+ $ allowed_taxonomies = self ::get_allowed_taxonomies ( $ post_type_object ->name );
945+ $ invalid_taxonomies = array_diff ( $ taxonomies_in_query , $ allowed_taxonomies );
946+
947+ if ( ! empty ( $ invalid_taxonomies ) ) {
948+ return new WP_Error (
949+ 'invalid_taxonomy ' ,
950+ sprintf (
951+ /* translators: %s: Comma-separated list of invalid taxonomy slugs. */
952+ __ ( 'The following taxonomies are not allowed: %s ' ),
953+ implode ( ', ' , $ invalid_taxonomies )
954+ ),
955+ array ( 'status ' => 400 )
956+ );
957+ }
958+
942959 $ tax_query = self ::process_query_recursive (
943960 $ query_input ['tax ' ],
944961 array ( __CLASS__ , 'process_tax_clause ' )
@@ -949,6 +966,23 @@ private static function execute_get_query( WP_Post_Type $post_type_object, array
949966 }
950967
951968 if ( ! empty ( $ query_input ['meta ' ] ) ) {
969+ // Validate that all meta keys in the query have show_in_abilities enabled.
970+ $ meta_keys_in_query = self ::extract_meta_keys_from_query ( $ query_input ['meta ' ] );
971+ $ allowed_meta_keys = self ::get_allowed_meta_keys ( $ post_type_object ->name );
972+ $ invalid_keys = array_diff ( $ meta_keys_in_query , $ allowed_meta_keys );
973+
974+ if ( ! empty ( $ invalid_keys ) ) {
975+ return new WP_Error (
976+ 'invalid_meta_key ' ,
977+ sprintf (
978+ /* translators: %s: Comma-separated list of invalid meta keys. */
979+ __ ( 'The following meta keys are not allowed: %s ' ),
980+ implode ( ', ' , $ invalid_keys )
981+ ),
982+ array ( 'status ' => 400 )
983+ );
984+ }
985+
952986 $ meta_query = self ::process_query_recursive (
953987 $ query_input ['meta ' ],
954988 array ( __CLASS__ , 'process_meta_clause ' )
@@ -1025,8 +1059,8 @@ private static function format_post( WP_Post $post, WP_Post_Type $post_type_obje
10251059 }
10261060
10271061 if ( post_type_supports ( $ slug , 'author ' ) ) {
1028- $ author = get_userdata ( (int ) $ post ->post_author );
1029- $ data ['author ' ] = array (
1062+ $ author = get_userdata ( (int ) $ post ->post_author );
1063+ $ data ['author ' ] = array (
10301064 'id ' => (int ) $ post ->post_author ,
10311065 'display_name ' => $ author ? $ author ->display_name : '' ,
10321066 );
@@ -1042,8 +1076,8 @@ private static function format_post( WP_Post $post, WP_Post_Type $post_type_obje
10421076 }
10431077
10441078 if ( post_type_supports ( $ slug , 'post-formats ' ) ) {
1045- $ format = get_post_format ( $ post );
1046- $ data ['format ' ] = $ format ? $ format : 'standard ' ;
1079+ $ format = get_post_format ( $ post );
1080+ $ data ['format ' ] = $ format ? $ format : 'standard ' ;
10471081 }
10481082
10491083 if ( post_type_supports ( $ slug , 'comments ' ) ) {
@@ -1084,13 +1118,19 @@ static function ( $term ): array {
10841118 }
10851119
10861120 if ( ! empty ( $ include ['meta ' ] ) && post_type_supports ( $ slug , 'custom-fields ' ) ) {
1087- $ meta = get_post_meta ( $ post ->ID );
1088- $ public_meta = array ();
1121+ $ meta = get_post_meta ( $ post ->ID );
1122+ $ public_meta = array ();
1123+ $ allowed_meta_keys = self ::get_allowed_meta_keys ( $ slug );
10891124
10901125 foreach ( $ meta as $ key => $ values ) {
1126+ // Skip protected meta keys.
10911127 if ( is_protected_meta ( $ key , 'post ' ) ) {
10921128 continue ;
10931129 }
1130+ // Only include meta keys that are registered with show_in_abilities enabled.
1131+ if ( ! in_array ( $ key , $ allowed_meta_keys , true ) ) {
1132+ continue ;
1133+ }
10941134 $ public_meta [ $ key ] = count ( $ values ) === 1 ? $ values [0 ] : $ values ;
10951135 }
10961136
@@ -1218,19 +1258,38 @@ private static function process_meta_clause( array $clause ): ?array {
12181258 }
12191259
12201260 $ allowed_compare = array (
1221- '= ' , '!= ' , '> ' , '>= ' , '< ' , '<= ' ,
1222- 'LIKE ' , 'NOT LIKE ' , 'IN ' , 'NOT IN ' ,
1223- 'BETWEEN ' , 'NOT BETWEEN ' ,
1224- 'EXISTS ' , 'NOT EXISTS ' ,
1225- 'REGEXP ' , 'NOT REGEXP ' , 'RLIKE ' ,
1261+ '= ' ,
1262+ '!= ' ,
1263+ '> ' ,
1264+ '>= ' ,
1265+ '< ' ,
1266+ '<= ' ,
1267+ 'LIKE ' ,
1268+ 'NOT LIKE ' ,
1269+ 'IN ' ,
1270+ 'NOT IN ' ,
1271+ 'BETWEEN ' ,
1272+ 'NOT BETWEEN ' ,
1273+ 'EXISTS ' ,
1274+ 'NOT EXISTS ' ,
1275+ 'REGEXP ' ,
1276+ 'NOT REGEXP ' ,
1277+ 'RLIKE ' ,
12261278 );
12271279 if ( ! empty ( $ clause ['compare ' ] ) && in_array ( $ clause ['compare ' ], $ allowed_compare , true ) ) {
12281280 $ result ['compare ' ] = $ clause ['compare ' ];
12291281 }
12301282
12311283 $ allowed_types = array (
1232- 'NUMERIC ' , 'CHAR ' , 'DATE ' , 'DATETIME ' ,
1233- 'TIME ' , 'BINARY ' , 'SIGNED ' , 'UNSIGNED ' , 'DECIMAL ' ,
1284+ 'NUMERIC ' ,
1285+ 'CHAR ' ,
1286+ 'DATE ' ,
1287+ 'DATETIME ' ,
1288+ 'TIME ' ,
1289+ 'BINARY ' ,
1290+ 'SIGNED ' ,
1291+ 'UNSIGNED ' ,
1292+ 'DECIMAL ' ,
12341293 );
12351294 if ( ! empty ( $ clause ['type ' ] ) && in_array ( $ clause ['type ' ], $ allowed_types , true ) ) {
12361295 $ result ['type ' ] = $ clause ['type ' ];
@@ -1251,9 +1310,16 @@ private static function process_date_clause( array $clause ): ?array {
12511310 $ result = array ();
12521311
12531312 $ int_fields = array (
1254- 'year ' , 'month ' , 'week ' , 'day ' ,
1255- 'hour ' , 'minute ' , 'second ' ,
1256- 'dayofweek ' , 'dayofweek_iso ' , 'dayofyear ' ,
1313+ 'year ' ,
1314+ 'month ' ,
1315+ 'week ' ,
1316+ 'day ' ,
1317+ 'hour ' ,
1318+ 'minute ' ,
1319+ 'second ' ,
1320+ 'dayofweek ' ,
1321+ 'dayofweek_iso ' ,
1322+ 'dayofyear ' ,
12571323 );
12581324
12591325 foreach ( $ int_fields as $ field ) {
@@ -1315,4 +1381,109 @@ private static function process_date_top_level( array $input, array &$result ):
13151381 $ result ['column ' ] = $ input ['column ' ];
13161382 }
13171383 }
1384+
1385+ /**
1386+ * Returns all meta keys that are registered with show_in_abilities enabled for a post type.
1387+ *
1388+ * @since 7.0.0
1389+ *
1390+ * @param string $post_type_slug The post type slug.
1391+ * @return string[] List of allowed meta keys.
1392+ */
1393+ private static function get_allowed_meta_keys ( string $ post_type_slug ): array {
1394+ $ registered_meta = array_merge (
1395+ get_registered_meta_keys ( 'post ' , $ post_type_slug ),
1396+ get_registered_meta_keys ( 'post ' )
1397+ );
1398+
1399+ $ allowed = array ();
1400+ foreach ( $ registered_meta as $ key => $ args ) {
1401+ if ( ! empty ( $ args ['show_in_abilities ' ] ) ) {
1402+ $ allowed [] = $ key ;
1403+ }
1404+ }
1405+
1406+ return $ allowed ;
1407+ }
1408+
1409+ /**
1410+ * Extracts all meta keys from a meta query structure recursively.
1411+ *
1412+ * @since 7.0.0
1413+ *
1414+ * @param array $query The meta query input.
1415+ * @return string[] List of meta keys found in the query.
1416+ */
1417+ private static function extract_meta_keys_from_query ( array $ query ): array {
1418+ $ keys = array ();
1419+
1420+ if ( ! empty ( $ query ['queries ' ] ) && is_array ( $ query ['queries ' ] ) ) {
1421+ foreach ( $ query ['queries ' ] as $ sub_query ) {
1422+ if ( ! is_array ( $ sub_query ) ) {
1423+ continue ;
1424+ }
1425+
1426+ if ( isset ( $ sub_query ['queries ' ] ) ) {
1427+ // Nested group: recurse.
1428+ $ keys = array_merge ( $ keys , self ::extract_meta_keys_from_query ( $ sub_query ) );
1429+ } elseif ( ! empty ( $ sub_query ['key ' ] ) ) {
1430+ // Leaf clause with a key.
1431+ $ keys [] = sanitize_key ( $ sub_query ['key ' ] );
1432+ }
1433+ }
1434+ }
1435+
1436+ return array_unique ( $ keys );
1437+ }
1438+
1439+ /**
1440+ * Returns all public taxonomies associated with a post type.
1441+ *
1442+ * @since 7.0.0
1443+ *
1444+ * @param string $post_type_slug The post type slug.
1445+ * @return string[] List of allowed taxonomy slugs.
1446+ */
1447+ private static function get_allowed_taxonomies ( string $ post_type_slug ): array {
1448+ $ taxonomies = get_object_taxonomies ( $ post_type_slug , 'objects ' );
1449+ $ allowed = array ();
1450+
1451+ foreach ( $ taxonomies as $ taxonomy ) {
1452+ if ( $ taxonomy ->public ) {
1453+ $ allowed [] = $ taxonomy ->name ;
1454+ }
1455+ }
1456+
1457+ return $ allowed ;
1458+ }
1459+
1460+ /**
1461+ * Extracts all taxonomy slugs from a taxonomy query structure recursively.
1462+ *
1463+ * @since 7.0.0
1464+ *
1465+ * @param array $query The taxonomy query input.
1466+ * @return string[] List of taxonomy slugs found in the query.
1467+ */
1468+ private static function extract_taxonomies_from_query ( array $ query ): array {
1469+ $ taxonomies = array ();
1470+
1471+ if ( ! empty ( $ query ['queries ' ] ) && is_array ( $ query ['queries ' ] ) ) {
1472+ foreach ( $ query ['queries ' ] as $ sub_query ) {
1473+ if ( ! is_array ( $ sub_query ) ) {
1474+ continue ;
1475+ }
1476+
1477+ if ( isset ( $ sub_query ['queries ' ] ) ) {
1478+ // Nested group: recurse.
1479+ $ taxonomies = array_merge ( $ taxonomies , self ::extract_taxonomies_from_query ( $ sub_query ) );
1480+ } elseif ( ! empty ( $ sub_query ['taxonomy ' ] ) ) {
1481+ // Leaf clause with a taxonomy.
1482+ $ taxonomies [] = sanitize_key ( $ sub_query ['taxonomy ' ] );
1483+ }
1484+ }
1485+ }
1486+
1487+ return array_unique ( $ taxonomies );
1488+ }
13181489}
0 commit comments