redirect_target(); if ( null === $target ) { return; } wp_safe_redirect( $target, 301 ); exit; } /** * Resolve the destination for the current request. * * Split from maybe_redirect() so the URL construction and the guards are testable without a * real redirect and without `exit`. * * @since 0.0.34 * @return string|null Absolute admin URL, or null when this request is not ours. */ public function redirect_target(): ?string { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reading the page argument to route a GET, no state change. $page = $this->request_key( $_GET['page'] ?? null ); if ( Menu::INTEGRATIONS_LEGACY_SLUG !== $page ) { return null; } $query = 'admin.php?page=' . Menu::ABILITIES_SLUG; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- ditto. $tab = $this->request_key( $_GET['tab'] ?? null ); if ( '' !== $tab ) { $query .= '&tab=' . $tab; } // Always rebuilt from admin_url() with a sanitised tab appended, so there is no // caller-supplied host or path anywhere in the destination — no open-redirect surface, // whatever arrives in the query string. return admin_url( $query ); } /** * Reduce one raw request value to a key, or to an empty string. * * `sanitize_key()` takes a string. Pass it `?page[]=x` and it raises a TypeError — which on * this hook, at priority 1, would be a fatal on an arbitrary admin request rather than a * failed redirect. So non-scalars are discarded before they get there. * * @since 0.0.34 * @param mixed $value Raw value from the request. * @return string Sanitised key, or '' when the value was absent or not scalar. */ private function request_key( $value ): string { if ( ! is_scalar( $value ) ) { return ''; } return sanitize_key( (string) wp_unslash( $value ) ); } }