diff --git a/includes/query/class-tmdo-post-query-router.php b/includes/query/class-tmdo-post-query-router.php index 7e82377..9692c72 100644 --- a/includes/query/class-tmdo-post-query-router.php +++ b/includes/query/class-tmdo-post-query-router.php @@ -138,6 +138,11 @@ class TMDO_Post_Query_Router { * @return void */ public function pre_get_posts( \WP_Query $query ): void { + // The router's query vars are its own: drop any a caller passed in. + foreach ( array( self::QUERY_VAR, self::SORT_VAR, self::COPY_VAR, self::COUNT_VAR ) as $var ) { + $query->set( $var, '' ); + } + // Mode gate: only run when post mode is reads_from_flat. if ( ! self::is_router_active() ) { return; @@ -389,7 +394,7 @@ class TMDO_Post_Query_Router { if ( empty( $copy ) ) { return $orderby; } - return '`' . self::table_alias( $copy['post_type'], $copy['group'] ) . '`.`post_date` ' . $copy['order']; + return '`' . self::table_alias( $copy['post_type'], $copy['group'] ) . '`.`post_date` ' . ( 'ASC' === $copy['order'] ? 'ASC' : 'DESC' ); } /** diff --git a/tests/integration/PostQueryRouterCopyTest.php b/tests/integration/PostQueryRouterCopyTest.php index ccfc1b5..07b11c6 100644 --- a/tests/integration/PostQueryRouterCopyTest.php +++ b/tests/integration/PostQueryRouterCopyTest.php @@ -107,6 +107,32 @@ class PostQueryRouterCopyTest extends TestCase { $this->assertStringEndsWith( "`post_date` DESC, FIELD({$wpdb->posts}.ID,5,9) DESC, ({$wpdb->posts}.ID IN (3,4)) DESC, " . self::ALIAS . '.`post_id` DESC LIMIT 0, 10', $sql ); } + /** + * The router's query vars are its own: values a caller passed in + * (new WP_Query( array( 'wpdo_post_copy_route' => … ) )) are dropped + * (2.4 security review L2). + */ + public function test_query_vars_passed_by_a_caller_are_dropped(): void { + $query = new WP_Query(); + foreach ( array( + 'post_type' => 'rc_item', + 'orderby' => 'title', + 'wpdo_post_copy_route' => array( 'post_type' => 'rc_item', 'group' => 'rc_core', 'statuses' => array( 'publish' ), 'order' => 'DESC, (SELECT 1)' ), + 'wpdo_post_sort' => array( 'post_type' => 'rc_item', 'group' => 'rc_core', 'column' => 'rc_price', 'order' => 'ASC' ), + 'wpdo_post_clauses' => array( 'rc_item' => array() ), + 'wpdo_post_count_sql' => 'SELECT 1', + ) as $k => $v ) { + $query->set( $k, $v ); + } + $router = new TMDO_Post_Query_Router(); + $router->pre_get_posts( $query ); + + foreach ( array( 'wpdo_post_copy_route', 'wpdo_post_sort', 'wpdo_post_clauses', 'wpdo_post_count_sql' ) as $var ) { + $this->assertEmpty( $query->get( $var ), $var ); + } + $this->assertSame( 'wp_posts.post_title ASC', $router->posts_orderby( 'wp_posts.post_title ASC', $query ) ); + } + public function test_copy_route_keeps_group_by_with_another_join(): void { global $wpdb; $sql = $this->request( " LEFT JOIN wp_term_relationships ON ({$wpdb->posts}.ID = wp_term_relationships.object_id)" );