Skip to content

Harden calendar trashed-message Undo URL construction#944

Merged
GaryJones merged 2 commits into
developfrom
GaryJones/harden-calendar-trashed-message
Apr 23, 2026
Merged

Harden calendar trashed-message Undo URL construction#944
GaryJones merged 2 commits into
developfrom
GaryJones/harden-calendar-trashed-message

Conversation

@GaryJones
Copy link
Copy Markdown
Contributor

Summary

The calendar view renders a "post moved to the trash" notice with an Undo link when the admin arrives with ?trashed=N&ids=... in the URL. That notice was built by interpolating the raw \$_GET['ids'] value straight into the link's href, deriving the post type from the first comma-separated value, and leaning on esc_url() to make the result safe.

esc_url() only makes a URL safe to emit into HTML — it escapes the ampersand for HTML context so that reading the href in the source shows &, but it does not URL-encode values inside the query string. A crafted request like ?trashed=1&ids=1%26action%3Dmalicious would therefore produce an Undo link whose query string contained an injected action=malicious parameter alongside ids=1. The post type, read from the first value in the list with no validation, could similarly be any arbitrary string.

The fix pushes every id through absint(), reassembles the list from the resulting integers, and only proceeds when post_type_exists() confirms the derived post type is real. The URL itself is then built with add_query_arg() so the & separator can never originate from user input. As tidying whilst in the file, the count values for both branches are cast to int once and reused, a stray unset( \$_GET['undeleted'] ) is corrected to the parameter the branch actually guards on (untrashed), and the ValidatedSanitizedInput phpcs suppression is dropped now that sanitisation happens in code rather than by comment.

Test plan

  • Trash a post from the calendar view; confirm the "moved to the trash" notice renders with a working Undo link
  • Manually append &ids=1%26action%3Dmalicious to a ?trashed=1 calendar URL and confirm the rendered Undo link carries only a clean ids=1 value and a registered post type
  • Untrash from the Undo link and confirm the "restored from the Trash" notice renders correctly

The calendar's trashed/untrashed notice interpolated the raw $_GET
['ids'] query parameter into the Undo link's URL and derived the post
type from the first value of that list without checking that it was a
registered type. esc_url() on the resulting link only makes the URL
safe to emit into HTML; it does not stop a crafted ids value from
injecting additional query arguments (e.g. ids=1%26action%3Dmalicious)
or from pointing the undo to an arbitrary post type.

Each id is now cast through absint() before the list is reassembled,
the post type is rejected unless post_type_exists() confirms it, and
add_query_arg() builds the URL so the ampersand separator cannot come
from user input. The cast also flows into the translated message and
into number_format_i18n() so both receive a known-integer value.

While in the file, the stray unset( $_GET['undeleted'] ) is corrected
to the parameter the branch actually guards on (untrashed), and the
ValidatedSanitizedInput phpcs suppression is dropped because the
input is now sanitised in code rather than by comment.
@GaryJones GaryJones requested a review from a team as a code owner April 23, 2026 23:30
@GaryJones GaryJones self-assigned this Apr 23, 2026
@GaryJones GaryJones added this to the Next milestone Apr 23, 2026
PHPCS flagged $_GET['ids'] as unsanitised input because the sniff
does not recognise the subsequent absint/explode chain as a
sanitisation path. Wrap the initial read in sanitize_text_field
so the intent is explicit and CI stays clean; the resulting value
is still fed through absint per id and post_type_exists on the
inferred post type, so the security properties are unchanged.
@GaryJones GaryJones merged commit 100db80 into develop Apr 23, 2026
10 checks passed
@GaryJones GaryJones deleted the GaryJones/harden-calendar-trashed-message branch April 23, 2026 23:55
@GaryJones GaryJones mentioned this pull request Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant