Make WordPress Core

Opened 5 weeks ago

Last modified 5 weeks ago

#65072 assigned defect (bug)

Registering a query_var called 'preview_nonce' stops previews from working.

Reported by: dsas's profile dsas Owned by: dsas's profile dsas
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Query Keywords: has-patch has-unit-tests
Focuses: Cc:

Description

Summary

The static front page fallback in WP_Query::get_posts() line ~2042 sets is_page = true and is_home = false, but does not update is_singular. This causes the query to generate incorrect SQL — using post_type = 'post' instead of post_type = 'page' — and return zero results.

The equivalent check in parse_query() (line ~1057) correctly maintains the invariant by recalculating is_singular at line 1135. The get_posts() backstop does not.

How to trigger
The backstop fires when parse_query() fails to detect the static front page because $this->query contains unexpected public query vars (anything not in the allowlist of preview, page, paged, cpage). This can happen when a plugin registers a query var that collides with a parameter WordPress core uses in preview URLs — for example, registering preview_nonce as a public query var.

Steps to reproduce

  1. Register preview_nonce as a public query var: add_filter( 'query_vars', function ( $vars ) { $vars[] = 'preview_nonce'; return $vars; } );
  2. Set a static front page in Settings → Reading
  3. Visit /?preview_id=<page_id>&preview_nonce=<valid_nonce>&preview=true

Expected: The static front page content is displayed.
Actual: The blog post index is displayed. The preview URL is redirected with preview=true stripped.

What happens internally

  1. parse_query() line ~1057: The array_diff check fails because preview_nonce is not in the allowlist. is_home stays true, is_page stays false, is_singular stays false.
  1. get_posts() line ~2046: The backstop fires because is_home is true and $query_vars['preview'] is 'true'. It sets is_page = true, is_home = false, and page_id to the front page ID. But it does not set is_singular = true.
  1. get_posts() line ~2618: $post_type_where is correctly set to post_type = 'page' based on is_page = true.
  1. get_posts() line ~2705: The ! $this->is_singular branch is entered. This branch builds its own combined post_type + post_status clause. Since $post_type is empty (line ~2003), line ~2713 defaults to array('post'), overriding the correct post_type_where from step 3.
  1. The resulting SQL is WHERE ID = <page_id> AND post_type = 'post', which returns zero rows because the front page is a page.
  1. With zero results, redirect_canonical strips preview=true from the URL and redirects. On the second request, neither parse_query() nor the backstop can identify the request as the static front page.

Note

I've fixed the plugin which exposed this issue, but the is_singular invariant violation in the backstop is a core issue.

Change History (1)

This ticket was mentioned in PR #11571 on WordPress/wordpress-develop by @dsas.


5 weeks ago
#1

  • Keywords has-patch has-unit-tests added

The static front page backstop in WP_Query::get_posts() sets is_page = true and is_home = false, but did not update is_singular. This caused the query to generate incorrect SQL — using post_type = 'post' instead of post_type = 'page' — and return zero results.

The equivalent check in parse_query() correctly maintains the invariant by recalculating is_singular at line 1135. The get_posts() backstop now does the same.

The backstop fires when parse_query() fails to detect the static front page because $this->query contains unexpected public query vars (anything not in the allowlist of preview, page, paged, cpage). This can happen when a plugin registers a query var that collides with a parameter WordPress core uses in preview URLs.

Trac ticket: https://core.trac.wordpress.org/ticket/65072

## Use of AI Tools

Note: See TracTickets for help on using tickets.