Skip to content

Commit 8b8aa9e

Browse files
committed
Query: Guard string vars against array injection
1 parent ac186d7 commit 8b8aa9e

2 files changed

Lines changed: 63 additions & 12 deletions

File tree

src/wp-includes/class-wp-query.php

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -826,18 +826,21 @@ public function parse_query( $query = '' ) {
826826
$query_vars['p'] = (int) $query_vars['p'];
827827
}
828828

829-
$query_vars['page_id'] = is_scalar( $query_vars['page_id'] ) ? absint( $query_vars['page_id'] ) : 0;
830-
$query_vars['year'] = is_scalar( $query_vars['year'] ) ? absint( $query_vars['year'] ) : 0;
831-
$query_vars['monthnum'] = is_scalar( $query_vars['monthnum'] ) ? absint( $query_vars['monthnum'] ) : 0;
832-
$query_vars['day'] = is_scalar( $query_vars['day'] ) ? absint( $query_vars['day'] ) : 0;
833-
$query_vars['w'] = is_scalar( $query_vars['w'] ) ? absint( $query_vars['w'] ) : 0;
834-
$query_vars['m'] = is_scalar( $query_vars['m'] ) ? preg_replace( '|[^0-9]|', '', $query_vars['m'] ) : '';
835-
$query_vars['paged'] = is_scalar( $query_vars['paged'] ) ? absint( $query_vars['paged'] ) : 0;
836-
$query_vars['cat'] = preg_replace( '|[^0-9,-]|', '', $query_vars['cat'] ); // Array or comma-separated list of positive or negative integers.
837-
$query_vars['author'] = is_scalar( $query_vars['author'] ) ? preg_replace( '|[^0-9,-]|', '', $query_vars['author'] ) : ''; // Comma-separated list of positive or negative integers.
838-
$query_vars['pagename'] = is_scalar( $query_vars['pagename'] ) ? trim( $query_vars['pagename'] ) : '';
839-
$query_vars['name'] = is_scalar( $query_vars['name'] ) ? trim( $query_vars['name'] ) : '';
840-
$query_vars['title'] = is_scalar( $query_vars['title'] ) ? trim( $query_vars['title'] ) : '';
829+
$query_vars['page_id'] = is_scalar( $query_vars['page_id'] ) ? absint( $query_vars['page_id'] ) : 0;
830+
$query_vars['year'] = is_scalar( $query_vars['year'] ) ? absint( $query_vars['year'] ) : 0;
831+
$query_vars['monthnum'] = is_scalar( $query_vars['monthnum'] ) ? absint( $query_vars['monthnum'] ) : 0;
832+
$query_vars['day'] = is_scalar( $query_vars['day'] ) ? absint( $query_vars['day'] ) : 0;
833+
$query_vars['w'] = is_scalar( $query_vars['w'] ) ? absint( $query_vars['w'] ) : 0;
834+
$query_vars['m'] = is_scalar( $query_vars['m'] ) ? preg_replace( '|[^0-9]|', '', $query_vars['m'] ) : '';
835+
$query_vars['paged'] = is_scalar( $query_vars['paged'] ) ? absint( $query_vars['paged'] ) : 0;
836+
$query_vars['cat'] = preg_replace( '|[^0-9,-]|', '', $query_vars['cat'] ); // Array or comma-separated list of positive or negative integers.
837+
$query_vars['author'] = is_scalar( $query_vars['author'] ) ? preg_replace( '|[^0-9,-]|', '', $query_vars['author'] ) : ''; // Comma-separated list of positive or negative integers.
838+
$query_vars['pagename'] = is_scalar( $query_vars['pagename'] ) ? trim( $query_vars['pagename'] ) : '';
839+
$query_vars['name'] = is_scalar( $query_vars['name'] ) ? trim( $query_vars['name'] ) : '';
840+
$query_vars['title'] = is_scalar( $query_vars['title'] ) ? trim( $query_vars['title'] ) : '';
841+
$query_vars['author_name'] = is_string( $query_vars['author_name'] ) ? $query_vars['author_name'] : '';
842+
$query_vars['feed'] = is_string( $query_vars['feed'] ) ? $query_vars['feed'] : '';
843+
$query_vars['attachment'] = is_string( $query_vars['attachment'] ) ? $query_vars['attachment'] : '';
841844

842845
if ( is_scalar( $query_vars['hour'] ) && '' !== $query_vars['hour'] ) {
843846
$query_vars['hour'] = absint( $query_vars['hour'] );

tests/phpunit/tests/query/parseQuery.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,52 @@ public function test_parse_query_hierarchical_taxonomy_query_var_array() {
264264

265265
$this->assertIsArray( $q->posts );
266266
}
267+
268+
/**
269+
* Data provider of string-only query vars and a representative valid value.
270+
*
271+
* @ticket 64507
272+
*
273+
* @return array[]
274+
*/
275+
public function data_string_query_vars() {
276+
return array(
277+
'author_name' => array( 'author_name', 'johndoe' ),
278+
'feed' => array( 'feed', 'rss2' ),
279+
'attachment' => array( 'attachment', 'my-image' ),
280+
);
281+
}
282+
283+
/**
284+
* Ensure that string query vars pass through parse_query() unchanged.
285+
*
286+
* @ticket 64507
287+
* @dataProvider data_string_query_vars
288+
*
289+
* @param string $query_var Query var name.
290+
* @param string $value Valid string value.
291+
*/
292+
public function test_parse_query_string_var_string_value( $query_var, $value ) {
293+
$q = new WP_Query();
294+
$q->parse_query( array( $query_var => $value ) );
295+
296+
$this->assertSame( $value, $q->query_vars[ $query_var ] );
297+
}
298+
299+
/**
300+
* Ensure that array values for string-only query vars are rejected (returns ''),
301+
* preventing a TypeError fatal from str_contains() / str_replace() / wp_basename()
302+
* on PHP 8+.
303+
*
304+
* @ticket 64507
305+
* @dataProvider data_string_query_vars
306+
*
307+
* @param string $query_var Query var name.
308+
*/
309+
public function test_parse_query_string_var_array_value( $query_var ) {
310+
$q = new WP_Query();
311+
$q->parse_query( array( $query_var => array( 'unexpected', 'array' ) ) );
312+
313+
$this->assertSame( '', $q->query_vars[ $query_var ] );
314+
}
267315
}

0 commit comments

Comments
 (0)