[wp-trac] [WordPress Trac] #65055: _pad_term_counts() uses string-concatenated SQL without prepared statement
WordPress Trac
noreply at wordpress.org
Tue Apr 14 05:39:28 UTC 2026
#65055: _pad_term_counts() uses string-concatenated SQL without prepared statement
-------------------------------------+------------------------------
Reporter: rajeshcp | Owner: rajeshcp
Type: defect (bug) | Status: assigned
Priority: normal | Milestone: Awaiting Review
Component: Database | Version: trunk
Severity: major | Resolution:
Keywords: has-patch needs-testing | Focuses:
-------------------------------------+------------------------------
Comment (by liaison):
Replying to [comment:3 abcd95]:
> The current code isn't exploitable — esc_sql() does handle escaping
correctly here, and the values involved are all server-side (database IDs
and registered post types, not user input). But switching to
$wpdb->prepare() is still the right call as a best-practice improvement.
The patch looks good.
>
> One small addition worth considering: _update_post_term_count() filters
object_types through post_type_exists() before querying but
_pad_term_counts() doesn't. Could be worth adding for consistency.
@abcd95 I agree that aligning this with _update_post_term_count() is a
great point for consistency. Filtering out unregistered post types would
indeed make the query more robust.
I’ve drafted a refined version of the patch based on your suggestion:
{{{#!php
function _update_post_term_count( $terms, $taxonomy ) {
// ...
$object_types = (array) $tax_obj->object_type;
foreach ( $object_types as $index => $object_type ) {
if ( ! post_type_exists( $object_type ) ) {
unset( $object_types[ $index ] );
}
}
// ...
}
}}}
{{{#!php
function _pad_term_counts( &$terms, $taxonomy ) {
// Get the object and term IDs and stick them in a lookup table.
$tax_obj = get_taxonomy( $taxonomy );
+ $object_types = (array) $tax_obj->object_type;
+ // --- filter invalid post types ---
+ $object_types = array_filter( $object_types, 'post_type_exists' );
+
+ if ( empty( $object_types ) ) {
+ return; // if no valid post type,return directly
+ }
+ $object_types = esc_sql( $object_types );
- $object_types = esc_sql( $tax_obj->object_type );
- $results = $wpdb->get_results( "SELECT object_id,
term_taxonomy_id FROM $wpdb->term_relationships INNER JOIN $wpdb->posts ON
object_id = ID WHERE term_taxonomy_id IN (" . implode( ',', array_keys(
$term_ids ) ) . ") AND post_type IN ('" . implode( "', '", $object_types )
. "') AND post_status = 'publish'" );
-
+ $results = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT object_id, term_taxonomy_id FROM
$wpdb->term_relationships INNER JOIN $wpdb->posts ON object_id = ID WHERE
term_taxonomy_id IN (" . implode( ',', array_fill( 0, count( $term_ids ),
'%d' ) ) . ') AND post_type IN (' . implode( ',', array_fill( 0, count(
$object_types ), '%s' ) ) . ") AND post_status = 'publish'",
+ array_merge( array_keys( $term_ids ),
$object_types )
+ )
+ );
foreach ( $results as $row ) {
$id = $term_ids[ $row->term_taxonomy_id ];
}}}
This seems to cover the concern perfectly. @rajeshcp, what do you think
about incorporating this filter into the PR?
--
Ticket URL: <https://core.trac.wordpress.org/ticket/65055#comment:4>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list