From c41946d98b28aefc1c74fb7e40b3a4f8c9d1421e Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Thu, 11 Jun 2026 10:51:19 -0400 Subject: [PATCH] fix: sanitize superglobal reads in AdminScreenResolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isCurrentScreen/isCurrentAction/getCurrentScreen/getCurrentAction read $_REQUEST raw — no wp_unslash, no sanitization. Route them through a shared readRequestKey() helper using sanitize_key(wp_unslash(...)), mirroring how core treats the page query arg. Non-string values now resolve to null instead of leaking arrays into comparisons. Fixes #34 --- lib/Strategies/AdminScreenResolver.php | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/Strategies/AdminScreenResolver.php b/lib/Strategies/AdminScreenResolver.php index e95f71a..dacd57e 100644 --- a/lib/Strategies/AdminScreenResolver.php +++ b/lib/Strategies/AdminScreenResolver.php @@ -41,7 +41,7 @@ public function getUrlForAction(string $slug, string $action, array $context = [ */ public function isCurrentScreen(string $slug): bool { - return $_REQUEST['page'] === $slug; + return $this->readRequestKey('page') === $slug; } /** @@ -49,7 +49,7 @@ public function isCurrentScreen(string $slug): bool */ public function isCurrentAction(string $slug, string $action): bool { - return $this->isCurrentScreen($slug) && $_REQUEST[$this->actionKey] === $action; + return $this->isCurrentScreen($slug) && $this->readRequestKey($this->actionKey) === $action; } /** @@ -57,7 +57,7 @@ public function isCurrentAction(string $slug, string $action): bool */ public function getCurrentScreen(): ?string { - return $_REQUEST['page'] ?? null; + return $this->readRequestKey('page'); } /** @@ -65,6 +65,23 @@ public function getCurrentScreen(): ?string */ public function getCurrentAction(): ?string { - return $_REQUEST[$this->actionKey] ?? null; + return $this->readRequestKey($this->actionKey); + } + + /** + * Read a request key sanitized for screen/action comparisons. + * + * Screen slugs and action names are key-shaped values; sanitize_key() + * mirrors how WordPress core treats the `page` query arg and strips + * anything an attacker could smuggle through the raw superglobal. + */ + protected function readRequestKey(string $key): ?string + { + if (!isset($_REQUEST[$key]) || !is_string($_REQUEST[$key])) { + return null; + } + + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only comparison helpers; mutation handlers verify nonces at their own boundary. + return sanitize_key(wp_unslash($_REQUEST[$key])); } } \ No newline at end of file